Three things the 2026-10-31 five-library relayout taught, written where the next operator reads them: - A verify run must open the servers' databases plain `mode=ro`. Both keep their libraries in WAL mode; `immutable=1` skips the write-ahead log, so a scanner's own committed rows read as missing and the read can fail as "database disk image is malformed". The census page claimed immutable — now it says what the code does. - "No row at the destination" splits into four classes, and only REAL (no row at dst AND the file still at src) is the relayout's own damage. Phantom rows naming a pre-move path are normal behaviour for both servers. And the owning server decides whether a pass can convict: Jellyfin holds no audiobook library, so that journal verifies against Plex or it proves nothing. - A chain of passes rolls back newest journal first, or the tool refuses; the refusals measured that day are the tool working, not failing. The music and audiobook arms moved to their own pages (progressive disclosure; the parent drops 175 lines and stays the owner of the shared gates, the executor, the journal law and the rollback). Stale per-run counts destroyed: episode counts, byte sizes, move tallies for a pass that already happened.
69 lines
3.2 KiB
Markdown
69 lines
3.2 KiB
Markdown
---
|
|
type: reference
|
|
title: Media Music Relayout
|
|
description: "Re-filing /mnt/entropy/Music by Jellyfin's tag rows: the census, the album key, the refusals."
|
|
tags: [dankserver, media-server, jellyfin, media-relayout]
|
|
timestamp: 2026-10-31T00:00:00Z
|
|
---
|
|
|
|
# Media Music Relayout
|
|
|
|
`/mnt/entropy/Music` is rips and discography packs, and Jellyfin has matched and
|
|
tagged almost all of it. `scripts/media-music.py` re-files it into the shape
|
|
Jellyfin's music scanner reads —
|
|
|
|
Music/<album artist>/<Album> (<year>)/NN - <Title>.ext
|
|
|
|
— and the authority is the tag row of the track, never the name the ripper gave
|
|
the file. The shared gates, the executor, the journal law and the rollback live in
|
|
[[Media Relayout]]; this page is the music arm only.
|
|
|
|
```bash
|
|
# 1. The authority: Jellyfin's audio rows, one per track, with the album and
|
|
# artist ids its own database carries (the DB opens read-only).
|
|
python3 scripts/media-census.py music --root /mnt/entropy/Music \
|
|
--out /tmp/music_rows.tsv
|
|
# 2. What is on disk (stat only).
|
|
python3 scripts/media-census.py enumerate --root /mnt/entropy/Music \
|
|
--out /tmp/music_enum.tsv
|
|
# 3. The plan.
|
|
python3 scripts/media-music.py --root /mnt/entropy/Music --enum /tmp/music_enum.tsv \
|
|
--rows /tmp/music_rows.tsv --out /tmp/music_plan.tsv \
|
|
--exceptions /tmp/music_exceptions.txt
|
|
# 4. Read the exceptions file FIRST — it is the review, not a log. Then the same
|
|
# executor, journal and rollback as [[Media Relayout]] shows, on its own journal.
|
|
scripts/media-apply.py apply --plan /tmp/music_plan.tsv --root /mnt/entropy \
|
|
--journal /mnt/entropy/media-relayout-music.jsonl --apply
|
|
```
|
|
|
|
The group is what the tags assert, not where the files sit: **(album-artist tag,
|
|
album, year)**. One album arrives as three disc folders and as two copies of one
|
|
rip, and one MusicBrainz release group is one album. A compilation is one album
|
|
whose tracks name many performers, so it files once under `Various Artists`:
|
|
keying on the performer would file one album once per artist. Two different
|
|
album-artist tags on one album name are two albums, which is what Jellyfin's two
|
|
album items say.
|
|
|
|
What it refuses:
|
|
|
|
+- **A sidecar is placed by the audio around it, never by its own name.** A
|
|
`.lrc`, `.txt` or picture that shares a track's stem follows that track and
|
|
takes the name the track now has, or Jellyfin cannot find it; anything else
|
|
goes to the album its own folder was placed in, or to the folder whose discs
|
|
sit one level below it. A folder whose audio belongs to two albums parks its
|
|
sidecar rather than guessing.
|
|
+- **An image album keeps its name.** A folder holding one audio file and a
|
|
`.cue` is a whole album in one file; numbering it `01 - …` tells Jellyfin to
|
|
index it as one track.
|
|
+- **A video container parks**, because Jellyfin will not index it as music and a
|
|
rename to `.m4a` would claim it is; **audio no provider row names parks** too,
|
|
because unmatched audio is a tagging job, not a renaming job.
|
|
|
|
ONE writer at a time on this array: two rename passes race, and neither journal
|
|
tells. Verify the run against Jellyfin's own tables — see *Proving the move
|
|
landed* in [[Media Relayout]].
|
|
|
|
## See Also
|
|
|
|
[[Media Relayout]], [[Jellyfin]], [[Media Audiobook Relayout]]
|