aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_title_parse.py
blob: 1a277f2cd44eb0ef69a32dee9e3e009425341e96 (plain) (blame)
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
"""
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_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 == ""