Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions docs/hosting-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,30 @@ For large packages, also support ranged requests and expose their headers:

```text
Accept-Ranges: bytes
Access-Control-Expose-Headers: Content-Range, Accept-Ranges, Content-Length
Access-Control-Expose-Headers: Content-Range, Accept-Ranges, Content-Length, ETag
```

With them, a large package starts playing after a few kilobytes. Without them, it is downloaded whole before anything plays, which still works. A simple range such as `bytes=0-99` needs no CORS preflight, so a host that ignores `OPTIONS` requests is not a problem.

GitHub Pages meets both requirements as it comes. The player's [setup guide](https://github.com/missing-elements/h5p-offline-player/blob/main/h5p-player-setup.md#requirements) describes exactly what it checks.
An `ETag` or `Last-Modified` header lets the player tell a file republished at the same address from the old one, and a visitor who comes back reads almost nothing of the package again. The frame can read `Last-Modified` from any host, and an `ETag` only when the host lists it in `Access-Control-Expose-Headers`, as above.

GitHub Pages meets both requirements as it comes. The player's [setup guide](https://github.com/missing-elements/h5p-offline-player/blob/main/docs/setup.md#requirements) lists exactly what it needs.

## Packages and library files

An H5P package normally includes the libraries it needs. Exports from H5P.com and h5p.org usually contain only `content/` and expect the site they came from to provide the libraries.

Embed My plays those too. The player keeps a bundle of the H5P hub's libraries for every content type the hub serves, about 14 MB, served from the player origin and downloaded once per browser, and a package that ships without its libraries takes them from there. It asks no other site for libraries. See [Privacy and security](privacy-and-security.md).
Embed My plays those too. The player keeps a pack of the H5P hub's libraries for every content type the hub serves, one file per library, served from the player origin. A package that ships without its libraries downloads only the ones it needs from there, and the browser keeps each one for the next package that uses it. It asks no other site for libraries. See [Privacy and security](privacy-and-security.md).

A package written for an older version of a library plays too, and so does one that carries an older version. The pack has the newest version of each library, the player plays that one, and before the activity starts it converts the content with the library's own upgrade steps, as Moodle, WordPress and the other H5P plugins do when a site installs a newer version. If a step fails, the activity plays as it was written. The lines under the preview name the libraries that played newer than the package carries them. To play them exactly as exported instead, add `&refresh-libraries=off` to the end of the `src` in the snippet.

If the preview still reports missing libraries, the package needs a library the pack does not carry: a new major version the H5P hub does not serve yet, or a library written for a single site. Export the activity as a complete package, including libraries, when your authoring tool offers that option. The player's [libraries guide](https://github.com/missing-elements/h5p-offline-player/blob/main/docs/libraries.md) explains how library sources work.

### Content made for newer libraries

A package written for an older version of a library plays too. The bundle carries the newest version of each library, and before the activity starts the player converts the content with that library's own upgrade steps, as Moodle, WordPress and the other H5P plugins do when a site installs a newer version. If a step fails, the activity plays as it was written.
H5P.com gets a library's new version weeks before the H5P hub serves it, so an activity made there in those weeks can name a version that nobody else has yet. The player plays it at the newest version of that library the pack has, and the lines under the preview say which.

If the preview still reports missing libraries, the package needs a library the bundle does not carry. Usually it is newer than the bundle: the bundle is rebuilt from the H5P hub every week, and a later release usually carries it. Less often it is a library written for a single site. Export the activity as a complete package, including libraries, when your authoring tool offers that option. The player's [libraries guide](https://github.com/missing-elements/h5p-offline-player/blob/main/docs/libraries.md) explains how library sources work.
Most new versions only add fields, which the older version ignores. Where one changed the shape of the content, a part of the activity can be missing or drawn wrong, so play through every part of it. To refuse such a package instead, add `&downgrade-libraries=off` to the end of the `src` in the snippet. The pack follows the H5P hub, so once the hub serves the newer version, a later release of the player plays the activity as made.

## How big can a package be?

Expand Down
8 changes: 4 additions & 4 deletions docs/privacy-and-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ Embed only packages you trust, as you would any script.

## When the frame is opened on its own

The frame's address can also be opened directly, as a link, which [Troubleshooting](troubleshooting.md) suggests for some app browsers. Opened that way, a package from another site would share the player origin's own storage with every other package opened like that, so the page plays it as data: none of the package's own scripts run, its libraries come from the player's bundle, and its content is checked against those libraries' field definitions before it is shown, as the H5P plugins for Moodle, WordPress and Drupal check an upload. A package that cannot play without its own copy of a library waits for a click, and the page names the site it comes from before running it.
The frame's address can also be opened directly, as a link, which [Troubleshooting](troubleshooting.md) suggests for some app browsers. Opened that way, a package from another site would share the player origin's own storage with every other package opened like that, so the page plays it as data: none of the package's own scripts run, its libraries come from the player's own library pack, and its content is checked against those libraries' field definitions before it is shown, as the H5P plugins for Moodle, WordPress and Drupal check an upload. A package that cannot play without its own copy of a library waits for a click, and the page names the site it comes from before running it.

## What the frame can do to your page

The snippet gives the frame fullscreen, and the microphone and camera for the content types that record or film: Audio Recorder, Speak the Words and AR Scavenger. The browser asks the visitor before the frame uses either, and for an activity that does neither you can take `microphone; camera` out of the `allow` attribute. The frame gets no location. It cannot read or change your page. But browsers let any iframe navigate the whole tab once the visitor has clicked inside it, so a hostile package could send a visitor who clicks on it to another address. That is one more reason to embed only packages you trust. A site that wants to rule it out can add `sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"` to the iframe; test the activity afterwards, because a sandbox also stops the frame's own "Open it on its own" link.
The snippet gives the frame fullscreen, and the microphone and camera. Audio Recorder and Speak the Words record with the microphone; the camera is for AR Scavenger, which the player [does not run](troubleshooting.md#an-ar-scavenger-activity-shows-only-its-title). The browser asks the visitor before the frame uses either, and for an activity that does neither you can take `microphone; camera` out of the `allow` attribute. The frame gets no location. It cannot read or change your page. But browsers let any iframe navigate the whole tab once the visitor has clicked inside it, so a hostile package could send a visitor who clicks on it to another address. That is one more reason to embed only packages you trust. A site that wants to rule it out can add `sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"` to the iframe; test the activity afterwards, because a sandbox also stops the frame's own "Open it on its own" link.

The script line, `h5p-resizer.js`, is the one part of the snippet that runs on your page itself, with the same rights as your own scripts. It is about sixty lines, it only sets an iframe's height when that frame reports it, and you can read it at the address in the snippet. Leave it out and the frame keeps the height in the `style` attribute.

## What is saved

No learner records. The embed page never turns on the player's resume feature, so answers and progress are not saved anywhere, and reloading the page starts the activity over. Embed My stores nothing on its side either.

The visitor's browser does cache the package's files, and keeps a list of the packages played there with each one's address, title, licence details, language and when it was last played (Cache Storage and IndexedDB on the player's origin, kept separate per embedding site), so the next load is faster. The list shows which activities were opened on that device. Clearing the browser's site data removes both.
The visitor's browser does cache the package's files and the files of the player's library pack it used, and keeps a list of the packages played there with each one's address, title, licence details, language and when it was last played (Cache Storage and IndexedDB on the player's origin, kept separate per embedding site), so the next load is faster. The list shows which activities were opened on that device. Clearing the browser's site data removes both.

## Cookies and tracking

Expand All @@ -34,7 +34,7 @@ The pages and the script are served by GitHub Pages. Like any web host, GitHub s

H5P content itself may contact services named by the package, such as YouTube, Vimeo, Google Fonts, MathJax, or an organization-hosted media service. Review a package and its third-party content before publishing it.

The frame asks no one else for libraries. A package exported without its libraries takes them from a bundle on the player origin, and from nowhere else.
The frame asks no one else for libraries. A package exported without its libraries takes the ones it needs from the library pack on the player origin, and from nowhere else.

## Data protection

Expand Down
8 changes: 6 additions & 2 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ Check that:

## The activity works in my LMS but not in Embed My

An export without its libraries normally plays anyway: the player takes them from its own bundle of the H5P hub's libraries, and upgrades content written for older library versions before it starts. If the preview still names missing libraries, the package needs one the bundle does not carry: usually a version newer than the bundle, less often one written for a single site. Re-export a complete package, including libraries. See [Packages and library files](hosting-packages.md#packages-and-library-files).
An export without its libraries normally plays anyway: the player takes the ones it needs from its own pack of the H5P hub's libraries, and upgrades content written for older library versions before it starts. Content made for a newer version than the pack has plays at the newest one it has, and the preview says so. If the preview still names missing libraries, the package needs one the pack does not carry: a new major version, or a library written for a single site. Re-export a complete package, including libraries. See [Packages and library files](hosting-packages.md#packages-and-library-files).

## An AR Scavenger activity shows only its title

The player does not run AR Scavenger. It writes its 3D and AR scripts into the page as inline scripts, which the frame's security policy refuses, so the activity shows its title and no scene, here and wherever the player runs.

## The activity is cut off or leaves blank space

Expand All @@ -37,7 +41,7 @@ The site's Content Security Policy must allow `frame-src https://embed-my.github

## Other problems

The player's [troubleshooting table](https://github.com/missing-elements/h5p-offline-player/blob/main/h5p-player-setup.md#troubleshooting) covers error codes and less common symptoms in detail, such as formulas shown as raw LaTeX or a missing copyright button.
The player's [troubleshooting guide](https://github.com/missing-elements/h5p-offline-player/blob/main/docs/troubleshooting.md) covers error codes and less common symptoms in detail, such as formulas shown as raw LaTeX or a missing copyright button.

## Reporting a problem

Expand Down
7 changes: 5 additions & 2 deletions e2e/site.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ test('the home page previews a package and writes its snippet', async ({ page })
await expect(checks.first()).toContainText('Do you know what just happened? · Question Set · CC0 1.0 · by missing-elements')
await expect(checks.filter({ hasText: 'Reachable from a browser' })).toHaveAttribute('data-tone', 'ok')
await expect(checks.filter({ hasText: /Streams in place|Downloaded whole/ })).toContainText('MB')
await expect(checks.filter({ hasText: 'Carries its libraries' })).toHaveAttribute('data-tone', 'ok')
// The quiz carries its libraries. A player that plays them from its own pack (player origin with
// `pack`) plays the older ones at the pack's newer versions and says so, which is worth knowing,
// not a fault; one that attaches a whole bundle plays them as exported.
await expect(checks.filter({ hasText: 'Carries its libraries' })).toHaveAttribute('data-tone', /^(ok|info)$/)
await expect(checks.filter({ hasText: /Ready in \d+\.\d s/ })).toHaveCount(1)

expect(problems, problems.join('\n')).toEqual([])
Expand All @@ -62,7 +65,7 @@ test('the lines under the preview say where missing libraries came from', async
await expect(page.locator('#stage')).toHaveAttribute('data-state', 'ready', { timeout: 60_000 })
const line = page.locator('#checks li').filter({ hasText: 'Exported without its libraries' })
await expect(line).toHaveAttribute('data-tone', 'info')
await expect(line).toContainText("the player's bundle")
await expect(line).toContainText("the player added the ones it needs from its library pack")
await expect(line.getByRole('link', { name: 'About library files' })).toHaveAttribute('href', '/docs/hosting-packages#packages-and-library-files')
})

Expand Down
10 changes: 5 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ <h2 class="step-title" id="h-faq"><svg class="safety-icon" aria-hidden="true"><u
<details class="faq-item" open>
<summary><span>If it plays here, will it play in my LMS?</span></summary>
<div class="faq-answer">
<p>Very likely, not certainly. The player runs the standard H5P runtime with the H5P hub's current libraries, and upgrades content written for older library versions first, as an LMS does when it installs newer ones. Moodle, WordPress or another LMS may run older library versions or changes of their own, and a package made for one site can need a library only that site has. A failure here is the more useful result: a link the browser cannot reach, a package that is not an archive, or libraries that nobody serves will fail in most places.</p>
<p>Very likely, not certainly. The player runs the standard H5P runtime with the H5P hub's current libraries, and upgrades content written for older library versions first, as an LMS does when it installs newer ones. Moodle, WordPress or another LMS may run older library versions or changes of their own, and a package made for one site can need a library only that site has. Content made for a library version newer than the hub's, as H5P.com sometimes has for a few weeks, plays at the newest version there is, and the lines under the preview say so. A failure here is the more useful result: a link the browser cannot reach, a package that is not an archive, or libraries that nobody serves will fail in most places.</p>
</div>
</details>
<details class="faq-item">
Expand Down Expand Up @@ -358,8 +358,8 @@ <h2 class="step-title" id="h-faq"><svg class="safety-icon" aria-hidden="true"><u
<details class="faq-item">
<summary><span>Can H5P's own buttons be in my language?</span></summary>
<div class="faq-answer">
<p>Yes, in more than fifty languages. H5P's own buttons and dialogs, such as <strong>Fullscreen</strong>, <strong>Rights of use</strong> and the licence names, follow a <code>lang</code> on the frame's address: add <code>&amp;lang=de</code>, or <code>&amp;lang=pt-BR</code>, to the end of the <code>src</code> in the snippet. Without it they are in English. The frame cannot read your page's language itself, because it runs on another site.</p>
<p>The activity's own text is in whatever language it was written in. The translations come from Moodle's language packs, so a few strings may stay in English.</p>
<p>Yes, in the activity's own language, in any of more than fifty. H5P's own buttons and dialogs, such as <strong>Fullscreen</strong>, <strong>Rights of use</strong> and the licence names, follow the language the package declares in its <code>h5p.json</code>, which H5P's own exports write, so they match the activity's text whatever language your page is in.</p>
<p>For a package that declares no language, add <code>&amp;lang=de</code>, or <code>&amp;lang=pt-BR</code>, to the end of the <code>src</code> in the snippet; without it they are in English. The translations come from Moodle's language packs, so a few strings may stay in English.</p>
</div>
</details>
<details class="faq-item">
Expand All @@ -369,9 +369,9 @@ <h2 class="step-title" id="h-faq"><svg class="safety-icon" aria-hidden="true"><u
</div>
</details>
<details class="faq-item">
<summary><span>Can an AI assistant check a package for me?</span></summary>
<summary><span>Can an AI assistant check a package for me, or make one?</span></summary>
<div class="faq-answer">
<p>Yes. The player's repository ships <a href="https://www.skills.sh/missing-elements/h5p-offline-player" rel="noopener">agent skills</a> for Claude Code, Cursor, Copilot, Codex and the rest. <code>h5p-verify</code> plays a package in a real H5P runtime in a headless browser and reports start-up, errors, missing libraries and a screenshot: the same check as this page, without a link. Two companions make a package's video start at once, and put the player on a site of your own.</p>
<p>Yes. The player's repository ships <a href="https://www.skills.sh/missing-elements/h5p-offline-player" rel="noopener">agent skills</a> for Claude Code, Cursor, Copilot, Codex and the rest, and none needs an API key or an H5P server. <code>h5p-verify</code> plays a package in a real H5P runtime in a headless browser and reports start-up, errors, missing libraries and a screenshot: the same check as this page, without a link. <code>h5p-author</code> turns a prompt or a lesson into an activity of any of 28 content types, checks it and opens it for you to play. The others make a package's video start at once, put the player on a site of your own, and turn a package into a SCORM zip for an LMS.</p>
<pre><code>npx skills add missing-elements/h5p-offline-player --skill h5p-verify</code></pre>
</div>
</details>
Expand Down
Loading
Loading