media: match new audiobooks against Audible nightly and on import #387

Merged
mysticalsoap merged 2 commits from feat/audiobookshelf-match into trunk 2026-10-07 19:00:00 -04:00
Owner

Problem

Audiobookshelf only reads what a release's tags carry, and the title comes from the album tag, so the same series arrived as Dungeon Crawler Carl, Book 2 - Carl's Doomsday Scenario, The Butcher's Masquerade: Dungeon Crawler Carl, Book 5 and Dungeon Crawler Carl (Unabridged). Audible has title, subtitle, description and cover separated, but a Match is a click per book (#379). Folder naming (#384) solved series and sequence, not titles.

Fix

  • media/metadata/audiobookshelf_match.py: runs the library's Match Books for every book library through the API; the library's "skip matching media with ASIN" keeps identified items out, so a run is idempotent; errors over ntfy.
  • dagu/dags/audiobookshelf-match.yaml: nightly at 00:30 in the metadata container (the only job container on the media network; dagu-tools is not), with a 90 s wait.
  • media/metadata/bookshelf_audio_import_hook.sh plus the mounts on bookshelf-audio: Readarr's On Release Import enqueues the same DAG, same shape as the Lidarr hook. The wait covers Readarr firing before the watcher has added the item; a match and a scan exclude each other, so a miss falls through to 00:30.
  • media/metadata/secrets/ added to the backup set (it already held the ntfy and Jellyfin keys).
  • Docs: Maintenance.md job row and paragraph, Media.md §7a Connect step and §8a API key step. .gitignore names the retag beets files instead of the deleted overhaul's.

Investigated and rejected: an Audiobookshelf-side trigger (no item-added event exists; upstream #3464 is still open) and tag writing from Readarr (no series/narrator/ASIN fields, in-place writes through the seed hardlink).

Verification

Live, 2026-10-07:

  • Connect entry created through the API; Readarr's Test event returns 200 (the hook exits 0 on anything but Download). The Dagu secret mount is owned by the app user, 600.
  • Dagu loaded the DAG (30 0 * * *, one step). First enqueued run failed on the match endpoint's plain-text OK reply; fixed, then a direct run prints audiobooks: match started (HTTP 200) and a second enqueued run succeeded (18:55:20 → 18:56:51, the 90 s wait included).
  • Audiobookshelf after the match: books 2, 3, 4, 5 and 8 went from tag-shaped titles to Audible's (The Butcher's Masquerade / Dungeon Crawler Carl, Book 5), all eight now carry an ASIN, series and sequence unchanged. Book 1 kept Dungeon Crawler Carl (Unabridged): it shipped an ASIN in its tags, so "skip ASIN" excludes it by design; one manual Quick Match.
  • Missing key file is reported over ntfy, not a traceback.

Closes #379

🤖 Generated with Claude Code

## Problem Audiobookshelf only reads what a release's tags carry, and the title comes from the `album` tag, so the same series arrived as `Dungeon Crawler Carl, Book 2 - Carl's Doomsday Scenario`, `The Butcher's Masquerade: Dungeon Crawler Carl, Book 5` and `Dungeon Crawler Carl (Unabridged)`. Audible has title, subtitle, description and cover separated, but a Match is a click per book (#379). Folder naming (#384) solved series and sequence, not titles. ## Fix - `media/metadata/audiobookshelf_match.py`: runs the library's Match Books for every book library through the API; the library's "skip matching media with ASIN" keeps identified items out, so a run is idempotent; errors over ntfy. - `dagu/dags/audiobookshelf-match.yaml`: nightly at 00:30 in the metadata container (the only job container on the media network; dagu-tools is not), with a 90 s wait. - `media/metadata/bookshelf_audio_import_hook.sh` plus the mounts on bookshelf-audio: Readarr's On Release Import enqueues the same DAG, same shape as the Lidarr hook. The wait covers Readarr firing before the watcher has added the item; a match and a scan exclude each other, so a miss falls through to 00:30. - `media/metadata/secrets/` added to the backup set (it already held the ntfy and Jellyfin keys). - Docs: Maintenance.md job row and paragraph, Media.md §7a Connect step and §8a API key step. `.gitignore` names the retag beets files instead of the deleted overhaul's. Investigated and rejected: an Audiobookshelf-side trigger (no item-added event exists; upstream #3464 is still open) and tag writing from Readarr (no series/narrator/ASIN fields, in-place writes through the seed hardlink). ## Verification Live, 2026-10-07: - Connect entry created through the API; Readarr's Test event returns 200 (the hook exits 0 on anything but `Download`). The Dagu secret mount is owned by the app user, 600. - Dagu loaded the DAG (`30 0 * * *`, one step). First enqueued run failed on the match endpoint's plain-text `OK` reply; fixed, then a direct run prints `audiobooks: match started (HTTP 200)` and a second enqueued run succeeded (18:55:20 → 18:56:51, the 90 s wait included). - Audiobookshelf after the match: books 2, 3, 4, 5 and 8 went from tag-shaped titles to Audible's (`The Butcher's Masquerade` / `Dungeon Crawler Carl, Book 5`), all eight now carry an ASIN, series and sequence unchanged. Book 1 kept `Dungeon Crawler Carl (Unabridged)`: it shipped an ASIN in its tags, so "skip ASIN" excludes it by design; one manual Quick Match. - Missing key file is reported over ntfy, not a traceback. Closes #379 🤖 Generated with [Claude Code](https://claude.com/claude-code)
music_retag.py writes retag-library.db / retag-state.pickle; the ignored
overhaul-* names belong to the deleted overhaul.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
media: match new audiobooks against Audible nightly and on import
All checks were successful
validate / validate (pull_request) Successful in 25s
0bd5fdc2a1
Audiobookshelf takes the title from the album tag and the rest from
whatever the release embedded, so titles arrive in a different shape per
release and a Match is a click per book. The library's Match Books
endpoint does the same thing for every item without an ASIN; with "skip
matching media with ASIN" on, a run is idempotent, and "prefer matched
metadata" lets Audible's title override the tag's.

Nightly DAG in the metadata container (reaches audiobookshelf on the
media network; dagu-tools does not), plus a Bookshelf-audio On Release
Import hook that enqueues the same DAG, like Lidarr's. The DAG waits
90 s before matching: Readarr fires the hook before the watcher has
added the item, and a match and a scan exclude each other, so a miss
falls through to the nightly run.

media/metadata/secrets/ joins the backup set: it already held the ntfy
and Jellyfin keys and was never included.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mysticalsoap deleted branch feat/audiobookshelf-match 2026-10-07 19:00:00 -04:00
Sign in to join this conversation.
No description provided.