diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-26 15:22:17 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-26 15:29:05 +0200 |
| commit | 4c45792c861c47d44ebdbbab4130e37fba5d6795 (patch) | |
| tree | 2bfeaa52840057964aa69f7e2cbd9906701df849 /packages/meshbay-node/tests/test_enrich.py | |
| parent | db7f81fd08742847f3ebea061c75530b7b31b934 (diff) | |
| download | meshbay-4c45792c861c47d44ebdbbab4130e37fba5d6795.tar.gz | |
fix(video): a Specials/Bonus folder's episodes were matched to TMDB as unrelated standalone movies
Found live: a "Specials" folder full of one-off-named bonus episodes had
every file appear as its own poster, matched against TMDB by its own
title, because guessit finds no season/episode grammar at all in a
filename with no SxxExx of its own — so the classification (episode vs
standalone movie), based solely on that, fell to the movie branch. Real,
unrelated films happened to share several of those one-off titles and
matched confidently, one per Special, cluttering the Videos view with
dozens of wrong posters instead of grouping under the show.
An ancestor folder saying this is part of a show — a numbered season, or
Specials/Bonus/Extras -> season 0 — is now trusted over the filename
having no SxxExx of its own. The show's name can't come from this file's
own guessit title (that's the bug) or from siblings in the same Specials
folder (every one of them has the same gap) — it's borrowed from the
show's ordinary season folders next door, which do carry it in the usual
ShowName.SxxExx shape (_title_from_show_siblings). A synthetic, stable
episode number (alphabetical rank among the folder's video files) stands
in for the real one nothing in a Specials folder provides.
Generic, not specific to a folder literally named "Specials": the same
fallback fires for any season-like ancestor folder whose files lack
per-file episode grammar, numbered seasons included (covered by a
dedicated test).
Also hardens _title_from_siblings to require the sibling's own episode
number too, not just a title — otherwise it would borrow one Special's
one-off title as if it were representative, on a folder like this one.
Existing already-indexed entries will need a rescan (restart the node) to
be re-enriched under this logic — nothing re-derives them on its own.
Diffstat (limited to 'packages/meshbay-node/tests/test_enrich.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_enrich.py | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/packages/meshbay-node/tests/test_enrich.py b/packages/meshbay-node/tests/test_enrich.py index 2205c7e..5497812 100644 --- a/packages/meshbay-node/tests/test_enrich.py +++ b/packages/meshbay-node/tests/test_enrich.py @@ -8,7 +8,10 @@ from pathlib import Path import pytest from meshbay_common.protocol import IndexEntry -from meshbay_node.indexer.enrich import Enricher, _season_from_ancestors, _title_from_siblings +from meshbay_node.indexer.enrich import ( + Enricher, _season_from_ancestors, _synthetic_episode_number, + _title_from_show_siblings, _title_from_siblings, +) from meshbay_node.media_cache import MediaCache _HAVE_FFMPEG = shutil.which("ffmpeg") and shutil.which("ffprobe") @@ -53,6 +56,67 @@ def test_title_from_siblings_none_when_no_titled_sibling(tmp_path): assert _title_from_siblings(folder / "S01E02.mkv") is None +def test_title_from_siblings_ignores_a_titled_sibling_with_no_episode_number(tmp_path): + # A Specials-shaped folder: every file parses as a "movie" (its own + # one-off title, guessit finds no season/episode grammar at all) — none + # of them is a representative sibling for the show's actual name. + folder = tmp_path / "Some Show" / "Specials" + folder.mkdir(parents=True) + (folder / "Bonus Feature One.mkv").touch() + + assert _title_from_siblings(folder / "Bonus Feature Two.mkv") is None + + +def test_title_from_show_siblings_borrows_from_a_sibling_season_folder(tmp_path): + show = tmp_path / "Some Show" + specials = show / "Specials" + specials.mkdir(parents=True) + (specials / "Bonus Feature One.mkv").touch() + season1 = show / "Season 1" + season1.mkdir() + (season1 / "Some Show.S01E01.mkv").touch() + + assert _title_from_show_siblings(specials / "Bonus Feature One.mkv") == "Some Show" + + +def test_title_from_show_siblings_none_when_no_show_folder_has_one(tmp_path): + show = tmp_path / "Some Show" + specials = show / "Specials" + specials.mkdir(parents=True) + (specials / "Bonus Feature One.mkv").touch() + (specials / "Bonus Feature Two.mkv").touch() + + assert _title_from_show_siblings(specials / "Bonus Feature One.mkv") is None + + +def test_title_from_show_siblings_not_specific_to_the_word_specials(tmp_path): + # Genericity check: the same gap (a season-like folder whose files carry + # no episode grammar of their own) can show up under a plain numbered + # season folder too, not only one named "Specials" — nothing in + # _title_from_show_siblings should assume otherwise. + show = tmp_path / "Some Show" + season2 = show / "Season 2" + season2.mkdir(parents=True) + (season2 / "One-Off Episode Title.mkv").touch() + season1 = show / "Season 1" + season1.mkdir() + (season1 / "Some Show.S01E01.mkv").touch() + + assert _title_from_show_siblings(season2 / "One-Off Episode Title.mkv") == "Some Show" + + +def test_synthetic_episode_number_is_stable_alphabetical_rank(tmp_path): + folder = tmp_path / "Specials" + folder.mkdir() + (folder / "Bonus Feature One.mkv").touch() + (folder / "Bonus Feature Three.mkv").touch() + (folder / "Bonus Feature Two.mkv").touch() + + assert _synthetic_episode_number(folder / "Bonus Feature One.mkv") == 1 + assert _synthetic_episode_number(folder / "Bonus Feature Three.mkv") == 2 + assert _synthetic_episode_number(folder / "Bonus Feature Two.mkv") == 3 + + # ── end-to-end against a real (tiny, synthetic) video file ────────────────── pytestmark_ffmpeg = pytest.mark.skipif(not _HAVE_FFMPEG, reason="ffmpeg/ffprobe not installed") @@ -201,3 +265,36 @@ async def test_enricher_handles_episode_with_season_from_folder(tmp_path, media_ assert fields["display_title"] == "Some Show" assert fields["season"] == 3 assert fields["episode"] == 7 + + +@pytestmark_ffmpeg +@pytest.mark.asyncio +async def test_enricher_handles_specials_folder_with_no_episode_grammar(tmp_path, media_cache): + # The real bug this guards: every file in a Specials folder is named + # after its own one-off joke, not the show — guessit finds no + # season/episode in any of them, so without the ancestor-folder check + # each one used to fall to the movie branch and get searched against + # TMDB as an unrelated standalone film (found live: a real show's + # Specials folder matched several of its bonus episodes to real, + # unrelated movies that happened to share their one-off titles). + show = tmp_path / "Some Show" + season1 = show / "Season 1" + season1.mkdir(parents=True) + (season1 / "Some Show.S01E01.mkv").touch() + specials = show / "Specials" + specials.mkdir() + (specials / "Bonus Feature One.mkv").touch() + clip = specials / "Bonus Feature Two.mkv" + _make_clip(clip) + entry = IndexEntry(id="fileid3", name=clip.name, path=str(clip.relative_to(tmp_path)), + size=clip.stat().st_size, type="video", added_at=0) + + enricher = Enricher(media_cache) + _, fields = await _run(enricher, entry, clip) + + # Not "Bonus Feature Two" — that's this one Special's own title, and + # matching it against TMDB by itself is exactly the bug. The show's + # name, from its ordinary season folder next door. + assert fields["display_title"] == "Some Show" + assert fields["season"] == 0 + assert fields["episode"] is not None |