-
CoverHashUrlCache — Tiny SQLite cache mapping cover URLs to their pHash strings.
-
CoverFetchPool — Downloads cover images concurrently and returns their pHashes.
comicbox.formats.base.online.cover_hash¶
source module comicbox.formats.base.online.cover_hash
Cover-hash primitives and the matcher’s hashing-invocation policy.
pHash (8x8 = 64 bits), computed in pure Python bit for bit the way imagehash.phash computes it, because Metron’s precomputed hashes (Issue.cover_hash, returned by Mokkari) come from imagehash and are string-compared against ours. ComicVine and GCD candidates require downloading the cover image — that’s M6’s concern.
The matcher invocation policy decides when hashing runs:
- Skip when the top metadata score is unambiguous (clears
confidence_thresholdAND well-separated from runner-up). - Hash top K candidates when uncertain or close-call.
- Skip when nothing clears
min_confidence(hashing won’t save it).
Classes
Functions
-
phash_int — Return the 64-bit pHash of an image, as
imagehash.phashcomputes it. -
compute_phash — Return the pHash of an image as a 16-digit lowercase hex string.
-
parse_hash — Parse a hex-encoded pHash string into its 64-bit integer.
-
hamming_distance — Hamming distance between two hex-encoded pHash strings.
-
cover_score — Convert a Hamming distance into a [0, 1] similarity score.
source phash_int(image: PILImage) → int
Return the 64-bit pHash of an image, as imagehash.phash computes it.
source compute_phash(image_bytes: bytes) → str
Return the pHash of an image as a 16-digit lowercase hex string.
source parse_hash(hex_str: str) → int
Parse a hex-encoded pHash string into its 64-bit integer.
Raises
-
ValueError
source hamming_distance(a: str, b: str) → int
Hamming distance between two hex-encoded pHash strings.
source cover_score(local_hash: str, candidate_hash: str) → float
Convert a Hamming distance into a [0, 1] similarity score.
s_cover = 1 - (hamming / 64). Clamped to [0, 1] for safety.
source class CoverHashUrlCache(db_path: Any)
Tiny SQLite cache mapping cover URLs to their pHash strings.
Holds ONE connection for its lifetime rather than reconnecting per call. The matcher hashes up to 15 candidates per ambiguous comic, so the old reconnect-per-get/set cost two fresh connections per candidate — pure overhead on the hot path. check_same_thread=False plus _lock keeps that single connection safe for the cover-fetch pool’s workers and for -j N boxes sharing one cache object.
Open / create the sqlite cache file at db_path.
Methods
-
get — Return the cached pHash for a cover URL, or None if absent.
-
get_many — Return the cached pHash for every URL that has one.
-
set — Store a pHash for a cover URL, overwriting any previous value.
-
set_many — Store many URL → pHash mappings in one transaction.
-
close — Close the connection. Safe to call more than once.
source method CoverHashUrlCache.get(url: str) → str | None
Return the cached pHash for a cover URL, or None if absent.
source method CoverHashUrlCache.get_many(urls: Sequence[str]) → dict[str, str]
Return the cached pHash for every URL that has one.
One query per chunk instead of one connection per URL — the batch cover-fetch path resolves a whole top-K set in a single round-trip against the cache before any download starts.
source method CoverHashUrlCache.set(url: str, phash: str) → None
Store a pHash for a cover URL, overwriting any previous value.
source method CoverHashUrlCache.set_many(pairs: Iterable[tuple[str, str]]) → None
Store many URL → pHash mappings in one transaction.
source method CoverHashUrlCache.close() → None
Close the connection. Safe to call more than once.
source class CoverFetchPool(max_workers: int = MAX_COVER_FETCH_WORKERS)
Downloads cover images concurrently and returns their pHashes.
Owns ONE httpx.Client for its lifetime, so a top-K batch reuses pooled connections instead of paying a TCP+TLS handshake per candidate. Failures are logged and dropped exactly as the serial path did — a cover that won’t download is a missing signal, never a failed lookup.
Record the worker ceiling; the HTTP client is built on first use.
Methods
-
fetch_hash — Download one cover and return its pHash, or None on any failure.
-
fetch_hashes — Download many covers concurrently; return
{url: phash}for the wins. -
close — Close the HTTP client. Safe to call more than once.
source method CoverFetchPool.fetch_hash(url: str) → str | None
Download one cover and return its pHash, or None on any failure.
source method CoverFetchPool.fetch_hashes(urls: Sequence[str]) → dict[str, str]
Download many covers concurrently; return {url: phash} for the wins.
URLs that fail to download or hash are simply absent from the result. Order is irrelevant — the caller maps back by URL.
source method CoverFetchPool.close() → None
Close the HTTP client. Safe to call more than once.