h2hdb
h2hdb stores the shared catalog and download coordination data for an H2HDB
library. It supports SQLite and MariaDB and provides commands to initialize and
check the database.
Use this package when administering the database or reading the catalog from Python. To import galleries, browse books, or run downloads, choose the application for that task:
| Task | Application |
|---|---|
| Import downloaded galleries and prepare a reading library | h2hdb-ingest |
| Browse and download books with an OPDS reader | h2hdb-opds |
| Synchronize the published library with Komga | h2hdb-komga |
| Process gallery download requests | h2hdb-downloader |
The applications share one database. Installing Core alone does not import files, produce CBZs, or start a web server.
Install
Requires Python 3.14 or later. SQLite support is included. For a MariaDB installation, the supported baseline is MariaDB 10.11.11, including Synology package build 10.11.11-1551.
Install into a virtual environment:
python3.14 -m venv .venv
source .venv/bin/activate
python -m pip install h2hdb
python -m h2hdb --help
On Windows, create the environment with py -3.14 -m venv .venv and activate
it with .venv\Scripts\Activate.ps1 in PowerShell. Run subsequent python
commands in that environment.
If another H2HDB application manages your environment, use the Core version installed with that application. When upgrading a shared deployment, select application versions that support the same database schema.
Set up a database
SQLite
Save this as core-writer.json:
{
"database": {
"sql_type": "sqlite",
"database": "catalog.sqlite3",
"access_mode": "read-write"
},
"logger": {
"level": "INFO",
"file": null
}
}
This example creates catalog.sqlite3 in the current working directory. For a
service, use an absolute path in a persistent directory. Create its parent
directory first and ensure the process can write there.
Initialize the empty database, then verify it:
python -m h2hdb migrate --config core-writer.json
python -m h2hdb check --config core-writer.json
A successful initialization reports outcome=created and state=READY. The
database is ready for ingest; it does not yet contain a published library.
Follow the ingest application's setup instructions to import your galleries.
MariaDB
Create an empty database and a dedicated writer account on your MariaDB server.
The account used for initialization needs permission to create the schema's
tables, views, and indexes, as well as read and write its data. Save the
connection settings as core-writer.json:
{
"database": {
"sql_type": "mariadb",
"host": "127.0.0.1",
"port": 3306,
"user": "h2hdb_writer",
"password": "${H2HDB_DB_PASSWORD}",
"database": "h2hdb",
"access_mode": "read-write"
},
"logger": {
"level": "INFO",
"file": null
}
}
Provide H2HDB_DB_PASSWORD through the environment of the process running the
command, then run the same migrate and check commands as for SQLite.
Initialization creates tables inside the named database; it does not provision
MariaDB, create the database itself, or create accounts.
Reader configuration and secrets
For catalog readers, create core-reader.json with the same database location
and "access_mode": "read-only". On MariaDB, also use a dedicated reader account
with the read and metadata privileges required to inspect the schema. Keep the
writer credentials in ingest, downloader, and administration environments.
A JSON string equal to ${ENV_NAME} is replaced with that environment
variable's value. Missing variables stop startup. Embedded placeholders such as
"library-${INSTANCE}" are unsupported. Unknown configuration fields are
rejected, so check spelling when a file fails validation.
Core configuration has database, optional maintenance, and optional logger
sections. Application configuration files may wrap Core settings in another
section; use the relevant application's example for those commands.
Check and maintain the installation
| Command | When to use it | What success means |
|---|---|---|
python -m h2hdb migrate --config core-writer.json |
Initialize an empty database or resume an interrupted initialization | The database was created or resumed, or its existing READY marker was accepted |
python -m h2hdb check --config core-reader.json |
Run a full database audit | The current schema and stored catalog/coordination facts passed validation |
python -m h2hdb ready --config core-reader.json |
Run a frequent readiness probe | The database has the expected READY marker, schema version, and manifest |
ready is a quick, read-only check. It does not audit all stored data or verify
that external media files are present. check performs the full database audit
and may take substantially longer on a large library; it does not decode CBZs
or images.
Rerunning migrate on an already initialized database reports
outcome=already_ready and audit=not_performed. Use check when you need a full
audit. If initialization was interrupted, rerun migrate with the same software
and configuration; it resumes only a matching unfinished initialization.
The ingest application manages its own startup and periodic audit schedule. After an unclean stop, a validator change, or a due audit, startup may require a full check. A quick restart result is not evidence of a new full audit.
Logs go to the console by default. Set logger.file to save them to a file and
logger.level to "DEBUG" for more detailed timing and query statistics. During
a long full audit, progress records identify the active phase. Timing records
help diagnose a delay; they do not establish that the audit has finished.
INFO diagnostics cover source preparation and action costs, ingest claims, cleanup candidate selection and audit scans. Long-operation progress includes a pending connector call's category, fingerprint and age; completed counters do not include that call yet. Bounded slow-call samples retain expensive queries even after the detailed query-statistics capacity is reached. No query parameters or source payloads are logged. Phase totals expose repeated small costs that a list of the slowest individual phases can miss.
sql_calls counts connector method calls, not server statements or network
round trips. read_rows counts returned rows, not examined rows. Client SQL time
includes driver, transport, execution and waits. Use the manual cost probes to
measure backend work across input sizes; a fixed page size or SQL-call count is
not evidence of bounded database scanning.
Upgrade or restore a database
This release uses epoch 3, schema version 7. migrate initializes this
schema; it does not automatically upgrade older databases. Back up the database
and its matching library storage before changing the deployed application set.
Convert an exact schema-6 database
The one-time offline converter preserves the catalog, source observations, download queue, and external media. It only accepts the exact supported schema-6 database and the corresponding schema-7 software.
-
Stop ingest, downloader, OPDS, Komga synchronization, and any other database clients.
-
Take a verifiable database backup. Retain the matching library storage.
-
Obtain the source checkout matching the installed Core release. The converter is a checkout script, not a
python -m h2hdbsubcommand. -
From that checkout, use the matching Core Python environment and a read-write Core configuration to run:
python scripts/upgrade-audit-schema.py \ --config /path/to/core-writer.json --consumers-stopped
-
Resume compatible applications only after the conversion completes successfully.
--consumers-stopped acknowledges that you stopped the clients; it does not stop
them for you. The conversion includes a full database audit and can take as long
as check. It does not re-render CBZs. If interrupted, leave clients stopped and
rerun the same converter. Repeating a completed conversion performs a full audit
and reports already_converted.
Do not delete source folders, CBZs, or the existing database for this conversion. To return to the old software, restore the pre-upgrade database backup first; there is no automatic downgrade.
Other old or incompatible databases
Schema 5 and earlier have no supported in-place conversion. Preserve the old database and original downloaded source galleries, create a separate empty database, and use ingest to rebuild the catalog and a matching generated library. Keep the old database/library pair available until the replacement is verified. The rebuilt catalog does not automatically recover old download requests or operational history; re-enter any requests you still need.
An unexpected schema mismatch can also mean an application is using the wrong
Core version or database path. Confirm those settings before deciding a rebuild
is necessary. Do not use migrate to repair a damaged database in place.
Read the catalog from Python
For scripts that use the library directly, import the public API from h2hdb.
This example searches a library already published by ingest:
from contextlib import closing
from h2hdb import CatalogDiscoveryQuery, load_config, open_database
with closing(open_database(load_config("core-reader.json"))) as catalog:
revision = catalog.get_catalog_revision()
page = catalog.discover_publications(
query=CatalogDiscoveryQuery(search="example title"),
limit=50,
revision=revision,
)
for publication in page.publications:
print(publication.publication_id, publication.title)
open_database() performs a full audit. Discovery pages contain at most 128
publications. To continue, pass the returned page.next_cursor as after with
the same query and revision; None means there is no next page. If ingest
publishes a newer revision during browsing, fetch the current revision and
restart pagination instead of reusing the old cursor.
The public entry points cover these tasks:
| Entry point | Purpose |
|---|---|
VNextDatabaseAdminFacade |
Initialize, audit, and probe the database |
VNextCatalogFacade |
Read publications, search results, facets, tags, recent books, and image/acquisition descriptions |
VNextDownloadQueueFacade |
Submit and manage download requests |
VNextIngestFacade |
Coordinate an ingest integration |
Search combines its words and supplied filters with AND. Language, contributor, and subject filters match exact values. Upload/download time ranges include the start and exclude the end; page-count ranges include both ends and use the prepared artifact's page count. Catalog results describe the current publication only, and image/acquisition descriptions must be resolved by the application's storage adapter.
Troubleshooting
| Symptom | What to check |
|---|---|
| Configuration fails before connecting | Check JSON syntax, field names, environment variables, and that this is a Core configuration file |
| SQLite cannot open or write the database | Check the absolute path, parent directory, and process permissions |
| MariaDB connection or permission error | Check server availability, database name, credentials, and account privileges |
| Database is not READY after interrupted setup | Resume migrate with the same version and configuration |
| Schema or manifest mismatch | Check the installed application versions and the upgrade instructions above |
ready succeeds but the library is empty |
Complete the first ingest publication; readiness alone does not populate the catalog |
Full check fails |
Retain the error and backups; investigate the mismatch before restoring or rebuilding |
| Python catalog revision or cursor becomes invalid | Reload the current revision and restart the query |
For support, include the installed package versions, database backend, command, and redacted error in the issue tracker. Do not include credentials or an unredacted configuration file.
For optional local assessment, see the verification guide and catalog benchmark guide. These tools are separate from checking your own database.
License
Release files for h2hdb 0.39.9
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| h2hdb-0.39.9.tar.gz | 2.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| h2hdb-0.39.9-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.0 MB
Release files / h2hdb-0.39.9.tar.gz
| Download URL | h2hdb-0.39.9.tar.gz |
|---|---|
| Size | 2.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2d61ef8d8cfbccc28c9ef558e08881b70d4eea2cec3ed12ca55a59323c89351d
|
|
BLAKE2b-256 checksum How to use checksums |
526469b5ab9f7b68216a60487e99060fccb28d511995d6e44f561bede1924db7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / h2hdb-0.39.9-py3-none-any.whl
| Download URL | h2hdb-0.39.9-py3-none-any.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d2688d30838f1a11fc23f65bb358c9b7854a3a5ef632ea9c46bcafe6ffc860b2
|
|
BLAKE2b-256 checksum How to use checksums |
77a35e07a9d8acbadabe82120ad30ffc24e9e56d474091ab07de0805be3b9f8d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log