feat(metadata): audio tag rewrite via ffmpeg + CBZ ComicInfo injection

Phases 5b and 5c of the metadata writer.

5b — single-file audio (MP3/FLAC/M4A/M4B): the container's tags are
rewritten with ffmpeg -c copy (no re-encode; -map 0 keeps cover art,
-map_chapters 0 keeps chapters). The key set is exactly what the
scanner's applyAudioTagFallbacks reads back — title+album, subtitle,
artist/album_artist, composer (narrators, Audible's convention),
grouping + iTunes series-part, date, genre — so a write + rescan
round-trips. Output is ffprobe-VALIDATED (duration within 1s/0.5% of
the source) before the atomic rename; a lossy container rewrite never
replaces a good book. Fields the format can't carry per our own read
chain are deliberately not written, and fields_written reports only
what really round-trips (subtitle rides MP3/FLAC — this image's mp4
muxer drops subtitle keys, discovered empirically and pinned by test).

5c — CBZ: ComicInfo.xml injected (or replaced) with the same atomic zip
rewrite as the EPUB path, extended with an append-if-missing mode
since a comic may not carry one yet. Field map mirrors the scanner's
comic block: Title/Series/Number/Count/Volume, Writer (authors),
Summary, Publisher, Year/Month/Day, LanguageISO, Web, Genre+Tags;
fractional series numbers are omitted rather than truncated to a lie.
CBR/CB7 stay on the sidecar path (RAR/7z can't be rewritten in place).

Integration tests generate media at test runtime with ffmpeg (m4b) and
in-memory zips (cbz) — no binary fixtures — pinning the tag round-trip
through ffprobe, post-write rescan titles, the ComicInfo injection and
the validation-gate behavior.
This commit is contained in:
John O'Keefe committed 2026-10-27 16:04:34 -04:00
1 parent eaf1b2c8dd
commit 59eddc1386
5 files changed
+727 -30

No files matched your search

+10 -2
View File
@@ -309,8 +309,16 @@ everything stays in sync:
(cover, chapters and reading position are untouched). The file's
checksum changes, so device syncs (Kobo, KOReader) re-key the book on
their next sync.
- **Everything else** (PDF, comics, folder books, audio until tag
writing ships) — a Calibre-style sidecar is written next to the file:
- **Single-file audio (MP3/FLAC/M4A/M4B)** — the container's own tags
are rewritten (audio streams copied untouched, chapters and cover art
preserved). Only fields the format can carry are written: title,
subtitle (MP3/FLAC), authors, narrators (composer tag — Audible's
convention), series, date and genre.
- **CBZ** — a ComicInfo.xml is written inside the archive (injected or
replaced). CBR/CB7 archives can't be rewritten in place, so they take
the sidecar path.
- **Everything else** (PDF, folder books, CBR/CB7) — a Calibre-style
sidecar is written next to the file:
`metadata.opf` when the book is alone in its folder (Calibre's own
convention, so Calibre reads it too), or `<filename>.opf` when several
books share a folder, so one book's sidecar can never leak onto its