diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-29 18:50:37 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-29 18:50:37 +0200 |
| commit | 0bcd8521eec8c6fde3ba5a6dc127866b3fb991e7 (patch) | |
| tree | 9e14bc8c1b6d8b18c9e2ec8fe1708f6ad3f18cb6 /packages/meshbay-node/src/meshbay_node | |
| parent | 8de80b7e19738b716d9973c0b4ba3b42085f7359 (diff) | |
| parent | 00880fd8b9d362ad391c784f1ee31bd62aa18d62 (diff) | |
| download | meshbay-0bcd8521eec8c6fde3ba5a6dc127866b3fb991e7.tar.gz | |
Merge branch 'fix/movie-misclassified-as-show'
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/indexer/enrich.py | 13 | ||||
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/indexer/title_parse.py | 20 |
2 files changed, 31 insertions, 2 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/indexer/enrich.py b/packages/meshbay-node/src/meshbay_node/indexer/enrich.py index 07f0be7..8b2eca5 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/enrich.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/enrich.py @@ -283,9 +283,18 @@ class Enricher: fields["display_title"] = show_folder.name fields["season"] = season fields["episode"] = episode - elif ep.episode is not None: + elif (ep.season is not None and ep.episode is not None + and (title_parse.has_episode_marker(entry.name) + or title_parse.year_in(entry.name) is None)): # No season-like ancestor at all (a flat library) but the - # filename itself carries season+episode (§3.4). + # filename itself carries season+episode (§3.4) — *and* it + # is a real marker, not guessit reading a bare number as + # SxxExx. A movie whose "1080p" tag was truncated to "108", + # or "1280" left in the name, otherwise parses to S01E08 / + # S12E80 and gets shelved as a nonexistent series + # (found live 2026-08-29). A genuine flat-dumped episode + # has an explicit SxxExx/1x08/"Episode N" marker; a movie + # has a "(2019)"-style year and no such marker. title = ep.display_title or await asyncio.to_thread( _title_from_siblings, file_path) fields["display_title"] = title or title_parse.naive_title(entry.name) diff --git a/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py b/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py index 018746b..def947f 100644 --- a/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py +++ b/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py @@ -288,6 +288,26 @@ def strip_track_prefix(text: str) -> str: return re.sub(r"^[\s-]+", "", text[m.end():]).strip() if m else text +# An *explicit* season/episode marker: SxxExx, 1x08, "Episode 8", "Ep 8", +# "Season 1"/"Saison 1". guessit will also invent a season+episode from a +# bare 3-4 digit run ("1080p" truncated to "108" -> S01E08; "1280" -> +# S12E80), which is how a plain movie ends up shelved as a series +# (§10.1/V14). The indexer uses this to tell a real flat-library episode +# from that hallucination. +_EPISODE_MARKER_RE = re.compile( + r"s\d{1,2}[\s._-]*e\d{1,3}" + r"|\b\d{1,2}x\d{1,3}\b" + r"|\bepisode[\s._-]*\d{1,3}\b" + r"|\bep[\s._-]*\d{1,3}\b" + r"|\b(?:season|saison)[\s._-]*\d{1,2}\b", + re.IGNORECASE, +) + + +def has_episode_marker(filename: str) -> bool: + return bool(_EPISODE_MARKER_RE.search(filename)) + + def parse_episode_filename(filename: str) -> ParsedName: """ Parse an episode filename. `display_title` may come back None (e.g. |