summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests')
-rw-r--r--packages/meshbay-node/tests/test_enrich.py41
-rw-r--r--packages/meshbay-node/tests/test_media_meta_request.py61
-rw-r--r--packages/meshbay-node/tests/test_title_parse.py22
3 files changed, 123 insertions, 1 deletions
diff --git a/packages/meshbay-node/tests/test_enrich.py b/packages/meshbay-node/tests/test_enrich.py
index 4b957cd..a35c713 100644
--- a/packages/meshbay-node/tests/test_enrich.py
+++ b/packages/meshbay-node/tests/test_enrich.py
@@ -373,3 +373,44 @@ async def test_enricher_reads_a_three_digit_episode_number_correctly(tmp_path, m
assert fields["season"] == 6
assert fields["episode"] == 100
+
+
+@pytestmark_ffmpeg
+@pytest.mark.asyncio
+async def test_a_movie_with_a_mangled_quality_tag_is_not_shelved_as_a_series(
+ tmp_path, media_cache):
+ """
+ Found live 2026-08-29: a standalone film whose "1080p" tag was
+ truncated to "108" in the filename makes guessit invent S01E08, so
+ enrich (flat library, no season ancestor) filed it as a nonexistent
+ series. A real flat-dumped episode carries an explicit SxxExx / 1x08 /
+ "Episode N" marker; a movie has a "(2017)"-style year and none.
+ """
+ clip = tmp_path / "Some.Film.2017.MULTI.108.grp.mkv"
+ _make_clip(clip)
+ entry = IndexEntry(id="fid-trunc", name=clip.name, path=clip.name,
+ size=clip.stat().st_size, type="video", added_at=0)
+
+ enricher = Enricher(media_cache)
+ _, fields = await _run(enricher, entry, clip)
+
+ assert fields.get("season") is None and fields.get("episode") is None, (
+ "a movie with a mangled quality tag must not become a series")
+ assert fields["display_title"]
+
+
+@pytestmark_ffmpeg
+@pytest.mark.asyncio
+async def test_a_flat_episode_with_a_real_marker_stays_a_show_even_with_a_year(
+ tmp_path, media_cache):
+ """The guard must not misfire: a genuine flat-dumped episode that also
+ carries a year has an explicit SxxExx marker and stays a show."""
+ clip = tmp_path / "Some.Show.2022.S01E02.1080p.WEB.mkv"
+ _make_clip(clip)
+ entry = IndexEntry(id="fid-marker", name=clip.name, path=clip.name,
+ size=clip.stat().st_size, type="video", added_at=0)
+
+ enricher = Enricher(media_cache)
+ _, fields = await _run(enricher, entry, clip)
+
+ assert fields["season"] == 1 and fields["episode"] == 2
diff --git a/packages/meshbay-node/tests/test_media_meta_request.py b/packages/meshbay-node/tests/test_media_meta_request.py
index a752825..0a3df12 100644
--- a/packages/meshbay-node/tests/test_media_meta_request.py
+++ b/packages/meshbay-node/tests/test_media_meta_request.py
@@ -9,7 +9,7 @@ real risk here too, since a season folder routinely holds many episodes.
import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
-from meshbay_common.protocol import IndexEntry
+from meshbay_common.protocol import MNP, IndexEntry
from meshbay_node.indexer.group_index import GroupIndex
from meshbay_node.media_cache import MediaCache
from meshbay_node.transport.webrtc_server import WebRTCPeerSession
@@ -165,3 +165,62 @@ async def test_unknown_file_id_is_refused(media_cache):
await session._do_media_meta_request({"file_id": "nope"})
assert session.sent == [{"type": "error", "detail": "File not found"}]
+
+
+async def test_a_not_yet_enriched_entry_with_no_cache_is_answered_without_a_search(media_cache):
+ """
+ A video the indexer has seen but not enriched yet has no display_title
+ (enrich.py always sets one) and season/episode still None — which the
+ movie/show split reads as "movie" and hands its raw filename to TMDB's
+ movie search. During a slow initial scan with a browser on the Videos
+ tab that is a storm of `search/movie?query=<raw filename>` and bogus
+ cached matches (found live 2026-08-29). With nothing cached it must
+ answer confidence 0 and let the client refetch once the enriched fields
+ arrive — never guess a match from the raw filename.
+ """
+ index = GroupIndex(group_id="g" * 32, sk_node=Ed25519PrivateKey.generate())
+ entry = IndexEntry(id="id-raw", name="Show.S01E01.1080p.WEB.mkv",
+ path="shows/Show", size=1, type="video", added_at=0)
+ index.add_entry(entry)
+ client = FakeTmdbClient()
+ session = _session(index, media_cache, client)
+
+ await session._do_media_meta_request({"file_id": "id-raw"})
+
+ assert len(session.sent) == 1
+ resp = session.sent[0]
+ assert resp["type"] == MNP.MEDIA_META_RESP
+ assert resp["file_id"] == "id-raw"
+ assert resp["confidence"] == 0
+ assert "tmdb_id" not in resp
+ assert client.searched == [], "no TMDB search for a not-yet-enriched video"
+
+
+async def test_a_not_yet_enriched_entry_is_served_from_cache_without_a_search(media_cache):
+ """
+ The point the operator raised: a restart must not re-query TMDB for a
+ file already resolved. An un-enriched entry (season/episode not yet
+ populated) whose content hash already has a cached match is served
+ straight from that cache, honouring the cached *kind* rather than the
+ provisional "movie" the split would pick — so a show episode keeps its
+ real "tv" match instead of triggering a fresh movie search on its raw
+ filename.
+ """
+ index = GroupIndex(group_id="g" * 32, sk_node=Ed25519PrivateKey.generate())
+ entry = IndexEntry(id="id-known", name="Show.S02E05.1080p.WEB.mkv",
+ path="shows/Show", size=1, type="video", added_at=0)
+ index.add_entry(entry)
+ await media_cache.set_file_tmdb("id-known", "1396", "tv")
+ await media_cache.set_tmdb_meta("1396", "tv", {
+ "title": "The Cached Show", "original_title": "The Cached Show",
+ "first_air_date": "2008-01-20", "confidence": 1.0,
+ })
+ client = FakeTmdbClient()
+ session = _session(index, media_cache, client)
+
+ await session._do_media_meta_request({"file_id": "id-known"})
+
+ resp = session.sent[0]
+ assert resp["tmdb_id"] == "1396"
+ assert resp["title"] == "The Cached Show"
+ assert client.searched == [], "a cached match must not be re-searched on restart"
diff --git a/packages/meshbay-node/tests/test_title_parse.py b/packages/meshbay-node/tests/test_title_parse.py
index ac4d434..045635f 100644
--- a/packages/meshbay-node/tests/test_title_parse.py
+++ b/packages/meshbay-node/tests/test_title_parse.py
@@ -7,6 +7,7 @@ is a manual acceptance step (§11), not something this repo's corpus holds.
from meshbay_node.indexer.title_parse import (
ParsedName,
clean_query,
+ has_episode_marker,
leading_episode_number,
naive_title,
parse_episode_filename,
@@ -113,6 +114,27 @@ def test_clean_query_despaces_a_folder_name_without_eating_the_last_word():
assert naive_title("Some.Show.Name") != "Some Show Name" # the trap it avoids
+# ── §10.1/V14: telling a real episode marker from a mangled number ──────────
+
+def test_has_episode_marker_accepts_real_markers():
+ for name in ["Some.Show.S01E08.mkv", "some.show.s1.e8.mkv",
+ "Some Show 1x08.mkv", "Some Show 01x08.mkv",
+ "Some Show Episode 8.mkv", "Some Show ep08.mkv",
+ "Some Show ep.8.mkv", "Some Show Season 1.mkv",
+ "Une Serie Saison 3.mkv"]:
+ assert has_episode_marker(name), name
+
+
+def test_has_episode_marker_rejects_bare_numbers_and_ordinary_words():
+ # "1080p" truncated to "108", "1280" left in, a year, plain words that
+ # merely contain "ep" — none of these are episode markers.
+ for name in ["Some.Film.2017.MULTI.108.grp.mkv",
+ "Some.Flick.2013.1280.x264-grp.mkv",
+ "Some Movie 2017.mkv", "The Dark Knight.mkv",
+ "Sleep 8.mkv", "Deep 8 mm.mkv", "Ocean's 11.mkv"]:
+ assert not has_episode_marker(name), name
+
+
# ── bug 2026-08-29: guessit peels "Volume N" off the title ─────────────────
def test_movie_volume_number_is_folded_back_into_the_title():