aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_tmdb_search_ladder.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-29 15:43:38 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-29 15:43:38 +0200
commit5d28d0c96cc8489645178b483835489779cb3887 (patch)
tree90248f1c3211ca37495a774d0bc140109c299abc /packages/meshbay-node/tests/test_tmdb_search_ladder.py
parent236e5e811355212945b89c4f7a99df5c837e97c7 (diff)
downloadmeshbay-5d28d0c96cc8489645178b483835489779cb3887.tar.gz
feat(node): V8–V11 — show-branch ladder, year-aware _best_match, wider sequel_variants
V8: the TV/show branch of _tmdb_search used the old "first candidate over 0.6 wins" shape. It now shares one _tmdb_ladder helper with the movie branch — score every candidate query, keep the best, fast-path a confident primary hit. A year lifted off the show's folder name (title_parse.year_in, e.g. "Some.Show.2022.S01") rescues a sub-0.6 hit that lands on the exact year. title_parse.clean_query de-dots a folder-derived title without naive_title's extension-stripping trap. V9: _best_match gains an optional `year`. When the top result is not a confident textual hit (ratio < 0.6) and a year was requested, a different result of that exact release year is preferred — TMDB already year-filtered the search, so this is a hard corroboration, not the fuzzy re-rank §3.3 warns against. A confident top hit is never overridden. search_movie/search_tv forward the year. V10: sequel_variants widened — trailing Roman→digit as well as digit→Roman, spelled-out indices (one..twelve / un..douze / ordinals), and a "Part N" / "Chapitre N" wrapper. Still empty for a trailing word that is not an index or a 4-digit year. V11: when the primary hit is already decent (>= 0.6) and there is nothing more specific to try (no alternative_title, no sequel variant — only a punctuation restatement left), the ladder returns without the extra requests. The clean-title common case is back to one call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BMLQjqFGCize2KtNBT79v
Diffstat (limited to 'packages/meshbay-node/tests/test_tmdb_search_ladder.py')
-rw-r--r--packages/meshbay-node/tests/test_tmdb_search_ladder.py58
1 files changed, 56 insertions, 2 deletions
diff --git a/packages/meshbay-node/tests/test_tmdb_search_ladder.py b/packages/meshbay-node/tests/test_tmdb_search_ladder.py
index 49b71bd..9865c77 100644
--- a/packages/meshbay-node/tests/test_tmdb_search_ladder.py
+++ b/packages/meshbay-node/tests/test_tmdb_search_ladder.py
@@ -61,8 +61,12 @@ class LadderTmdb:
return self._table[(title, None)]
return None, 0.0
- async def search_tv(self, title):
- self.calls.append(("tv", title))
+ async def search_tv(self, title, year=None):
+ self.calls.append((title, year))
+ if (title, year) in self._table:
+ return self._table[(title, year)]
+ if (title, None) in self._table:
+ return self._table[(title, None)]
return None, 0.0
@@ -80,6 +84,17 @@ async def _run(name: str, table: dict):
return result, ratio, client
+async def _run_show(display_title: str, name: str, table: dict):
+ entry = IndexEntry(
+ id="s", name=name, path="Show/S1", size=1, type="video", added_at=0,
+ display_title=display_title, season=1, episode=1,
+ )
+ client = LadderTmdb(table)
+ session = WebRTCPeerSession.__new__(WebRTCPeerSession)
+ result, ratio = await session._tmdb_search(client, entry, is_show=True)
+ return result, ratio, client
+
+
# ── a two-part film: the parts must resolve to different entries ────────────
async def test_second_volume_resolves_to_the_second_volume():
@@ -191,3 +206,42 @@ async def test_strong_direct_match_costs_a_single_request():
_, _, client = await _run("A.Quiet.Film.2010.mkv",
{("A Quiet Film", 2010): (STANDALONE, 1.0)})
assert client.calls == [("A Quiet Film", 2010)]
+
+
+# ── §10.1/V11: a decent primary hit with nothing more specific to try ──────
+
+async def test_decent_primary_with_no_stronger_candidate_costs_one_call():
+ result, _, client = await _run(
+ "A.Quiet.Film.2010.mkv",
+ {("A Quiet Film", 2010): ({"id": "77", "title": "A Quiet Movie",
+ "release_date": "2010-01-01"}, 0.70)},
+ )
+ assert result["id"] == "77"
+ assert client.calls == [("A Quiet Film", 2010)], (
+ "no alt title, no sequel index → the punctuation-restatement fallback "
+ "is not worth a second request once the primary hit is decent")
+
+
+# ── §10.1/V8: the show branch uses the same scored ladder ─────────────────
+
+async def test_show_scored_ladder_beats_a_weak_primary_hit():
+ result, _, _ = await _run_show(
+ "Some.Show.Name.S01", "s01e01.mkv",
+ {
+ ("Some.Show.Name.S01", None): ({"id": "10", "name": "Promo Special",
+ "first_air_date": "2019-01-01"}, 0.55),
+ ("Some Show Name S01", None): ({"id": "20", "name": "Some Show Name",
+ "first_air_date": "2017-01-01"}, 0.95),
+ },
+ )
+ assert result["id"] == "20"
+
+
+async def test_show_year_in_folder_name_rescues_a_weak_hit():
+ result, ratio, _ = await _run_show(
+ "Some.Show.2019.S01", "s01e01.mkv",
+ {("Some.Show.2019.S01", 2019): ({"id": "30", "name": "Some Show",
+ "first_air_date": "2019-05-01"}, 0.30)},
+ )
+ assert result["id"] == "30"
+ assert ratio >= 0.6