Repository navigation
feat: write description, genre, media type and gapless tags (#208) - #419
Merged
Merged
Conversation
Re-land of #397 on top of the merged ASIN post-step (#404). - Tagger: description (MP4 desc + ldes, MP3 TXXX description, FLAC comment = the Vorbis DESCRIPTION field, so a source DESCRIPTION is replaced, not doubled), genre (top-level genres joined with "; ", "/" in a name becomes " & "), and on MP4 media_type=2 (stik) and gapless_playback=1 (pgap), all before -movflags +faststart. - Descriptions: line breaks and tabs become spaces, other C0 controls are dropped, and each value is capped at 16 KiB UTF-8 at a word boundary with an ellipsis. - Processor: one Audible details lookup per import, shared with the series backfill; when it only serves the tags it gives up after 15 s (stored description, no genres). The tagging switch is read once and handed to the lookup and organize(). - AudibleService: primaryGenres (top of each category ladder / Audnexus type genre). Co-authored-by: Tobias Bunk <github@bunk.at> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Tobias Bunk <github@bunk.at> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Description: the raw text is cut to 4x the 16 KiB cap (surrogate safe) before any work, then non-whitespace C0 controls are dropped and each whitespace run (incl. VT, FF, U+2028/U+2029) becomes one space in a single linear pass. The old pattern was quadratic on long space runs (200,000 spaces: about a minute). - Genres: split on '/' and ';', trim, join with ' & ' (no regex backtracking; both are Audiobookshelf separators). - The stored description fallback is bounded and HTML-stripped like the lookup path. - A failed ffmpeg run reports the stderr tail, never the argv. - The tag details lookup starts right after the switch read and is awaited just before organize(); a no-op handler keeps it handled. - capUtf8Bytes clamps tiny limits; catalog primaryGenres keeps only non-blank strings; docs updated. - The #208 spec checks Audiobookshelf's book genres and description. Co-authored-by: Tobias Bunk <github@bunk.at> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Tobias Bunk <github@bunk.at> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- metadata-tagger.ts was over the 400-line limit: the value sanitizers, raw bound, byte cap and ffmpeg failure text move unchanged into src/lib/utils/metadata-text.ts (no behaviour change); their direct tests move to tests/utils/metadata-text.test.ts. - The unhandled-rejection test now makes resolveTagDetails reject, so it fails without the processor's no-op catch. Co-authored-by: Tobias Bunk <github@bunk.at> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Tobias Bunk <github@bunk.at> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 20, 2026
Merged
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.
Linked issue
Part of #208. Re-lands #397 by @N0cky, who wrote the original change and is credited as co-author on these commits. #397 gets closed once this merges.
What changed
With metadata tagging on (the existing
metadata_tagging_enabledswitch; no new settings), imported files now also carry the book's description, genres and, on MP4, the audiobook media type and gapless flag:desc+ldesTXXX:descriptioncomment(the VorbisDESCRIPTIONfield, so an existing one is replaced, not doubled);)©genTCONGENREstik=2pgap=1primaryGenreson the Audible book: Audnexus genres of typegenre, or the top of each catalog ladder./and;inside a genre name become&, because Audiobookshelf splits genre tags on both. The existing display list is unchanged.src/lib/processors/organize-tag-details.ts). When the lookup only serves the tags, it gives up after 15 s: the stored description is used (bounded and HTML-stripped like the lookup's), with no genres.src/lib/utils/metadata-text.ts. Docs:documentation/phase3/file-organization.mdanddocumentation/integrations/audible.md.Publisher, subtitle and language (the rest of #208) can build on the ASIN post-step from #404.
Checks
mainmerged in: the full unit suite in the Node 24 test container passes (296 files, 3,952 passed, 5 skipped), and so do the image build, the smoke test and the end-to-end spec below (3/3).regressions/208-asin-tag.spec.ts, three imports): with tagging on, Audiobookshelf reads the ASIN, the description and the genres as separate genres (for exampleMystery, Thriller & SuspenseandScience Fiction & Fantasy) from a single M4B and from a chapter merge; with tagging off, no description is written. Smoke: pass.moovfirst) and a 200 KB MP3 description. FLAC is checked at the ffmpeg level: exactly oneDESCRIPTIONafter a rewrite.main.Risk and rollback
Release note
🤖 Generated with Claude Code