https://github.com/ateeducacion/h5p2elpx
Convert H5P activities into editable eXeLearning .elpx projects.
https://github.com/ateeducacion/h5p2elpx
elpx exelearning h5p
Last synced: 13 days ago
JSON representation
Convert H5P activities into editable eXeLearning .elpx projects.
- Host: GitHub
- URL: https://github.com/ateeducacion/h5p2elpx
- Owner: ateeducacion
- License: agpl-3.0
- Created: 2026-05-23T15:21:06.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-06T23:52:57.000Z (22 days ago)
- Last Synced: 2026-06-07T01:17:24.543Z (22 days ago)
- Topics: elpx, exelearning, h5p
- Language: TypeScript
- Homepage: https://ateeducacion.github.io/h5p2elpx/
- Size: 61.9 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# h5p2elpx
Convert H5P activities into editable eXeLearning `.elpx` projects.
`h5p2elpx` is a **conservative** converter: it maps supported source content
into editable eXeLearning iDevices and keeps unsupported content visible with
clear warnings instead of silently dropping it. Every conversion produces a JSON
report explaining what was converted, partially converted, or left as a
fallback.
Supported inputs:
- **H5P** (`.h5p`) — 50+ content type adapters.
- **ADC / Content** (`.zip`) — the legacy authoring tool used by the Aula
Digital Canaria platform. All six export flavours are detected
automatically: *native*, plain *zip*, *SCORM 1.2*, *SCORM 2004*,
*xAPI/Tin Can*, and *local*. Bundles that omit `project.json` are recovered
by deobfuscating the `ntxCafCompressed` blob in `index.html` (zlib + base64;
no encryption).
> Status: MVP. CLI + browser-only web app + 50+ H5P adapters + ADC import.
## Installation
Install [Bun](https://bun.sh). The CLI is published to GitHub Packages as
`@ateeducacion/h5p2elpx`.
```bash
echo "@ateeducacion:registry=https://npm.pkg.github.com" >> .npmrc
bunx --bun @ateeducacion/h5p2elpx convert activity.h5p -o activity.elpx
```
If GitHub Packages asks for authentication, add a GitHub token that can read
packages:
```bash
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> .npmrc
```
For local development, clone this repository and install its dependencies:
```bash
git clone https://github.com/ateeducacion/h5p2elpx.git
cd h5p2elpx
bun install
```
The eXeLearning runtime template (`fixtures/elpx/template.elpx`) is already
committed. To rebuild it from a fresh
[eXeLearning release](https://github.com/exelearning/exelearning/releases)
(default: `v4.0.0`, theme `base`):
```bash
bun run build-template # latest pinned default
bun run build-template v4.0.0 base
```
The script downloads `exelearning-static-vX.Y.Z.zip`, unpacks
`bundles/{idevices,libs,common,content-css}.zip` and
`bundles/themes/.zip`, and assembles them into the runtime parts of the
`.elpx` template (`idevices/`, `libs/`, `theme/`, `html/`, `content/css/`,
`index.html`, etc.). `h5p2elpx` always regenerates `content.xml` and
`content.dtd` during conversion. The GitHub Pages workflow runs this on every
push so the bundled web template stays up to date.
## CLI
Run the published CLI:
```bash
# Inspect an .h5p package (detects main library, lists dependencies)
bunx --bun @ateeducacion/h5p2elpx inspect activity.h5p
# Convert one .h5p file into an .elpx project
bunx --bun @ateeducacion/h5p2elpx convert activity.h5p \
-o activity.elpx \
--layout preserve \
--include-original-h5p \
--report report.json
# Convert a folder of .h5p files
bunx --bun @ateeducacion/h5p2elpx convert ./h5p-folder -o bundle.elpx
# Validate an .elpx
bunx --bun @ateeducacion/h5p2elpx validate activity.elpx
```
From a cloned checkout, use `bun run cli -- ` instead.
Options for `convert`:
| Option | Values | Default |
| --- | --- | --- |
| `--layout` | `blocks`, `pages`, `preserve` | `preserve` |
| `--unsupported` | `keep`, `text`, `drop` | `keep` |
| `--include-original-h5p` | flag | off |
| `--title ` | string | "Imported H5P content" |
| `--lang ` | string | unset |
| `--report ` | path | none |
| `--template ` | path to a real eXe `.elpx` template | none |
| `--strict` | flag | off (fails if anything is unsupported) |
## Web app
Drag-and-drop, fully client-side. No backend.
```bash
bun run --cwd packages/web dev # local dev
bun run --cwd packages/web build # production build → packages/web/dist
```
Deployed automatically to GitHub Pages on every push to `main` via
`.github/workflows/pages.yml`.
## Report a broken H5P file
If an `.h5p` file does not convert correctly, open a GitHub issue with the
`Broken H5P conversion` form. Attach the file when possible, or paste a public
or shared download URL.
## Compatibility matrix
| Phase | H5P type | Mapping |
| --- | --- | --- |
| 1 | `H5P.Text`, `H5P.AdvancedText` | text iDevice |
| 1 | `H5P.Image` | text iDevice with `
` |
| 1 | `H5P.Audio` | text iDevice with `` |
| 1 | `H5P.Video` | text iDevice with `` |
| 1 | `H5P.InteractiveVideo` | `interactive-video` iDevice (text + single-choice slides preserved) |
| 1 | `H5P.Column` | flattens children into iDevices |
| 1 | `H5P.CoursePresentation` | one eXe page per slide (preserve mode) |
| 1 | `H5P.InteractiveBook` | one eXe page per chapter |
| 1 | `H5P.Table` | HTML `` in a text iDevice |
| 2 | `H5P.TrueFalse` | trueorfalse iDevice |
| 2 | `H5P.MultiChoice` | form iDevice (`activityType: "selection"`) |
| 2 | `H5P.SingleChoiceSet` | one form iDevice per choice |
| 2 | `H5P.Blanks` | form iDevice |
| 2 | `H5P.Dialogcards`, `H5P.MemoryGame` | flipcards iDevice |
| 2 | `H5P.Summary` | form iDevices |
| 3 | `H5P.DragText` | form iDevice |
| 3 | `H5P.MarkTheWords` | text fallback |
| 3 | `H5P.DragQuestion` | structured fallback (drag list + drop zones) |
| 3 | `H5P.ImageHotspots` | `map` iDevice (info markers with rich popups on the background image) |
| 3 | `H5P.MultipleHotspotQuestion` | `map` iDevice (rectangle hotspots, quiz mode) |
| 3 | `H5P.Crossword` | `crossword` iDevice (native eXe `$eXeCrucigrama`) |
| 3 | `H5P.ImageJuxtaposition` | `beforeafter` iDevice |
| 3 | `H5P.IFrameEmbed` | `external-website` iDevice |
| 3 | `H5P.FindTheWords` | `word-search` iDevice (encrypted DataGame) |
| 3 | `H5P.Flashcards`, `H5P.ImagePair` | `flipcards` iDevice |
| 3 | `H5P.Accordion` | text iDevice with `` panels |
| 3 | `H5P.Essay` | text iDevice with `` placeholder |
| 3 | `H5P.QuestionSet`, `H5P.Questionnaire` | flatten children into iDevices |
| 3 | `H5P.Dictation` | form iDevice (`activityType: "fill"`, drops audio) |
| 3 | `H5P.SortParagraphs`, `H5P.ImageSequencing` | ordered text iDevice (author re-scrambles in eXe) |
| 3 | `H5P.ImageSlider`, `H5P.Collage` | text iDevice with sequential figures |
| 3 | `H5P.ImageHotspotQuestion` | `map` iDevice (rectangle hotspots, quiz mode) |
| 4 | `H5P.GuessTheAnswer` | `flipcards` iDevice (image+question front, solution back) |
| 4 | `H5P.AdventCalendar` | `flipcards` iDevice (one door per card) |
| 4 | `H5P.InformationWall` | `flipcards` iDevice (one panel per card) |
| 4 | `H5P.MultiMediaChoice` | form iDevice (`activityType: "selection"`, image options) |
| 4 | `H5P.ArithmeticQuiz` | expanded into N form (selection) iDevices with deterministic problems |
| 4 | `H5P.AdvancedBlanks` (Complex Fill the Blanks) | form iDevice (`activityType: "fill"`) |
| 4 | `H5P.Agamotto` | `beforeafter` iDevice for two frames, sequential text iDevice otherwise |
| 4 | `H5P.GameMap` | `map` iDevice (markers parsed from `telemetry`) |
| 4 | `H5P.Link` | text iDevice with `` |
| 4 | `H5P.DocumentationTool` | one eXe page per `H5P.StandardPage` |
| anything else | visible warning iDevice (unless `--unsupported drop`) |
## Roadmap — H5P types still on the text fallback
Remaining content types from
stay on the generic
`text` fallback because eXeLearning has no native equivalent:
Chart, Timeline, Personality Quiz, KewAr Code, AR Scavenger, Audio
Recorder, Speak the Words(+Set), Virtual Tour (360), Branching
Scenario, Cornell Notes, Structure Strip, Impressive Presentation.
Sample `.h5p` files for every implemented mapping live under
`fixtures/h5p/` and are exercised by `packages/core/tests/fixtures.test.ts`.
## Architecture
```
.h5p ZIP
→ packages/core/src/h5p/read-h5p.ts (JSZip, browser-safe)
→ packages/core/src/normalize/adapters/* (registry — one file per H5P lib)
→ Normalized AST (packages/core/src/normalize/nodes.ts)
→ packages/core/src/convert/convert.ts (layout planner: blocks|pages|preserve)
→ packages/core/src/exe/idevices/* (registry — one writer per typeName)
→ packages/core/src/exe/elpx-writer.ts (clones a template, injects content.xml)
→ .elpx ZIP + JSON ConversionReport
```
`packages/core` has zero Node-only imports — it works identically in the CLI
and in the browser.
## Adding a new H5P mapper
1. Create `packages/core/src/normalize/adapters/.ts` exporting
`machineName` and `adapt(content) → NormalizedNode`.
2. Add it to the registry in `adapters/index.ts`.
3. Add a unit test under `packages/core/tests/`.
## Adding a new eXeLearning iDevice writer
1. Create `packages/core/src/exe/idevices/.ts` building an
`ElpxIdevice` from a normalized node.
2. Wire it from `convert.ts` for the AST kinds it handles.
## Development
We use [Bun](https://bun.sh) and [Biome](https://biomejs.dev) — verbs are
the same as eXeLearning's:
```bash
make install # bun install
make lint # biome check . (read-only)
make fix # biome check --write . (auto-fix lint + format)
make typecheck # tsc --noEmit
make test # vitest run
make test-watch # vitest in watch mode
make up # start the Vite dev server (web app)
make web-build # build the production web bundle
make template # rebuild fixtures/elpx/template.elpx from upstream
make ci # the same gate CI runs
```
CI: `.github/workflows/test.yml` runs typecheck + tests on every push and PR.
`.github/workflows/pages.yml` builds and deploys the web app to GitHub Pages.
## Known limitations
* The generated `content.xml` targets the real eXeLearning ODE v2.0 format.
`h5p2elpx` rewrites `content.xml`/`content.dtd` on every conversion while
preserving the runtime assets from the bundled template (or a custom one
passed via `--template`).
* H5P.InteractiveVideo preserves `H5P.Text` / `H5P.AdvancedText` overlays and
`H5P.MultiChoice` / `H5P.SingleChoiceSet` questions as eXe interactive-video
slides. Other interaction types (drag, hotspots, summaries, …) are dropped
with a warning in the report.
* H5P.DragQuestion loses geometric positions (becomes a descriptive list).
* `H5P.ImageHotspots`, `H5P.MultipleHotspotQuestion` and
`H5P.ImageHotspotQuestion` are pinned on the native eXe `map`
iDevice, but informational content (links, embedded media) inside
H5P.ImageHotspots panels is rendered into the popup body as static
HTML.
## License
AGPL-3.0-or-later (see `LICENSE`).