metadata: unify the music pipeline into one per-album process #241
Labels
No labels
audit-work
bug
docs
general-admin
major-upgrade
needs-vps-sync
new-service
on-hold
outside-work
post-podman
renovate
upstream
vps
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
mysticalsoap/docker#241
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Music metadata is handled by four scripts that grew separately: music_audit (detect), music_retag (apply pinned retags), music_tag_sync (dates + comments), album_art_sync (art + the beets import that feeds everything). Four state files, two overrides files, load-bearing execution order in run_all.sh, and cross-script nudges (retag deletes an album's album_art_state entry to force a same-pass re-fetch). It works, but the couplings are invisible and each new concern adds another fragment.
Shape
One per-album pipeline (movie jobs stay separate):
mb_release-> dehardlink, full retag, then the normal passes.One state file, one overrides file, one report. Existing scripts become stages; musiclib.py carries the shared pieces.
Carried constraints
Constraint discovered on the first bulk music_tag_sync run: two instances raced (a hook-triggered dag run vs a manual docker exec) and fought over dehardlink tmps. Per-file collisions are hardened (short pid-unique tmp names), but the unified pipeline should assume a single-writer rule: every run goes through the dagu queue (enqueue, never direct exec), so the queue serializes library writers. Also: editing run_all.sh while a run is active corrupted the in-flight bash read (02:20 run errored) -- the unified single-script entry point removes that hazard too.
Stage: edition trim (from the Marquee Moon / Ants From Up There pass, PR #381)
An album that lands as a bonus-track edition (remaster, deluxe) should be cut down to the pinned release by the pipeline, not by hand. Today's manual sequence, which worked, maps onto one opt-in pin:
trimis explicit — a pin alone must never delete files.PUT /api/v1/album/{id}withanyReleaseOk: falseandreleases[].monitoredtrue only for the entry whoseforeignReleaseIdis the pin. The UI dropdown can't take an id and renders several editions identically (five "8 tracks, United States, 12" Vinyl" for Marquee Moon). Album id comes fromGET /api/v1/album?artistId=matched onforeignAlbumId(the release-group) — beets already has that.ws/2/release/<mbid>?inc=recordings), keep the local files that match by position + title, delete the rest. Seeds are unaffected either way: the library copy is a hardlink or already copy-broken. Pre-retag files have no MB ids, so this is title/number matching — the cue-split caveat from the retag stage applies here too.RefreshArtist, then re-monitor. Order is fixed: Lidarr's identifier follows the MB ids in tags, and without them it scores by date/label, picks a different edition and rejects every file with "Album release not requested". Switching the release never remaps on its own, and a rescan before the retag fails the same way.statistics.trackFileCount == trackCount; ntfy on mismatch.Plumbing: the metadata container has no Lidarr credentials today (the hook goes the other way, Lidarr → Dagu). Step 1/5 need the Lidarr API key as a secret in
media/metadata/secrets/,_FILEconvention, read inside the container only.