feat(media): media-verify.py replays a journal against the servers' own tables
A scan answers 204 whether or not it found anything, so the fleet's documented proof — every move row has a row at its dst and zero rows still name its src — was a hand-written sqlite query every time anyone needed it. That is the step a half-finished relayout is judged on, so it is now a command with an exit code. It encodes the two doors that bite: Jellyfin stores the container-side path (/data stands for the share) while Plex stores the host path verbatim and hides deleted scans behind deleted_at on two tables. The report breaks the missing rows down by extension, because a .cue or .log has no row to land in and must not read as a lost album; --ignore-ext keeps those out of the verdict.
This commit is contained in:
1 parent
33f97cd3b3
commit
96944f0b17
3 files changed
+411
-13
No files matched your search
@@ -25,10 +25,11 @@ only reader is Plex — and nothing else in the repo touches a media file.
|
||||
| `scripts/media-shows.py` | no — TSV out | a plan that re-files the TV library itself |
|
||||
| `scripts/media-music.py` | no — TSV out | a plan that re-files the music library by its tags |
|
||||
| `scripts/media-audiobooks.py` | no — TSV out | a plan that re-files the audiobooks by the book Plex matched |
|
||||
| `scripts/media-verify.py` | no — reads the servers' DBs read-only | the verdict that every journaled move landed |
|
||||
|
||||
The seams are `tests/media_relayout.bats`, `tests/media_shows.bats`,
|
||||
`tests/media_music.bats` and `tests/media_audiobooks.bats`; the movie and TV
|
||||
target contracts are quoted from
|
||||
`tests/media_music.bats`, `tests/media_audiobooks.bats` and `tests/media_verify.bats`;
|
||||
the movie and TV target contracts are quoted from
|
||||
[jellyfin.org/docs/general/server/media/movies](https://jellyfin.org/docs/general/server/media/movies)
|
||||
in `media-plan.py`'s docstring.
|
||||
|
||||
@@ -118,19 +119,18 @@ shred -u /tmp/jf.hdr
|
||||
|
||||
A scan answers `204` whether or not it finds anything, so proof is the journal
|
||||
replayed against both servers' own tables: every `move` row has a row at its
|
||||
`dst`, and **zero** rows still name its `src`. There is no `sqlite3` binary on
|
||||
these hosts and none inside either container — read the DBs with the host
|
||||
`python3` and `mode=ro&immutable=1`, which takes no lock while the server runs.
|
||||
`dst`, and **zero** rows still name its `src`. `scripts/media-verify.py` is that
|
||||
replay: it opens each database read-only (there is no `sqlite3` binary on these
|
||||
hosts or inside either container) and exits non-zero while a row is stale.
|
||||
|
||||
| | path | path column | shape |
|
||||
|---|---|---|---|
|
||||
| Jellyfin | `/home/dank/Containers/jellyfin/config/data/jellyfin.db` | `BaseItems.Path` | one `BaseItems` table; `Type` holds the .NET class (`…Entities.TV.Episode`). There is no `MediaItems`/`TypedItems`. |
|
||||
| Plex | `/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db` | `media_parts.file` | join `media_parts → media_items → metadata_items`; filter `deleted_at is null` on **both** or deleted scans pollute the count. |
|
||||
```bash
|
||||
scripts/media-verify.py --journal /mnt/entropy/media-relayout-music.jsonl \
|
||||
--db-kind jellyfin --ignore-ext jpg,jpeg,png,log,cue,toc,nfo,txt,m3u,accurip
|
||||
```
|
||||
|
||||
**Jellyfin stores container-side paths.** The share binds `/mnt/entropy → /data`
|
||||
in that container, so a journal `src` of `/mnt/entropy/TVShows/…` is stored as
|
||||
`/data/TVShows/…`. Plex is bound at the same path it has on the host, so its
|
||||
rows match the journal verbatim.
|
||||
Ignore only what a server cannot index. A missing `.flac` or `.mkv` is the
|
||||
fault; a row still naming its `src` means the scan has not followed the rename
|
||||
yet, so re-run the check after the scan, never before.
|
||||
|
||||
Three doors that look right and are not: `GET /search?query=` matches metadata
|
||||
titles, never file paths; `?path=` on `/library/sections/N/all` makes Plex's
|
||||
|
||||
Reference in new issue
Block a user