feat(media): the folder carries its tvdb id; specials and year-seasons resolve

Three defects in the TVShows planner, all three visible in the applied plan:

- A series folder named a show by words alone. Jellyfin's Tvdb agent and
  Plex's scanner both read `[tvdbid-NNNN]`, so the resolved id now rides the
  series folder — and only the folder: a file name gets 180 characters and
  the episode title needs the room.
- 1,412 files parked because their provider row said `season 0`. Season 0 is
  a legal season; Jellyfin's contract is `Season 00` for specials.
- 1,033 files parked because Jellyfin's date matcher writes the AIRDATE YEAR
  into the season column. Such a row is now renumbered by the title tvdb
  states for the series' id, read backwards off its own episode list. A
  title tvdb gives to two episodes names no episode, and that file parks
  with the reason it always carried.

A season tvdb's own episode list CONTRADICTS — a release pack books more
episodes there than the authority holds, the anime numbering hazard — keeps
its rename and loses the tvdb title, because the slot its number points at
is a different episode. Those series are listed in the exceptions file,
which is where the operator reads them.

Tests: six through the fixture (five red before the change; the ambiguous
title is a guard that the recovery must never guess). Full bats suite green.
This commit is contained in:
Frank committed 2026-10-31 12:27:15 -07:00
1 parent 04802b3e48
commit acab4cef22
3 files changed
+221 -18

No files matched your search

@@ -181,7 +181,8 @@ show. A name whose marker is `Part 2` is a film and stays put.
already matched and numbered most of it. `scripts/media-shows.py` re-files that
library into the shape Jellyfin reads, with **tvdb as the name authority**:
<Series> (<year>)/Season NN/<Series> (<year>) - SxxEyy - <Episode Title>.ext
<Series> (<year>) [tvdbid-NNNN]/Season NN/
<Series> (<year>) - SxxEyy - <Episode Title>.ext
It never guesses: every series name, episode number and title it writes traces to
an authority, and what has none is parked for a human to read.
@@ -234,7 +235,11 @@ Evidence, strongest first — the order the planner believes things:
whose own name says nothing;
- the **row's tvdb id, else its TMDb id carried through TMDb's external ids, else
an exact tvdb name search**: that id names the SERIES FOLDER and supplies the
episode title, because a name the server read off a folder is not a show name;
episode title, because a name the server read off a folder is not a show name.
The folder CARRIES the id: `[tvdbid-NNNN]` is what Jellyfin's Tvdb agent and
Plex's scanner both read, so a show whose title is ambiguous resolves by number
instead of by a human guessing which folder is which. The id rides the folder
only — a file name gets 180 characters and the episode title needs the room.
- 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
@@ -244,7 +249,7 @@ Evidence, strongest first — the order the planner believes things:
for the word "russia", *Special Edition I* for a file saying *Edition III*. Else it
parks.
Two refusals carry the design, and a dry run on the real library found both:
Four refusals carry the design: what the planner will NOT do is the point of it.
- **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
@@ -254,8 +259,23 @@ Two refusals carry the design, and a dry run on the real library found both:
- **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 file's own leading number, win.
- **A season that is a YEAR is the date matcher, not a season.** Jellyfin files
specials by airdate, so a row can arrive saying `season 2002`; that is not a
season and it never becomes a folder. The one authority on such a row is tvdb's
episode list read backwards: the title the row carries names the (season,
episode) tvdb gives it. A title tvdb gives to two episodes names nothing, and
the file parks with the reason it always carried. `Season 00` IS a legal
season — specials live there — so a row that really says 0 files there instead
of parking.
- **A season tvdb's own list contradicts gets no tvdb title.** An anime pack
numbered by its release group books `S05E01` in a season tvdb does not hold, or
books more episodes there than tvdb lists: the slot its number points at is a
different episode, and dressing it in that episode's title is a confident lie.
The file still renames to the standard shape; the title does not ride, and the
series is listed under *seasons whose numbering tvdb contradicts* in the
exceptions. A season holding FEWER files than tvdb lists stays plausible — a
partial pack is the normal shape.
**A provider name is evidence only when an id backs it.** Jellyfin names a Series
after the folder it scanned, so a library filed by release folders ends up with