Skip to content

feat: write publisher, subtitle and language tags (#208) - #426

Open
N0cky wants to merge 1 commit into
kikootwo:mainfrom
N0cky:feat/publisher-subtitle-language-tags
Open

N0cky wants to merge 1 commit into
kikootwo:mainfrom
N0cky:feat/publisher-subtitle-language-tags

Conversation

@N0cky

@N0cky N0cky commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #419 for #208: publisher, subtitle and language, on top of the ASIN post-step from #404. Same rules as before: under metadata_tagging_enabled, no new settings.

What is written

M4B/M4A/MP4 (ASIN post-step) MP3 FLAC
Publisher ----:com.apple.iTunes:PUBLISHER TPUB publisher
Subtitle ----:com.apple.iTunes:SUBTITLE TIT3, plus subtitle= to clear a source TXXX:SUBTITLE subtitle
Language ----:com.apple.iTunes:LANGUAGE TLAN language
  • Source: the same Audible details lookup (publisherName, language, plus new subtitle from Audnexus and the catalog). The book row stores none of them, so there is no fallback. Without a value nothing is written, and the source file's tags stay.
  • Values go through sanitizeShortTagValue: non-strings ignored, bounded, whitespace runs collapsed, controls dropped, capped at 1 KiB. The post-step reads every value back byte for byte, so what ffprobe would not return as written is replaced with U+FFFD: lone surrogates (toWellFormed) and U+FFFE/U+FFFF. Probed: ffmpeg's UTF-8 check rejects only those two. Other noncharacters, C1 controls, DEL, ZWJ, U+2028 and emoji read back fine.
  • buildMp4FreeformTags leaves out a value that is empty after sanitizing. Since the post-step writes all tags or none, one bad value can't cost the ASIN; a test covers this.
  • With tagging off, the ASIN step on a merged file still writes the ASIN only. The organizer takes its disk-space check and the "Embedded …" log line from buildMp4FreeformTags itself, so both match what is actually written. The two organizer tests use the real builder in their metadata-tagger mock for that.
  • The job log shows at most 100 characters of each value.

Choices worth a look

  • Language "English", not ISO 639. Audible returns english, and the tag is that name with a capital first letter, as Audiobookshelf's own Audible provider shows it. For MP3 this means TLAN=English, while ID3 expects an ISO 639-2 code there. With eng in MP3 files, Audiobookshelf would show "eng" next to "English" for M4B books, and its language filter would split one library into two values. Easy to change if you'd rather follow ID3 strictly, or leave language out for MP3.
  • MP3 subtitle as TIT3. ffmpeg's subtitle key becomes a TXXX frame, and a source TIT3 would survive next to it: the same "old next to new" problem as the FLAC description in feat: write description, genre, media type and gapless tags (#208) #397. TIT3 is the frame Mp3tag, Picard and Audiobookshelf's own embedding use, and Audiobookshelf reads it through its tit3 alias.

Testing

  • New metadata-tagger.book-details.test.ts (mapping per format, freeform builder) and metadata-tagger.book-details.integration.test.ts (real ffmpeg). Every source already carries the three fields, and the tests check that they are replaced, never duplicated: m4b freeform atoms in any case, MP3 TIT3 + TXXX:SUBTITLE + TPUB + TLAN, FLAC PUBLISHER/SUBTITLE/LANGUAGE with ORGANIZATION left alone. They also check that the ASIN still lands when another value is bad.
  • Extended tests for the subtitle mapping, tag details, the processor spread, and the organizer's gating and disk check.
  • e2e regressions/208-asin-tag extended and run locally (--trusted): Audiobookshelf reads tagPublisher "Audible Studios" and tagLanguage "English" into the book's publisher and language. With tagging off, neither is written. No fixture book has an Audible subtitle, so SUBTITLE is covered by the unit and real-ffmpeg tests only.
  • MP3/FLAC re-checked with the image's ffmpeg 9.0.2, read back with ffprobe 9.0.2 and mutagen.
  • Full npm test green (4045 passed), tsc clean, no new ESLint findings, docker build -f dockerfile.unified OK.

Metadata tagging now also writes the book's publisher, subtitle and
language from the Audible details lookup, under the existing
metadata_tagging_enabled switch (no new settings):

- M4B/M4A/MP4: freeform PUBLISHER, SUBTITLE and LANGUAGE in the ASIN
  post-step (ffmpeg's mp4 muxer drops them). A value that is empty
  after sanitizing is left out, so one bad value never costs the ASIN.
- MP3: TPUB, TIT3 (clearing a source TXXX:SUBTITLE so it never sits
  next to TIT3) and TLAN.
- FLAC: publisher, subtitle and language Vorbis fields, replacing the
  source's.

Values are bounded (1 KiB), whitespace-collapsed, and lone surrogates
and U+FFFE/U+FFFF become U+FFFD: the post-step reads every value back
byte for byte, and ffprobe returns neither as written. Non-string
lookup values are ignored. The organizer's disk check and log line come
from buildMp4FreeformTags, so they match what is written. The
language is Audible's name with its first letter capitalized
("English"), as Audiobookshelf shows it, not an ISO 639 code.

AudibleAudiobook gains subtitle (Audnexus and catalog). With tagging
off, the ASIN step on a merged file still writes the ASIN only.
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.

1 participant