Metadata-Version: 2.5
Name: pyh5p
Version: 0.1.0
Summary: Schema-driven creation, inspection, validation and packaging of H5P content
Project-URL: Homepage, https://github.com/Nikityyy/pyh5p
Project-URL: Repository, https://github.com/Nikityyy/pyh5p
Project-URL: Issues, https://github.com/Nikityyy/pyh5p/issues
Project-URL: Author, https://nikity.is-a.dev
Author-email: Nikita Berger <bergernikita1807@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Nikita Berger
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
License-File: LICENSE
Keywords: content,elearning,h5p,html5,lms
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Education
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: hatchling>=1.25; extra == 'dev'
Requires-Dist: hypothesis>=6.100; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: html
Requires-Dist: nh3>=0.2; extra == 'html'
Provides-Extra: js
Requires-Dist: quickjs>=1.19; extra == 'js'
Description-Content-Type: text/markdown

# pyh5p

`pyh5p` creates, edits, validates, and packages H5P content in Python. It
reads each library's `library.json` and `semantics.json`, so the Python code
does not need a class for every H5P content type.

## Quick start

Install the package:

```text
pip install pyh5p
```

Put the content data in `answers.json`:

```json
{
  "question": "What is 2 + 2?",
  "answers": [
    {"text": "4", "correct": true},
    {"text": "5", "correct": false}
  ]
}
```

Build the package:

```text
pyh5p build H5P.MultiChoice answers.json question.h5p
```

The command fetches a complete package and its dependencies from the official
H5P Content Type Hub when they are not already cached. Fetching is enabled by
default. `question.h5p` is ready to import into an H5P host.

For Lumi Desktop or Moodle installations that run Core API 1.27, request the
matching historical package set:

```text
pyh5p build H5P.MultiChoice answers.json question.h5p --core-api 1.27
```

## Python API

The same operation is available without a template or content-type-specific
Python code:

```python
import pyh5p

content = pyh5p.build(
    "H5P.MultiChoice",
    {
        "question": "What is 2 + 2?",
        "answers": [
            {"text": "4", "correct": True},
            {"text": "5", "correct": False},
        ],
    },
)
content.save("question.h5p")
```

The field names and validation rules come from the installed library's
semantics. The same API works with other runnable libraries, including local,
historical, and third-party libraries.

Media uses the same generic path. For example:

```python
content = pyh5p.build("H5P.Image", {"decorative": True}, validate=False)
content.import_media(
    "file", b"image bytes", target="images/example.png", mime="image/png"
)
content.save("image.h5p")
```

`LibraryStore` accepts local library directories and archives. Use it for
offline builds or for a controlled library set:

```python
store = pyh5p.LibraryStore("libraries")
content = pyh5p.build("H5P.MultiChoice", data, store=store, fetch=False)
```

## What is generic

The semantics engine handles groups, lists, nested libraries, text and HTML,
numbers, booleans, selects, image/video/audio/file fields, defaults, optional
values, regular expressions, ranges, allowed tags, widgets, `showWhen`,
`isSubContent`, unknown attributes, and recursive dependencies.

It also supports dependency closure for preloaded, dynamic, editor, and
nested-content libraries. Packages can be loaded, edited, validated, and
exported again. Builds use stable JSON ordering and ZIP timestamps where the
H5P format allows it.

## Command line

```text
pyh5p build H5P.MultiChoice answers.json question.h5p
pyh5p inspect question.h5p
pyh5p validate question.h5p
pyh5p schema H5P.MultiChoice --libraries .pyh5p-libraries -o schema.json
pyh5p unpack question.h5p unpacked
```

Use `--offline` with `build` when network access is not allowed. Use
`--cache-dir` to choose the library cache. `--content-only` is available when
the destination explicitly accepts a package without libraries.

To preview a package, import it into [Lumi Desktop](https://lumi.education/en/lumi-h5p-offline-desktop-editor/)
and open its preview. The H5P host runs the library JavaScript; pyh5p creates
the content and portable package.

## JSON Schema and AI applications

Ask pyh5p for the schema, pass it to any provider's structured-output API,
then validate and build the returned object:

```python
schema = pyh5p.schema_json("H5P.MultiChoice", store=store)
returned = provider.generate_json(schema=schema)
content = pyh5p.build("H5P.MultiChoice", returned, store=store)
content.require_valid()
```

The core package has no dependency on an AI provider.

## Compatibility and JavaScript hooks

Compatibility means that pyh5p parses the supplied definitions, validates the
semantic data, resolves dependencies, preserves distributable library files,
and writes the standard H5P package structure. The host must support the Core
API version declared by the selected libraries. Current Hub packages may
require Core API 1.28; `--core-api 1.27` selects the historical official Hub
package set for older hosts.

`presave.js` and `upgrades.js` are detected but never executed implicitly.
They can contain migrations that semantics alone cannot express. Applications
that need those migrations must provide a sandboxed JavaScript adapter through
the optional `js` extra, then validate the migrated data.

Historical packages can be installed from local sources when the current Hub
no longer serves their versions. pyh5p does not rewrite a library's declared
Core API or claim that a newer runtime works on an older host.

## Security

Archive reading and extraction reject traversal, symlinks, duplicate entries,
large archives, unsafe compression ratios, and disallowed extensions. Remote
downloads require HTTPS, an allowlisted host, and public DNS resolution.
Downloads happen only when fetching is enabled. Library JavaScript is never
executed by the Python package. Applications that display HTML or media must
apply their own sanitization and content policy.

## Development

```text
python -m pip install -e ".[dev]"
python -m pytest --basetemp .pytest-local
ruff check .
mypy src/pyh5p
python -m build
python -m twine check dist\*
```

The checked-in tests cover semantics, validation paths, dependency graphs,
nested content, media, round trips, archive security, Hub package selection,
and malformed input. `scripts/discover_types.py` can fetch an opt-in local
registry corpus. `scripts/differential_check.py` can run an external validator
when one is configured. PHP, Lumi, WordPress, and Moodle are not dependencies
of the package or CI job.

Contributions should include a regression test for each fixed bug. Keep the
core clean-room and do not copy code from the GPL H5P PHP libraries.

## License

The project is licensed under the [MIT License](LICENSE) and maintained by
[Nikita Berger](https://nikity.is-a.dev).

H5P is a trademark of H5P Group. pyh5p is independent and is not affiliated
with or endorsed by H5P Group, H5P.org, Lumi, Moodle, or WordPress. H5P
libraries retain their own licenses.
