diff --git a/scripts/media-shows.py b/scripts/media-shows.py index ebacd3d..860a945 100755 --- a/scripts/media-shows.py +++ b/scripts/media-shows.py @@ -168,12 +168,16 @@ SEASON_META = re.compile(r"^season\.nfo$", re.I) FOLDER_IMAGE = re.compile(r"^folder\.(jpg|jpeg|png|webp|bmp)$", re.I) SEASON_DIR_RE = re.compile(r"(?i)^\s*(?:season[\s._-]*(\d{1,3})|s(\d{1,2}))\s*$") BAD_CHARS = re.compile(r'[<>:"/|?*]') -# SxxEyy with an optional second episode, plus the 1x04 form. The second number -# is guarded so a resolution never reads as an episode: "S01E01-1080p" is ONE -# episode, and the longest-match backtracking of \d{1,3} is what the (?![\dpP]) -# after it closes off. +# SxxEyy with an optional second episode, plus the 1x04 form. Both episode numbers +# are guarded so a longer digit run never reads as an episode. A resolution glued +# on stays legal — "S01E01-1080p" is ONE episode — while "S03E2019" and +# "S04E47370355" (both real names on the array, from a box set whose provider rows +# say the same junk) state no episode at all: the run is an airdate or a pack +# index, and the first three digits of it would steal E201 and E473 from the +# episodes that own them. No marker means the file is numbered by its provider +# row, or by tvdb's title, or it parks. EP_RANGE = re.compile(r"(?i)(? {MAP[f]['id']}" for f in sorted(MAP_SEEN)) + "\n") + if stalled: + fh.write(f"\n{len(stalled):>6} map rows name a folder on the array that\n") + fh.write(" never became a series: every file in it parked, so the id\n" + " had nothing to name. The map names a RECORD — it numbers\n" + " no episodes and owns no bonus material, so read the park\n" + " reasons above this line: a folder of openings, endings and\n" + " promos with no show to claim them is the usual shape.\n") + fh.write(" " + "; ".join( + f"{f} -> {MAP[f]['id']}" for f in stalled[:300]) + "\n") if unseen: fh.write(f"\n{len(unseen):>6} map rows name no folder on the array: the\n") - fh.write(" pack moved, or the row carries a name the array never held.\n") + fh.write(" pack moved (into the review pile counts: the map reads\n" + " top-level folders only), or the row carries a name the\n" + " array never held.\n") fh.write(" " + "; ".join(unseen[:300]) + "\n") if MAP_CLASH: fh.write(f"\n{len(MAP_CLASH):>6} folders carry a provider tvdb id the map\n") diff --git a/serverAppConfigs-docs/Dankserver/Media Relayout.md b/serverAppConfigs-docs/Dankserver/Media Relayout.md index 568a3a0..d361960 100644 --- a/serverAppConfigs-docs/Dankserver/Media Relayout.md +++ b/serverAppConfigs-docs/Dankserver/Media Relayout.md @@ -228,7 +228,10 @@ scripts/media-apply.py apply --plan /mnt/entropy/media-relayout-shows2-plan.tsv Evidence, strongest first — the order the planner believes things: -- the **episode marker the file's own name states** (`S01E02-E03`, `1x04`); +- the **episode marker the file's own name states** (`S01E02-E03`, `1x04`); a digit + run longer than three at that position is NOT a number — `S03E2019` and + `S04E47370355` are one box set's airdate fragment and pack index — so such a file + is renumbered by tvdb's title or parks, and is never truncated down to fit; - 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**: which episode the file IS, and the numbering of a file diff --git a/tests/media_shows.bats b/tests/media_shows.bats index 7b1bbd2..e77f07d 100755 --- a/tests/media_shows.bats +++ b/tests/media_shows.bats @@ -513,6 +513,46 @@ PY [[ "$output" == *"Best Of The Examples.mkv"* ]] } + +# A release pack can carry a number that is not an episode at all: two files of +# the Attack on Titan box set on the array are named S03E2019 and S04E47370355, +# and the provider rows the server recorded for them say the same junk. Reading +# either as "the first three digits" files the box set under E201 and E473 and +# steals those episode slots from their real owners, so a run too long to be an +# episode is no marker and no number: the file is renumbered from tvdb's title +# when the series' id answers it, and parks when nothing answers. +@test "an episode number longer than three digits never files as an episode" { + file "$LIB/Junk Numbers/Attack Pack S03E2019.mkv" 5000 + title_row "$LIB/Junk Numbers/Attack Pack S03E2019.mkv" "Attack Pack" 2013 3 2019 + plan_park + run grep -c -- "- S03E201 -" "$OUT" + [ "$output" = "0" ] + [[ "$(dst "$LIB/Junk Numbers/Attack Pack S03E2019.mkv")" == "$OUTSIDE/"* ]] + grep -q "a provider row of season 3 episode 2019" "$EXC" +} + +@test "a provider episode too long to be one is renumbered by the title tvdb states" { + printf '{"tvdb:series:4242": %s, "tvdb:episodes:4242": %s}\n' \ + '{"name": "Attack Pack", "year": "2013"}' \ + '{"3/7": "A Title tvdb Holds"}' > "$CACHE" + file "$LIB/Junk Numbers/Attack Pack S03E2019.mkv" 5000 + title_row "$LIB/Junk Numbers/Attack Pack S03E2019.mkv" \ + "Attack Pack" 2013 3 2019 "" "" "A Title tvdb Holds" "4242" + plan + want="$LIB/Attack Pack (2013) [tvdbid-4242]/Season 03" + want+="/Attack Pack (2013) - S03E07 - A Title tvdb Holds.mkv" + [ "$(dst "$LIB/Junk Numbers/Attack Pack S03E2019.mkv")" = "$want" ] +} + +@test "a resolution glued to the marker still reads as one episode" { + file "$LIB/Junk Numbers/Glued Mark S01E02-1080p.mkv" 5000 + title_row "$LIB/Junk Numbers/Glued Mark S01E02-1080p.mkv" \ + "The Examples" 2010 1 2 "" "" "Second Look" + plan + [ "$(dst "$LIB/Junk Numbers/Glued Mark S01E02-1080p.mkv")" \ + = "$LIB/The Examples (2010)/Season 01/The Examples (2010) - S01E02 - Second Look.mkv" ] +} + # Anime numbering is the one place a tvdb title can be confidently wrong: a pack # numbered by its release group still names S05E01, and tvdb answers that slot with # a different episode. A season the authority does not hold, or holds fewer of than @@ -655,6 +695,20 @@ plan_map() { # the plan, with the operator's map in han [ "$status" -eq 0 ] } +@test "a map row whose folder is on the array but never became a series says so" { + # The array holds three folders exactly like this one: a credits/extras pack + # the operator mapped to a record, whose every file is bonus material with no + # series to own it. The folder IS on the array, and telling the operator the + # pack moved sends them hunting for a folder that never left. + printf 'Credit Pack\t114801\tthe record tvdb names\n' > "$BATS_TEST_TMPDIR/ids.tsv" + file "$LIB/Credit Pack/01. Extras/NCOP 01.mkv" 1200 + plan_map "$BATS_TEST_TMPDIR/ids.tsv" + grep -q "bonus material with no series to own it" "$EXC" + grep -q "never became a series" "$EXC" + run grep "no folder on the array" "$EXC" + [ "$status" -ne 0 ] +} + @test "a map line whose id is not a number stops the plan" { printf 'Black Clover 001\tnotanid\ttypo\n' > "$BATS_TEST_TMPDIR/ids.tsv" file "$LIB/Black Clover 001/Season 01/Black.Clover.001.mkv" 5000