Conversation
img2webp rejects any frame duration <= 0 ("Invalid negative duration"),
aborting the whole animated-WebP repack. Some sources (e.g. scraped
animated WebPs) carry 0ms frame durations, so clamp each duration up to
img2webp's 1ms minimum in Img2WebPAnimatedLossless.img2webp_args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Use a real PicoptSettings (not SimpleNamespace) in the img2webp duration test so config assignment type-checks. - Mark the module-level pytest autouse fixtures with `# pyright: ignore[reportUnusedFunction]`; they are used by pytest, not referenced by name. - Disable reportImplicitStringConcatenation: ruff format canonicalizes adjacent string literals to implicit concatenation, so the check can never be satisfied. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
radon cc flagged four functions at rank C. Extract behavior-preserving helpers to bring each to rank A/B: - WalkSkipper._is_skippable (16): split into _skip_reason and the disk-only _skip_reason_fs. - Walk._count (12): extract the recursion-stop predicate _count_stops_here. - pick_route_handler (12): fold the convert gate into _pick_convert_handler and extract _can_pack_repack for the repack check. - Handler._cleanup_after_optimize (11): extract the _should_replace predicate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per-directory .picopt.yaml resolution rebuilds the confuse config for every directory in the tree, repeating the timestamps, ignore, after, and memory-limit log lines each time. Gate them on the existing print_summary flag, as set_format_handler_map already does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The treestamps program config was recorded from the run-level CLI config, so identical options expressed via a tree root's .picopt.yaml instead of flags always mismatched and every stamp was silently discarded, re-optimizing whole trees. Each tree now records its own effective config: the new Grove builds one Treestamps per top path from DirConfig.get_tree_settings, which resolves the root's .picopt.yaml beneath CLI and env. timestamps: true at a tree root now enables stamping without -t (dry run and list still disable it, and subdirectory configs still can't toggle it mid-walk). The directory-config fingerprint is now per tree and value-based: it hashes the parsed picopt section of each config strictly below the root, so comment and formatting edits, -W rewrites, running with a different set of top paths, and file-vs-directory targets of the same tree no longer invalidate stamps — only real option changes do. The root's own config is excluded because its options are recorded as values. Stamps discarded on a config mismatch now warn with the differing keys (treestamps 4.1.1); stdlib logging is intercepted into loguru so the warning renders through the shared console, and log messages escape Rich markup so bracketed names like "Title [en]" display intact. Migration: existing stamp files mismatch once after this upgrade (now loudly); a single -N run preserves old stamps instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T7R5s6frL7jEykysAzjttq
PIL raises a plain OSError when it recognizes an image format but the contents are corrupt (e.g. a BMP with pixel depth 0), which escaped the detection probe and printed a full stack trace. Detection now wraps it in UnreadableImageError, reported as a one-line warning that is also counted and listed in the summary; the file is skipped untouched. The per-file exception guards now skip the stack trace for any PicoptError — extending the contract cli.main already had: expected conditions print their message, only unexpected exceptions dump stacks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T7R5s6frL7jEykysAzjttq
- Drop the `cls: type[Any]` annotation on the RarDetector/ZipDetector `identify` classmethods; ty flagged the widened first parameter as an invalid override of `Detector.identify`. - Replace remark's `no-duplicate-headings` with `no-duplicate-headings-in-section`. A changelog necessarily repeats `### Fixes` under each version; the in-section rule still catches real duplicates. Also includes the in-progress dependency bumps and ruff rule additions that were already in the working tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Add JPEG XL support Read and write JPEG XL via the pillow-jxl-plugin codec, as a new auto-discovered plugin. JXL is optimized by default and is now the preferred still conversion target ahead of WEBP and PNG. Only lossless JXL is re-encoded; lossy (XYB encoded) JXL is left alone, the same policy lossy WebP already has. The codec exposes no such signal, so picopt/pillow/jxl.py parses the codestream image header to read xyb_encoded, and refuses files carrying JPEG reconstruction data, whose re-encoding would silently destroy the ability to restore the source JPEG. New --convert-jpeg-to-jxl converts JPEG to JXL by lossless bitstream reconstruction, which is reversible byte for byte. It requires -c JXL as well: picopt otherwise never converts from a lossy source, so -c JXL alone must not sweep up a whole photo library. The codec only takes its reconstruction path when PIL knows the source filename, which it does only for path opens, so the tool opens by path and spills to a temp file for inputs that have none. That makes it work for archive members too. Three smaller things the codec needed: - It registers no save_all handler, and pil_save always passes save_all, which raised KeyError. The single-frame handler is registered for it; animated JXL is unsupported by the codec regardless. - Given no exif kwarg it reads EXIF off the opened image, which quietly defeated --strip-metadata. prepare_info now passes an empty exif. - It rejects palette and other modes, so ImageHandler gained a prepare_image hook, symmetric with prepare_info, to substitute one. Handlers can now declare CONFIG_ENABLED_KEY, a config flag that must be true for them to be probed at all. Failing it leaves the handler out of the probed handler map, which routing already reads as unavailable, so the startup conversion banner stays accurate for free. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Gate WebP to JXL conversion behind --convert-webp-to-jxl JPEG XL support in browsers and image tooling is still thin, so converting away from WebP, which every browser reads, should be a deliberate act rather than a side effect of asking for -c JXL. JPEG and WEBP are both formats picopt processes by default, which means the usual "name the source with -x" gate cannot express a choice about them: -x JPEG and -x WEBP are no-ops. Each therefore carries its own flag, and a plain -c JXL once again converts only GIF and PNG, as the README has always described. The previous commit had quietly made WEBP a third convert-by-default source. The flag hangs off the handler class, and JxlLossless also serves PNG, GIF and native JXL, which must stay ungated. So the WebP route now points at a JxlFromWebP subclass that exists only to carry CONFIG_ENABLED_KEY. Converting out of JXL into a more widely readable format stays ungated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * update deps * update deps * update deps --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Adding an option to the recorded timestamps config used to invalidate every existing timestamp file, re-optimizing whole trees. Picopt now passes its packaged defaults to treestamps 5.0.0, which fills keys missing from either side before comparing, so only a changed option value invalidates. Timestamp files written before 6.7.0 added the JXL options, and before 6.6.2 added the directory config fingerprint, are honored again. Timestamp files now open with a comment saying what they are, that they are machine-written, and that deleting one re-optimizes that tree. The redundant treestamps_config block is gone; older files still load. Discarding timestamps because a sub-directory .picopt.yaml changed now names it that way instead of printing the internal fingerprint key. Replace the local directory config fingerprint with the identical treestamps implementation, pinned by golden digests upstream. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
merge_config_file truncated the target before writing, so a crash, a full disk, or a kill during -w, -W, or --write-config-file could destroy a hand-authored .picopt.yaml or user config. Write to a unique sibling temp file, fsync, then replace, matching the discipline the image handler and treestamps already use for files that matter less. The temp name is unique rather than a fixed sibling so two concurrent writers cannot unlink each other's temp mid-write, and the owner-only mode is set before the rename so the target is never briefly wider. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Requires treestamps 5.0.1. A run that discarded a timestamps file for a config mismatch only rewrote it if it also optimized something. A run that optimized nothing left the rejected file on disk, so it warned about the same mismatch every time. uv.lock is left at treestamps 5.0.0 until 5.0.1 publishes to PyPI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
that discards it, even when that run optimizes nothing. Previously the
rejected file survived unchanged and warned about the same mismatch on every
subsequent run. Requires treestamps 5.0.1.