diff --git a/scripts/media-shows.py b/scripts/media-shows.py index 481674b..5da7470 100755 --- a/scripts/media-shows.py +++ b/scripts/media-shows.py @@ -428,10 +428,13 @@ held = {r["path"]: r for r in rows} def row_names_the_park(path, name): """The library's own review pile is not a show. When the park sat INSIDE the library the server indexed the whole folder as one series named after it, and - a row like that would rename a real show to `_unsorted` — so a row whose - series is the hidden top-level folder the file itself sits in is dropped.""" + a row like that would rename a real show to the pile — so a row whose series is + the hidden top-level folder the file itself sits in is dropped. The server also + sanitizes the display name, dropping the leading underscore, so `unsorted` and + `_unsorted` are the same pile: compare them without their leading punctuation, + or the pile survives as a visible series folder.""" top = rel(path).split("/")[0] - return top.startswith("_") and eng.norm(name) == eng.norm(top) + return top.startswith("_") and eng.norm(name.lstrip("_")) == eng.norm(top.lstrip("_")) def in_review_pile(path): @@ -687,10 +690,14 @@ def series_key(name): def label_of(s): - """` ()`, the form both matchers read.""" + """` ()`, the form both matchers read. A provider's name can + arrive WITH the year already on it — TVDb hands back `Acapulco (2021)` for the + reboot — and appending a second one names a folder nothing will match again.""" nm, yr = identity(s) nm = clean(nm) - return f"{nm} ({yr})" if yr else nm + if not yr or re.search(r"\(\d{4}\)\s*$", nm): + return nm + return f"{nm} ({yr})" def episode_title(r, s, season, ep): diff --git a/tests/media_shows.bats b/tests/media_shows.bats index 71fe428..d5c3b9b 100755 --- a/tests/media_shows.bats +++ b/tests/media_shows.bats @@ -93,6 +93,7 @@ setup() { # The park inside the library: the server indexed the whole pile as ONE show # named after the folder, so its rows state a series that is not a show. at "$PARK/Park Show S01E01.mkv" 5000 + at "$PARK/Twisted.Metal.S01E01.1080p.WEB-DL.mkv" 5200 # The enum census lists every file; the planner reads that instead of walking # the tree, so the fixture writes the same three columns. @@ -226,6 +227,17 @@ PY = "$LIB/Show Box (2016)/Season 01/Show Box (2016) - S01E01 - Pilot.mkv" ] } +@test "a provider name that already carries the year is not given it twice" { + # TVDb answers the reboot as `Acapulco (2021)`. The folder contract is ONE year, + # and `Acapulco (2021) (2021)` is a folder neither matcher nor human reuses. + printf '{"tvdb:series:78850": {"name": "Show Box (2016)", "year": "2016"}}\n' > "$CACHE" + plan + [ "$(dst "$BOX/Show.Box.S01E01.1080p.WEB-DL.mkv")" \ + = "$LIB/Show Box (2016)/Season 01/Show Box (2016) - S01E01 - Pilot.mkv" ] + run grep -F '(2016) (2016)' "$OUT" + [ -z "$output" ] +} + @test "a series no authority resolves keeps the provider's name and is reported" { plan [ "$(dst "$LIB/SNL Pack/Season 19/01 Charles Barkley, Nirvana.avi")" \ @@ -241,6 +253,21 @@ PY [[ "$output" == "$OUTSIDE/"* ]] } +@test "the pile's sanitized display name is still the pile, not a series" { + # Jellyfin drops the leading underscore when it shows the hidden folder, so the + # row says the series is "unsorted" while the file sits in `_unsorted`. Match + # only the underscored spelling and the plan renames real files into a visible + # folder named after the review pile — which is how 617 of them got there. + title_row "$PARK/Twisted.Metal.S01E01.1080p.WEB-DL.mkv" "unsorted" "" 1 1 \ + "" "" "Twisted Metal" + plan_park + run awk -F '\t' -v l="$LIB" \ + '$1 == "move" && index($3, l "/unsorted/") == 1 {print $3}' "$OUT" + [ -z "$output" ] + run awk -F '\t' '$1 == "move" && $2 ~ /Twisted\.Metal\.S01E01/ {print $3}' "$OUT" + [[ "$output" == "$OUTSIDE/"* ]] +} + @test "a subtitle follows its episode and keeps its language flag" { plan [ "$(dst "$SEASON/The.Examples.S01E01.Pilot.1080p.x265.en.srt")" \