diff --git a/serverAppConfigs-docs/Dankserver/Media Relayout.md b/serverAppConfigs-docs/Dankserver/Media Relayout.md index f5bdaf5..d71bca6 100644 --- a/serverAppConfigs-docs/Dankserver/Media Relayout.md +++ b/serverAppConfigs-docs/Dankserver/Media Relayout.md @@ -1,7 +1,7 @@ --- type: reference title: Media Relayout -description: "Census, plan and apply the movie library into Jellyfin format, and move TV out of it: the gates, refusals, rollback." +description: "Census, plan and apply the movie and TV libraries into Jellyfin format: gates, evidence chain, refusals, rollback." tags: [dankserver, media-server, jellyfin, plex, procedure] timestamp: 2026-10-30T00:00:00Z --- @@ -12,8 +12,8 @@ The movie library on [[Entropy Volume]] arrived as release folders — `Some.Movie.2019.1080p.WiKiTaLx.x265-FOO/` with the video inside named after the release. [[Jellyfin]] matches a movie by folder and file name, so the release noise cost posters, extras and versions. These scripts turn that -library into Jellyfin's own shape — and move TV back out of it — and nothing -else in the repo touches a media file. +library into Jellyfin's own shape — the movies, the TV packs, and the TV that +turns up in the movie share — and nothing else in the repo touches a media file. | Script | Touches media? | Output | |---|---|---| @@ -21,8 +21,9 @@ else in the repo touches a media file. | `scripts/media-plan.py` | no — TSVs in, TSVs out | the plan + the exception report | | `scripts/media-apply.py` | **renames, never copies**; sweeps only what a run emptied | the moves, plus a journal | | `scripts/media-tv.py` | no — TSV out | a plan that moves TV into the TV library | +| `scripts/media-shows.py` | no — TSV out | a plan that re-files the TV library itself | -The seam is `tests/media_relayout.bats`; the target contract is quoted from +The seams are `tests/media_relayout.bats` and `tests/media_shows.bats`; the target contract is quoted from [jellyfin.org/docs/general/server/media/movies](https://jellyfin.org/docs/general/server/media/movies) in `media-plan.py`'s docstring. @@ -153,6 +154,66 @@ folder it owns, never from the title whose bonus folder it happens to be parked in — `The Cleveland Show S03E07` inside `Die Hard (1988)/extras/` is not a 1988 show. A name whose marker is `Part 2` is a film and stays put. +## The TV library's own relayout + +`/mnt/entropy/TVShows` is scene packs rather than a library, and Jellyfin has +already matched and numbered most of it. `scripts/media-shows.py` re-files that +library into the same shape, and it never guesses: every series name and every +episode number it writes traces to an authority, and what has none is parked for +a human to read. + +```bash +# 1. The authority: Jellyfin's own matched rows (the DB opens read-only). +python3 scripts/media-census.py jellyfin --root /mnt/entropy/TVShows \ + --out /tmp/tv_titles.tsv +# 2. What is on disk (stat only), and the runtimes the film arm needs. +python3 scripts/media-census.py enumerate --root /mnt/entropy/TVShows \ + --out /tmp/tv_enum.tsv +python3 scripts/media-census.py probe --root /mnt/entropy/TVShows \ + --enum /tmp/tv_enum.tsv --jobs 6 --out /tmp/tv_probed.tsv +# The probe is the slow step: ffprobe reads both ends of every file. +# 3. The plan. --tmdb needs TMDB_API_KEY; without it a series no provider dated +# is simply named without a year, which Jellyfin accepts. +python3 scripts/media-shows.py --root /mnt/entropy/TVShows --enum /tmp/tv_enum.tsv \ + --titles /tmp/tv_titles.tsv --probed /tmp/tv_probed.tsv \ + --out /tmp/tv_plan.tsv --exceptions /tmp/tv_exceptions.txt +# 4. Read the exceptions file FIRST — it is the review, not a log. Then the same +# executor, journal and rollback as the movie relayout above. +scripts/media-apply.py apply --plan /tmp/tv_plan.tsv --root /mnt/entropy \ + --journal /mnt/entropy/media-relayout-tv.jsonl --apply +``` + +Evidence, strongest first — the order the planner believes things: + +- the **episode marker the file's own name states** (`S01E02-E03`, `1x04`); +- the **`Season NN` folder it sits in and the number its name leads with inside + it**: `Season 19/01 Charles Barkley, Nirvana.avi` is S19E01; +- the **provider row**: identity (the series name and its year), and the + numbering of a file whose own name says nothing; +- a **bonus folder** anywhere above the file, or Jellyfin's `ExtraType` → the + series' canonical extra folder; +- a runtime over `SHORT_MAX` makes it film-shaped, and it leaves for `--movies` + only when a movie provider names it — otherwise it parks. + +Two refusals carry the design, and a dry run on the real library found both: + +- **Folders resolve a series; they never invent one.** A folder's words may + resolve a file to a series a provider has already named — `(CBB) Tengen Toppa + Gurren Lagann - The Complete Series (BD 1080p)` names the registered *Tengen + Toppa Gurren Lagann* — but a bonus subfolder is not a show. An early run made + `…/03. Extra/01. NCOP/NCOP 01.mkv` into a root series called `01 NCOP`. +- **A scraped row does not outrank the filesystem.** Jellyfin numbers + multi-season packs badly: every file under `SNL 16-20/Season 16..19` carries + season 16 episode 20, and one Gundam row says season 2004. The folder, then + the file's own leading number, win; a row that still insists on season 1989 + parks its file instead of creating `Season 1989`. + +The provider's name is copied verbatim, warts included: Jellyfin carries a Series +named `Black Clover 001` (92 episodes, no year) because it matched a pack off a +filename. Renaming a show is Jellyfin's work — a folder rename never touches its +provider ids — so the mover keeps the name it is handed and strips only the pack +index a release leads with (`4a. Seed DESTINY` → `Seed DESTINY`). + ## Laws - **A moved file is a 404 in [[Plex]] until its section is rescanned.** The diff --git a/serverAppConfigs-docs/index.md b/serverAppConfigs-docs/index.md index bf32af6..6b8cd36 100644 --- a/serverAppConfigs-docs/index.md +++ b/serverAppConfigs-docs/index.md @@ -52,7 +52,7 @@ is a locator: one row, one clause, no numbers. | [[Jellyfin]] | Jellyfin on Dankserver: quadlet unit, overlay metadata view, provider keys, ports, access, transcode status. | | [[mariadb]] | The MariaDB backend for RomM: quadlet unit, network alias, env-file secret handling, and the backup path. | | [[Media Metadata View]] | The SSD-backed overlay the two media servers read and write: shape, guards, commands, laws, rollback. | -| [[Media Relayout]] | Census, plan and apply the movie library into Jellyfin format, and move TV out of it: the gates, refusals, rollback. | +| [[Media Relayout]] | Census, plan and apply the movie and TV libraries into Jellyfin format: gates, evidence chain, refusals, rollback. | | [[Plex]] | Plex Media Server is a **containerized media server on Dankserver**: a podman | | [[redis]] | The Redis cache and task queue for RomM: quadlet unit, network alias topology, and the backup path. | | [[RomM]] | RomM (ROM Manager) is a self-hosted ROM library manager and player. |