Skip to content
This repository was archived by the owner on Oct 8, 2026. It is now read-only.

Latest commit

 

History

History
246 lines (204 loc) · 12.8 KB

patchnotes.md

File metadata and controls

246 lines (204 loc) · 12.8 KB

Patch notes

v0.3.0 — 2026-09-15

The crash-safety release. The two HIGH findings that let a mid-run failure break the safety identity are closed, the never-deletes story's remaining holes are plugged, and the recorded 5.22 lane (the Audiobookshelf rescan push) ships. 111 tests, up from 71.

Safety

  • Crash-safe apply journaling. backup.json and reorg-manifest.json are rewritten atomically after every mutation instead of once at the end of the pass, so a crashed pass leaves a journal that reverses exactly what happened instead of losing the undo. An apply with nothing to change still leaves the existing journal alone. The journal is one-deep, and the depth is now documented (decision 2026-09-15).
  • Malformed-audio guard. A corrupt file raises MutagenError per file (warn, skip, continue) in retag, discover, and restore, instead of aborting the whole run mid-pass.
  • restore() works on headerless MP3s (it now saves with an explicit path; the old argument-less save() raised TypeError mid-restore), skips missing or unreadable entries with a warning, and returns a non-zero exit code when it had to skip.
  • narrator: None preserves companion-key credits. A file carrying the narrator only in the freeform key (TXXX:NARRATOR / ----:NARRATOR) no longer gets both slots blanked behind --apply.
  • reorg --reverse refuses occupied paths instead of silently replacing whatever arrived at the original staging path, and returns a non-zero exit code on refusals.
  • Both engines pre-flight catalog paths. Absolute paths and .. escapes are refused before any work, instead of crashing mid-apply (retag) or after all renames with no manifest (reorg).

New

  • Audiobookshelf rescan push (the 5.22 lane). reorg --apply --abs-url ... asks a configured Audiobookshelf server to rescan its libraries: opt-in, best-effort (a failed push never fails the completed moves), stdlib urllib only, and it never fires in a dry run. The spec's absolute no-network clause is amended at both recorded sites.
  • retag --fail-on-change: a dry run that exits non-zero when changes are pending. The idempotency property becomes a schedulable drift monitor and the natural pre-flight for the rescan push.
  • Undo previews and a pre-restore snapshot. --restore and --reverse accept --dry-run (per-file plan, nothing written); a real restore first snapshots the current tags to pre-restore.json, so restoring from the wrong backup is itself reversible.
  • abt tags FILE: a read-only dump of one file's physical tags that doubles as the malformed-file diagnostic (unreadable files print the mutagen error cleanly and exit 1).
  • --book NAME scoping (repeatable, case-insensitive) for retag and reorg: landing one book no longer costs four full-library passes. The help notes the sharp edge: a scoped --apply overwrites the one-deep undo journal with just the scoped files.
  • validate riders: a series-index padding-consistency lint (mixed padding mis-orders series folders), and embedded cover art now counts toward missing_covers (13 false positives gone on the real library).
  • status cross-reference: pairs not-on-disk entries with nearly named unclaimed folders, the signature of a path typo in the catalog.
  • discover warns when a multi-file set's tags disagree with the drafted first-file values, so a wrong first track no longer propagates silently.
  • python -m audiobooktools works (__main__.py); both console scripts already did.

Engine

  • One home for the extension constants: schema.AUDIO_EXT (with .mp4 now visible to every engine), schema.MP4_EXT, schema.IMAGE_EXT; the four drifted per-module copies are gone. The discs layout accepts non-mp3 tracks instead of hardcoding *.mp3.
  • Chapter titles keep legitimate trailing numbers ("05 Episode 42" stays "Episode 42"); only the leading track number is stripped.
  • series_gaps no longer lets fractional indices fill whole-number slots ("2.5" used to mask a missing book 2).
  • The single-layout fallback only fires when a folder holds exactly one audio file, so a missing named file can never silently tag another book's file.
  • load_catalog evicts the catalog package between loads, so two catalogs in one process no longer share the first catalog's DESC.

Packaging and docs

  • .gitignore is a lean project-specific file (the stock template's trap rules like *.log and lib/ are gone) and ignores .audiobooktools/; .gitattributes marks catalog/** linguist-vendored so the author's data stops counting as engine code.
  • Docs truth sweep: the spec's Restore-and-reverse section now states the real journal contract (format, depth, timing, pre-restore snapshot, occupied-path refusal, the unrecorded empty-dir prune), the dead ~/.claude pointers are re-pointed, workflows.md gains a read-only inspection section and marks the staging-vs-NAS section historical, the roadmap's PyPI phase reflects the pushed tag, and curation-rules drops the culled dramatized edition as an example.

Tests

  • 111 tests, up from 71, including the end-to-end idempotency checks the docs promised (synthetic-library retag and reorg), crash-journal behavior under simulated mid-apply failures, malformed-file skips, the restore/reverse guards, the path pre-flights, and every new command.

v0.2.0 — 2026-07-05

Phase 2 (quality of life). Three read-only introspection commands and a collapsed reorg view. Nothing here writes to the library; every new command is safe to run at any time.

New commands

  • status — one-screen summary of the catalog against a library: book count and how many entries actually have files on disk, resolved file count and total runtime (summed from decoded durations, with unreadable files reported separately), catalogue entries not present on disk, audio files on disk that no entry claims, and possible series-index gaps.
  • validate — lint pass over the catalog: missing covers, unused DESC keys, duplicate series_index values, subtitle/index disagreements (decimal .5 indices compared numerically so they do not false-positive), empty or junk narrator strings, and implausible year values. Exits non-zero when it finds anything, so it can gate CI; a clean catalog exits 0.
  • discover — scans a directory (default <library>/Unfiltered) and prints paste-ready draft entries pre-filled from each file's existing tags. Layout is inferred from the folder shape; multi-file layouts are emitted with a review marker. Dedups against a discoverable catalog, so it surfaces only new books. This also covers the "catalog autocompletion" idea: point --path at one new folder to draft that single entry.

Enhancements

  • reorg --diff — a collapsed dry run: one line per destination directory with its file count, instead of enumerating every move.

Engine

  • New audiobooktools.report (status + validate, with the mechanical checks factored into pure, unit-tested helpers) and audiobooktools.discover (tag reading, layout inference, and draft rendering).
  • reorg.run gains a diff keyword; reorg.print_diff_summary is the collapsed printer.

Tests

  • New tests across test_report.py, test_discover.py, test_release_notes.py, and a --diff case in test_reorg.py (71 total, up from 40).

Tooling and packaging

  • CI. GitHub Actions runs ruff (lint + format check) and pytest on Python 3.10–3.13 for every push and PR, plus a build job that fails if the author's catalog/ ever leaks into a distribution.
  • Distribution hygiene. The wheel is engine-only and the sdist now excludes catalog/, so personal library data never ships. Verified against a real uv build.
  • Runtime version source. audiobooktools.__version__ resolves from installed-package metadata first (importlib.metadata) and falls back to the repo's VERSION file, so wheel installs report the real version instead of 0.0.0+unknown (the VERSION file does not ship in the wheel).
  • Release automation. scripts/release_notes.py extracts a version's section from this changelog; a dormant publish.yml uses it to cut a GitHub Release and publish to PyPI (trusted publishing) when a vX.Y.Z tag is pushed. Inert until a PyPI trusted publisher is configured.
  • Catalog template. catalog-template/ is a minimal, copyable starting point for a fresh catalog/.

v0.1.3 — 2026-05-30

Enhancements

  • Typing and Linting: Fixed mypy typing issues by ignoring missing stub imports in pyproject.toml. Code passes strict checks and is now fully formatted by Ruff.
  • Documentation: Added comprehensive docstrings to all functions in cli.py, reorg.py, retag.py, and schema.py.
  • Robustness:
    • reorg.py: Improved collision checks during planning (_check) to safely detect and block operations where a destination file already exists and isn't being natively renamed or moved away in topological order.
    • retag.py: Improved resolve_files robustness around canonical vs staging paths.

v0.1.2 — 2026-05-30

Bug Fixes

  • Standalone Book Promotion: Fixed a bug in retag.py's resolve_files where a book previously added as a standalone (and correctly reorganized to its standalone path) would not be found if it was later promoted to a series (by adding a second book to the series). Added a new probe to check the standalone alternate path if the file has not yet been moved to the series folder.

v0.1.1 — 2026-05-30

Bug Fixes

  • File Resolution Fallback: Fixed a critical bug in retag.py's resolve_files. Previously, if a single layout explicitly requested a file via the file attribute and that file was missing from its staging directory, the tool would fall back to _top_audio and incorrectly overwrite the tags of the first unrelated audio file it found. It now correctly returns an empty list and prints a missing file warning.

v0.1.0 — 2026-05-28

Initial release. Extracts and formalizes the toolset that previously lived inside the author's audiobook library as a private .retag/ directory.

Highlights

  • Two-engine design. retag writes embedded tags from the catalog; reorg derives the folder tree from the same catalog. Both are dry-run by default and idempotent.
  • Reversible. Each apply pass writes a backup (backup.json) or manifest (reorg-manifest.json) that fully reverses the operation via --restore / --reverse.
  • Catalog separated from engine. audiobooktools/ is the engine; catalog/ is the user's data. The CLI loads the catalog by file path so one engine install can drive many catalogs.

Engine

  • audiobooktools.schema — sanitize, owned_counts, book_dir, single convenience constructor, GENRE constant. Helpers now take books as a parameter instead of reading from module-level globals.
  • audiobooktools.retag — refactored from the original retag.py; the module-level ROOT constant is gone; library_root is threaded through the call chain. Programmatic entry point: retag.run(library_root, books, *, apply, verbose, backup_path).
  • audiobooktools.reorg — same treatment. reorg.run(library_root, books, *, apply, manifest_path).
  • audiobooktools.cli — top-level CLI with retag / reorg subcommands, registered as both audiobooktools and abt console scripts via pyproject.toml. Discovery: --catalog defaults to the nearest catalog/books.py walking up from $PWD; --library defaults to the catalog's grandparent if it looks like a library, else $PWD.

Catalog

  • Pruned to 54 currently-owned audiobook series-entries (from the prior 103-entry spec). DESC dictionary pruned from 74 to 30 entries to match.
  • Six Pierce Brown Audible Studios m4bs onboarded as Red Rising series entries 1-6 (Tim Gerard Reynolds narration), replacing the prior Red Rising (Dramatized Adaptation) entry.
  • Past entries remain available in the original .retag/spec.py and the pre-extraction git history.

Docs

  • README.md — public-facing pitch and three-command tour.
  • spec.md — full data contract: BOOK dict schema, layout vocabulary, MP4/ID3 tag mapping, folder scheme, idempotency guarantee.
  • roadmap.md — Phase 1 (extraction) done, Phase 2 candidates listed.
  • docs/workflows.md — operational walkthroughs (adding from Unfiltered/, normal apply, restore, reverse).
  • docs/curation-rules.md — high-marks principle, narrator-quality decisions, year = recording year.
  • CLAUDE.md — project-local guidance for AI sessions.

Tests

  • All pure-helper tests carried over with import updates.
  • tests/test_catalog_imports.py — smoke test that catalog imports cleanly and every entry is well-formed.
  • The original on-disk integration test (which poked the live library) is replaced with a tmp_path-based collision-detection test.