Skip to main content

H2HDB Downloader (h2hdb-downloader)

Automates downloading galleries from exhentai/e-hentai (via hbrowser) and recording their state in an h2hdb database. It has no CLI or standalone runtime of its own — it's a library consumed by another project that owns the browser session and the overall process lifecycle.

Concepts

  • Gallery — a single exhentai/e-hentai gallery, identified by a gid (numeric id) and represented as h2h_galleryinfo_parser.GalleryURLParser once its URL is known.
  • Dedup — before issuing a real network download, the package reads live h2hdb state to see if the gid is already settled (downloaded, with no redownload flag or durable request). Settled gids are skipped — except periodically, at a random interval (1 to 19 attempts), when one is force-redownloaded as an integrity re-check.
  • Durable requests — immediately before a real download starts, the package creates a tokenized request in h2hdb's todownload_gids table. It conditionally completes that exact token only after success. A False result, exception, cancellation, or process termination leaves resumable work behind, while a newer request for the same gid cannot be erased by an older attempt finishing late. h2hdb also uses this table to publish a redownload request after all active deletion-candidate folders for a gid have actually disappeared.
  • Database coordination — every short h2hdb read/write section enters h2hdb's cross-process maintenance gate with a five-minute wait interval. Browser search, downloads, retry sleeps, and tag traversal stay outside the gate, so maintenance is never blocked by network work.
  • Manual queue — add a (gid, url) row to the CSV configured by csv_path. It is converted into the same durable request and picked up the next time the queue is drained. Before replay, the inbox is atomically rotated to a same-directory hidden claim file; interrupted claims are replayed automatically on the next run.
  • Deep download — download a gallery, then look at its artist/group tags and download sibling galleries that match a set of search conditions (e.g. other-language releases of the same work).

API

Downloader is the public service object; TagCascadePolicy is the other public export. Every method either acts on a target you explicitly pass in or, for the two queue-reading methods below, hands back a plain value with no further bookkeeping required from you. There is no "run the whole thing" method: deciding when to stop, what order to process things in, and how to report progress is the calling application's job, not the library's.

Downloader(
    driver: ExHDriver,         # an un-entered driver; see below
    config_path: str,          # path to the h2hdb JSON config
    csv_path: str | None = None,  # path to the manual download-queue CSV
    *,
    wait4client: int,       # seconds to wait before retrying after ClientOfflineException
    retry2download: int,    # seconds to wait before retrying after InsufficientFundsException
)

csv_path only enables the optional "queue a gid/url by editing a CSV file" feature described above. Leave it as None if you don't need that; durable database requests and live deduplication still work.

Downloader is itself an async context manager that opens and closes the browser session for you, so driver is expected un-entered:

async with Downloader(ExHDriver(headless=False), ...) as downloader:
    ...

If you'd rather manage the driver's lifecycle yourself, pass an already-entered driver and skip async with downloader.

Method names follow one rule throughout: no suffix means it operates directly on a GalleryURLParser you already have; _by_gid means it resolves a bare gid to its gallery via search first, then does the same thing.

  • await download_by_gallery(target) — download one GalleryURLParser, or an iterable of them. Returns {gid: downloaded} for each. Retries automatically on ClientOfflineException (waits wait4client seconds) and InsufficientFundsException (waits retry2download seconds); a wait of 0 means "don't retry, raise immediately."
  • await download_by_gid(gid) — resolve a bare gid to its gallery via search, then download it. If the gid no longer resolves to anything, it's recorded as removed in h2hdb; if it resolves to a different gid (the gallery was merged/redirected), the original gid is flagged for deletion after the replacement downloads successfully.
  • await download_by_tag(tag, conditions) — download every gallery under a hbrowser Tag, once per search condition in conditions (or unconditionally if conditions is empty).
  • await deep_download_by_gallery(gallery, policy, skip_check=False) — download gallery, then for each tag in policy.filters (e.g. "artist", "group") on that gallery, call download_by_tag with policy.conditions. The cascade only runs if the initial download actually happened, unless skip_check=True forces it to run regardless (useful when you already know the gallery is downloaded from a separate call and just want the cascade). policy is a TagCascadePolicy(filters, conditions) — both fields always travel together, so they're grouped into one frozen value object rather than two parallel parameters.
  • await deep_download_by_gid(gid, policy, skip_check=False) — same gid-resolution as download_by_gid, but deep.
  • await drain_queue(policy, skip_check=True) — absorb the manual CSV and process one live snapshot of durable database requests. A request is removed only after a successful download, confirmed removal, or successful redirect. The method doesn't loop or wait for new work.
  • pending_redownload_gids() — a snapshot list of gids h2hdb currently flags as needing a periodic redownload. Every call reads live database state; read-only and safe to call repeatedly as you work through it.

Example

The calling application owns the loop. A typical one drains the queue once, then walks the pending-redownload list, deep-downloading anything that actually got (re)downloaded:

import asyncio
from h2hdb_downloader import Downloader, TagCascadePolicy
from hbrowser import ExHDriver
from h2h_galleryinfo_parser import GalleryURLParser

policy = TagCascadePolicy(
    filters=("artist", "group"),
    conditions=("language:chinese$", "language:speechless$"),
)


async def main():
    async with Downloader(
        ExHDriver(headless=True),
        config_path="h2hdb-config.json",
        csv_path="todownload_gids.csv",
        wait4client=30 * 60,
        retry2download=4 * 60 * 60,
    ) as downloader:
        gallery = GalleryURLParser("https://exhentai.org/g/123/456/")
        await downloader.download_by_gallery(gallery)
        await downloader.download_by_gid(666)
        await downloader.deep_download_by_gallery(gallery, policy)

        await downloader.drain_queue(policy, skip_check=True)
        for gid in downloader.pending_redownload_gids():
            await downloader.deep_download_by_gid(gid, policy, skip_check=True)


asyncio.run(main())

License

This project is distributed under the terms of the GNU General Public Licence (GPL). For detailed licence terms, see the LICENSE file included in this distribution.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

h2hdb_downloader-0.4.8.tar.gz (31.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

h2hdb_downloader-0.4.8-py3-none-any.whl (23.4 kB view details)

Uploaded Python 3

File details

Details for the file h2hdb_downloader-0.4.8.tar.gz.

File metadata

  • Download URL: h2hdb_downloader-0.4.8.tar.gz
  • Upload date:
  • Size: 31.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for h2hdb_downloader-0.4.8.tar.gz
Algorithm Hash digest
SHA256 cf55e8438b4050b777d5ead115998b5a8c2b568fcf2480fc4f37925321ac754a
MD5 d481ea7de0d4cc20ae2b0b8b0a76636a
BLAKE2b-256 9371a4b6f8f34cc6a543354a0492f153c0718d2e14dc4a6e924b40f2311b0fd4

See more details on using hashes here.

Provenance

The following attestation bundles were made for h2hdb_downloader-0.4.8.tar.gz:

Publisher: publish.yml on Kuan-Lun/h2hdb-downloader

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file h2hdb_downloader-0.4.8-py3-none-any.whl.

File metadata

File hashes

Hashes for h2hdb_downloader-0.4.8-py3-none-any.whl
Algorithm Hash digest
SHA256 67f1f7ef87768de8b0ade37207030d2fbe3bdbd71401810f9c05873af8ec7dd7
MD5 df6971dbb74ea07dfdc63ef98e5d8d93
BLAKE2b-256 1c6a5940477e05faf46ee40ee4d3dae944d67a6dee78faba0c701b36ac007892

See more details on using hashes here.

Provenance

The following attestation bundles were made for h2hdb_downloader-0.4.8-py3-none-any.whl:

Publisher: publish.yml on Kuan-Lun/h2hdb-downloader

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.15.0

2 files

0.14.0

2 files

0.13.1

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.7

2 files

0.10.5

2 files

0.10.4

2 files

0.10.3

2 files

0.10.2

2 files

0.10.1

2 files

0.10.0

2 files

0.9.5

2 files

0.9.4

2 files

0.9.2

2 files

0.9.1

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.9

2 files

This release

0.4.8 This release

2 files

0.4.7

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

0.0.9

2 files

0.0.7

2 files

0.0.6

2 files

0.0.5

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page