aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_title_parse.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_title_parse.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_title_parse.py')
-rw-r--r--packages/meshbay-node/tests/test_title_parse.py44
1 files changed, 44 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_title_parse.py b/packages/meshbay-node/tests/test_title_parse.py
index 03fb588..ac4d434 100644
--- a/packages/meshbay-node/tests/test_title_parse.py
+++ b/packages/meshbay-node/tests/test_title_parse.py
@@ -6,12 +6,14 @@ is a manual acceptance step (§11), not something this repo's corpus holds.
from meshbay_node.indexer.title_parse import (
ParsedName,
+ clean_query,
leading_episode_number,
naive_title,
parse_episode_filename,
parse_movie_filename,
season_from_folder_name,
sequel_variants,
+ year_in,
)
@@ -69,6 +71,48 @@ def test_sequel_variants_empty_when_no_trailing_digit():
assert sequel_variants("Some Movie") == []
+# ── §10.1/V10: wider sequel-index handling ──────────────────────────────────
+
+def test_sequel_variants_roman_numeral_offers_the_digit_form():
+ v = sequel_variants("Old Frontier III")
+ assert "Old Frontier" in v
+ assert "Old Frontier 3" in v
+
+
+def test_sequel_variants_strips_a_part_keyword_wrapper():
+ v = sequel_variants("Some Saga Part 2")
+ assert "Some Saga" in v
+ assert "Some Saga II" in v
+
+
+def test_sequel_variants_reads_a_spelled_out_index():
+ v = sequel_variants("Story Chapter Three")
+ assert "Story" in v
+ assert "Story 3" in v and "Story III" in v
+
+
+def test_sequel_variants_ignores_a_trailing_word_that_is_not_an_index():
+ assert sequel_variants("The Dark Knight") == []
+ assert sequel_variants("In Bruges") == []
+
+
+def test_sequel_variants_ignores_a_four_digit_year_suffix():
+ assert sequel_variants("Blade Runner 2049") == []
+
+
+def test_year_in_lifts_a_year_from_a_show_folder_name():
+ assert year_in("Some.Show.2022.S01") == 2022
+ assert year_in("Some Show") is None
+ assert year_in("Episode 100 of 2010") == 2010
+
+
+def test_clean_query_despaces_a_folder_name_without_eating_the_last_word():
+ # naive_title would rsplit on the last dot and drop ".Name"
+ assert clean_query("Some.Show.Name") == "Some Show Name"
+ assert clean_query("Some.Show.Name.S01") == "Some Show Name S01"
+ assert naive_title("Some.Show.Name") != "Some Show Name" # the trap it avoids
+
+
# ── bug 2026-08-29: guessit peels "Volume N" off the title ─────────────────
def test_movie_volume_number_is_folded_back_into_the_title():