feat(media): tvdb names the show — titled episode files, id-backed series folders, park outside #102

Merged
dank merged 10 commits from feat/tv-tvdb-naming into main 2026-10-31 12:12:15 -07:00
2 changed files with 46 additions and 4 deletions
Showing only changes of commit c70f9b3f6d - Show all commits

No files matched your search

+14 -4
View File
@@ -278,9 +278,14 @@ def tmdb(kind, query):
CACHE[ck] = None
return None
top = found[0]
# The two search endpoints date their rows differently: movies carry
# `release_date`, tv carries `first_air_date`. Reading only one of them leaves
# every film answer year-less, and a year-less answer can never be refused by
# the year guard below.
when = str(top.get("year") or "") or \
str(top.get("release_date") or top.get("first_air_date") or "")[:4]
CACHE[ck] = {"name": (top.get("title") or top.get("name") or "").strip(),
"year": str(top.get("year") or "") or (top.get("first_air_date") or "")[:4],
"id": top.get("id")}
"year": when, "id": top.get("id")}
return CACHE[ck]
@@ -787,8 +792,13 @@ for r, name, kind in extras:
for r in films:
parsed, year = title_of(r["base"])
got = tmdb("movie", parsed)
words = eng.norm(got["name"]).split() if got else []
if not got or not eng.mentions(parsed.split(), words) \
# The provider's title must be a run of words the FILE itself states. The query
# is only what title_of could salvage from the name, and a short one ("russia")
# lets the top hit be a different work entirely: a 1963 Bond film is not a 1991
# documentary episode, and Special Edition I is not Special Edition III.
said = eng.norm(os.path.splitext(r["base"])[0]).split()
named = eng.norm(got["name"]).split() if got else []
if not got or not eng.mentions(named, said) \
or (year != "?" and got["year"] and got["year"] != year):
park(r, "long file with no episode evidence, and no movie provider names it")
continue
+32
View File
@@ -360,6 +360,38 @@ json.dump({"movie:some documentary":
[[ "$output" == "$PARK/"* ]]
}
@test "a movie answer the file does not itself name stays in the review pile" {
# TMDb's top hit for a truncated query is a NEAR miss, not an answer: "mobile
# suit gundam 00" comes back as Special Edition I while the file is Special
# Edition III, and a one-word query "russia" comes back as a 1963 Bond film. A
# file leaves the TV library only when it states the title the provider names.
printf '8000\t%s\t7200\thevc\t1920x1080\t23.976\t4000000\tyuv420p10le\taac:eng\t0\t0\tmatroska,webm\n' \
"$LIB/Longform/Some Documentary Special (2015).mkv" > "$PROBED"
python3 -c 'import json, sys
json.dump({"movie:some documentary":
{"name": "Some Documentary Special Part Two", "year": "2015", "id": 55}},
open(sys.argv[1], "w"))' "$CACHE"
plan
run dst "$LIB/Longform/Some Documentary Special (2015).mkv"
[[ "$output" == "$PARK/"* ]]
run awk -F '\t' -v m="$MOVIES/" '$1 == "move" && index($3, m) == 1 {print $3}' "$OUT"
[ -z "$output" ]
}
@test "a movie answer from another year is not that film" {
# The year the file states outranks a provider's eagerness: the 2015 special is
# not the 1999 one, however alike their names are.
printf '8000\t%s\t7200\thevc\t1920x1080\t23.976\t4000000\tyuv420p10le\taac:eng\t0\t0\tmatroska,webm\n' \
"$LIB/Longform/Some Documentary Special (2015).mkv" > "$PROBED"
python3 -c 'import json, sys
json.dump({"movie:some documentary":
{"name": "Some Documentary Special", "year": "1999", "id": 55}},
open(sys.argv[1], "w"))' "$CACHE"
plan
run dst "$LIB/Longform/Some Documentary Special (2015).mkv"
[[ "$output" == "$PARK/"* ]]
}
@test "the plan creates every folder before the move that needs it" {
plan
python3 - "$OUT" "$LIB" "$MOVIES" "$PARK" <<'PY'