Skip to content

Add optional jxl extra for JPEG XL page support - #290

Merged
bpepple merged 4 commits into
mainfrom
jxl-cover-hash
Nov 8, 2026
Merged

bpepple merged 4 commits into
mainfrom
jxl-cover-hash

Conversation

@bpepple

@bpepple bpepple commented Nov 8, 2026

Copy link
Copy Markdown
Member

Summary

Pillow can't decode JPEG XL images, so comics with .jxl pages couldn't be cover-hashed. They printed "Unable to get cover hash." and couldn't be matched by cover, and duplicate-page detection didn't work on them either.

This PR adds pillow-jxl-plugin as an optional jxl extra. When the plugin is installed, it's registered with Pillow on package import, so cover matching (talker.py) and duplicate detection (duplicates.py) both handle .jxl pages.

Changes

  • pyproject.toml / uv.lock: new jxl = ["pillow-jxl-plugin>=1.3.8"] extra
  • metrontagger/__init__.py: import pillow_jxl if it's installed; skip it silently if not
  • README.md: document the jxl extra
  • tests/test_talker.py: cover-hash test for a JXL cover, skipped when the plugin isn't installed

Without the extra, behaviour is unchanged.

Pillow has no JPEG XL decoder, so comics with .jxl pages could not be cover-hashed ("Unable to get cover hash.") or checked for duplicate pages.

Add pillow-jxl-plugin as an optional `jxl` extra and register it with Pillow on package import when it's installed. Document the extra in the README and add a cover-hash test that's skipped unless the plugin is installed.
@bpepple bpepple self-assigned this Nov 8, 2026
@bpepple bpepple added enhancement New feature or request minor New functionality; bumps the minor version labels Nov 8, 2026
pillow-jxl-plugin raises RuntimeError (not OSError) on corrupt or truncated JXL data. That error escaped get_comic_cover_hashes_cached and could abort a tagging run. In duplicate detection it was logged with a full stack trace instead of the usual debug skip.

- Catch RuntimeError when hashing covers (talker.py) and pages (duplicates.py).
- Add tests for a truncated JXL image and for a simulated decoder RuntimeError, in both modules.
- Install the jxl extra in CI on Python 3.11-3.13, the only versions pillow-jxl-plugin ships wheels for, so the JXL tests actually run.
pillow-jxl-plugin defaults to DECODE_THREADS = -1, which starts one decoder thread per CPU core for every image. Duplicate detection already decodes pages in a thread pool, so this oversubscribed the CPU. Set DECODE_THREADS = 0 after registering the plugin.
pillow-jxl-plugin decodes the whole image inside Image.open() and has no option to decode at a smaller size, so the img.draft() call in duplicate detection doesn't help with JXL pages. Note the extra CPU/memory cost in the README's jxl extra entry, point users to --quick-duplicates, and update the comment above the draft() call.
@bpepple
bpepple merged commit bf5a392 into main Nov 8, 2026
8 checks passed
@bpepple
bpepple deleted the jxl-cover-hash branch November 8, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request minor New functionality; bumps the minor version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant