Skip to content

feat: write description, genre, media type and gapless tags (#208) - #397

Closed
N0cky wants to merge 1 commit into
kikootwo:mainfrom
N0cky:feat/richer-metadata-tags
Closed

N0cky wants to merge 1 commit into
kikootwo:mainfrom
N0cky:feat/richer-metadata-tags

Conversation

@N0cky

@N0cky N0cky commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Part of #208: the first PR, with the fields ffmpeg writes natively. Publisher, subtitle and language follow once the ASIN post-step has landed.

What is written (under metadata_tagging_enabled, no new settings)

M4B/M4A/MP4 MP3 FLAC
Description (Audible summary) desc + ldes TXXX:description DESCRIPTION
Genre (top-level genres, ; -joined) ©gen TCON GENRE
Media type Audiobook stik=2 – –
Gapless pgap=1 – –
  • Line breaks in the summary become single spaces (the existing sanitizer drops them; this keeps paragraphs from running together).
  • Genres: new primaryGenres on AudibleAudiobook = Audnexus genres of type: 'genre' (sub-genre tags left out) or the top of each catalog ladder. The existing genres display list is unchanged. Audiobookshelf splits genre tags on ;; Audible genre names contain & and , but no ;.

Data

  • When tagging is on, the organize job looks up getAudiobookDetails(asin) once and shares it with the existing missing-series backfill (src/lib/processors/organize-tag-details.ts), so there is at most one lookup per import.
  • Behaviour change: with tagging on, every import with an ASIN now makes that one Audnexus request (before: only when series was missing). A failed lookup is non-fatal and falls back to the stored description.

Testing

  • Tag mapping unit tests per format, plus real-ffmpeg integration tests (round trip via ffprobe)
  • Checked against the image's ffmpeg 9.0.2: standard mdir atoms kept, stik/pgap read correctly by mutagen
  • Full npm test suite green, tsc clean, no new ESLint findings, docker build -f dockerfile.unified OK

…#208)

Metadata tagging now also writes a fixed set of fields ffmpeg supports
natively, under the existing metadata_tagging_enabled switch (no new
settings):

- Description (Audible summary): M4B desc + ldes, MP3 TXXX:description,
  FLAC DESCRIPTION. Line breaks become single spaces.
- Genre: top-level genres only, joined with "; " (Audiobookshelf splits
  genre tags on ";").
- M4B/M4A/MP4: media type Audiobook (stik=2) and gapless (pgap=1).

AudibleAudiobook gains primaryGenres (Audnexus genres of type "genre",
or the top of each catalog ladder); the genres display list is
unchanged. With tagging on, the organize job looks up the Audible
details once per import and shares them with the missing-series
backfill. A failed lookup is non-fatal and falls back to the stored
description.
@kikootwo

Copy link
Copy Markdown
Owner

Thanks @N0cky, this is a nice set of tags, and keeping paragraphs apart in the description is a good catch.

I'm going to take it from here: it'll land on our branch with you as co-author, on top of the ASIN tag change that's going in first, since that reworks the same tagging code. A few changes on top:

  • FLAC: the description gets written as comment. A file that already has a description would otherwise keep both, and Audiobookshelf shows them joined ("old;new").
  • Descriptions get capped at 16 KB. A very long one can make ffmpeg fail to start, and then the file loses all of its tags.
  • A / in a genre name gets replaced, since Audiobookshelf splits genres on / before it tries ;.
  • When the series is already known, the details lookup gets a 15-second limit, so a slow Audible doesn't hold up the import.
  • A test that the new tags still read back after the ASIN step rewrites the file.

I'll close this PR once that lands, so I'm leaving it open until then.

@N0cky

N0cky commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, sounds good, and good catches on the FLAC duplicate and the / split. Happy to do publisher/subtitle/language on top once the ASIN step is in, or leave them to you if you'd rather fold them in while you're there. Your call.

@kikootwo

Copy link
Copy Markdown
Owner

Thanks! I'll keep the re-land to what's here so it goes in cleanly. Publisher, subtitle and language would be great as a follow-up from you. Base it on main once this lands; I'll ping you here when it does.

kikootwo added a commit that referenced this pull request Oct 20, 2026
Re-land of #397. With metadata tagging on, imported files carry the Audible description and top-level genres (MP4 desc/ldes and ©gen, MP3 TXXX:description and TCON, FLAC comment and GENRE) and, on MP4, media type Audiobook (stik=2) and gapless (pgap=1). Descriptions are bounded and cleaned in one linear pass and capped at 16 KiB; genres are split on / and ; for Audiobookshelf. One Audible details lookup per import, shared with the series backfill, skipped after 15 s when it only serves the tags. Part of #208.

Co-authored-by: Tobias Bunk <github@bunk.at>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kikootwo

Copy link
Copy Markdown
Owner

@N0cky this landed in #419 with you as co-author, and it'll be in the next release. Thanks again!

It's on main now, so publisher, subtitle and language can be based on it whenever you're ready. The tag text helpers now live in src/lib/utils/metadata-text.ts, and the ASIN post-step (addMp4FreeformTags() in metadata-tagger.ts) is where new freeform items go.

@kikootwo kikootwo closed this Oct 20, 2026
@kikootwo kikootwo mentioned this pull request Oct 20, 2026
@N0cky
N0cky deleted the feat/richer-metadata-tags branch October 20, 2026 23:03
@kikootwo

Copy link
Copy Markdown
Owner

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants