1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
"""
Tests for indexer/title_parse.py — synthetic filenames only, one per
docs/mediacenter.md §3.3/§3.4 rule. The real ~1950-file library validation
is a manual acceptance step (§11), not something this repo's corpus holds.
"""
from meshbay_node.indexer.title_parse import (
ParsedName,
naive_title,
parse_episode_filename,
parse_movie_filename,
season_from_folder_name,
sequel_variants,
)
# ── §3.3 row: plain, well-formed movie filename ──────────────────────────────
def test_plain_movie_filename_parses_confidently():
r = parse_movie_filename("The.Great.Adventure.2015.1080p.BluRay.x264.mkv")
assert r.display_title == "The Great Adventure"
assert r.year == 2015
assert r.confidence is True
# ── §3.3 row 1: real title in alternative_title ──────────────────────────────
def test_franchise_numeric_code_exposes_alternative_title():
r = parse_movie_filename("Franchise.007.-.The.Real.Subtitle.1999.720p.mkv")
assert r.year == 1999
# guessit lands the franchise fragment in title and the real subtitle in
# alternative_title — both must be surfaced so a caller (tmdb.py) can
# try either, per the design doc's "search both fields" fix.
assert r.alt_title is not None
# ── §3.3 row 2: hyphenated title split before a parenthesized year ───────────
def test_naive_title_normalizes_hyphens_dots_and_underscores():
nt = naive_title("Hero-Name.(2002).DVDRip.XviD-GROUP.avi")
assert "-" not in nt
assert "." not in nt
assert "_" not in nt
assert "2002" not in nt # the year itself is stripped, not just its parens
assert "Hero" in nt and "Name" in nt
# ── §3.3 row 3: French edition vocabulary stuck to the title ─────────────────
def test_french_edition_phrases_are_stripped():
r = parse_movie_filename("Some.Movie.Version.Longue.2010.mkv")
assert "version" not in (r.display_title or "").lower()
assert "longue" not in (r.display_title or "").lower()
r2 = parse_movie_filename("Autre.Film.Remasterise.1998.mkv")
assert "remasteris" not in (r2.display_title or "").lower()
# ── §3.3 row 4: trailing sequel digit / Roman numeral ────────────────────────
def test_sequel_variants_strips_digit_and_offers_roman_numeral():
variants = sequel_variants("Some Sequel 2")
assert "Some Sequel" in variants
assert "Some Sequel II" in variants
def test_sequel_variants_empty_when_no_trailing_digit():
assert sequel_variants("Some Movie") == []
# ── §3.3 row 6: no usable title at all ───────────────────────────────────────
def test_low_confidence_when_no_title_or_year():
r = parse_movie_filename("abc123.mkv")
assert r.confidence is False
# the mandated fallback is always available regardless
assert r.naive_title == "abc123"
# ── §3.2/§3.4: episode filename with no show name at all ─────────────────────
def test_episode_only_filename_has_no_title_but_has_season_episode():
r = parse_episode_filename("S08E02.SUBFRENCH.720p.mkv")
assert r.display_title is None
assert r.season == 8
assert r.episode == 2
# confidence is False (no title yet) — the indexer must supply one from
# a representative sibling filename in the same folder, per §3.4.
assert r.confidence is False
def test_episode_filename_with_show_name_parses_confidently():
r = parse_episode_filename("Some.Show.Name.S02E05.720p.WEB.mkv")
assert r.display_title == "Some Show Name"
assert r.season == 2
assert r.episode == 5
assert r.confidence is True
# ── §3.4: season-like ancestor folders, including non-English vocabulary ────
def test_season_folder_english():
assert season_from_folder_name("Season 2") == 2
def test_season_folder_french_word():
assert season_from_folder_name("Saison 3") == 3
def test_season_folder_roman_numeral():
assert season_from_folder_name("Saison IV") == 4
def test_season_folder_book_word_roman_numeral():
# Some shows number their seasons by "book" (Roman numerals) rather
# than "season"/"saison" — real, observed vocabulary (§3.4).
assert season_from_folder_name("Livre I") == 1
assert season_from_folder_name("Livre VI") == 6
def test_specials_folder_maps_to_season_zero():
assert season_from_folder_name("Specials") == 0
assert season_from_folder_name("Bonus") == 0
assert season_from_folder_name("Extras") == 0
def test_non_season_folder_name_returns_none():
assert season_from_folder_name("Some Show Name") is None
def test_parsed_name_is_a_plain_dataclass():
# sanity: constructible with just the one required field, per the
# "None => caller must supply from elsewhere" contract.
p = ParsedName(display_title=None)
assert p.confidence is False
assert p.naive_title == ""
|