summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-26 15:36:23 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-26 15:36:23 +0200
commit84aa73e89cfe486c62317f761dcf87d7845eb4f7 (patch)
tree35b655fa27340ca6295f00daaa72a014c8f58c56
parent4c45792c861c47d44ebdbbab4130e37fba5d6795 (diff)
downloadmeshbay-84aa73e89cfe486c62317f761dcf87d7845eb4f7.tar.gz
fix(video): TMDB match cache never invalidated on movie<->show reclassification
The real reason a node restart alone didn't fix already-indexed entries after the previous commit's enrichment change: _do_media_meta_request checked media_cache's file->tmdb mapping (keyed by content hash) and trusted it unconditionally, before ever comparing it against the file's *current* movie/show classification. A file whose season/episode changed on a later scan — exactly what the Specials-folder fix does, for every file it reclassifies from "movie" to "tv" — kept answering with its stale, wrong-kind-of-match forever, since nothing about a reclassification touches this cache or its key. Now falls through to a fresh search whenever the cached media_type disagrees with what the entry resolves to right now, rather than trusting a mapping that predates the file's current classification.
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py16
-rw-r--r--packages/meshbay-node/tests/test_media_meta_request.py37
2 files changed, 51 insertions, 2 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
index 724527b..2b3e3ee 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -2914,8 +2914,20 @@ class WebRTCPeerSession:
meta = None
tmdb_id = None
if cached is not None:
- tmdb_id, media_type = cached
- meta = await media_cache.get_tmdb_meta(tmdb_id, media_type)
+ cached_tmdb_id, cached_media_type = cached
+ # Trustworthy only if it still agrees with what this file
+ # resolves to *now*. season/episode come from index-time
+ # enrichment (enrich.py), which can reclassify a file between
+ # movie and show on a later scan without this cache knowing —
+ # it is keyed by the file's content hash alone, which a
+ # reclassification never changes. Found live: an enrichment fix
+ # to a Specials-folder bug reclassified hundreds of files from
+ # "movie" to "tv", and every one kept answering with its
+ # stale movie-era match forever, because this was trusted
+ # before ever comparing media_type against the current one.
+ if cached_media_type == media_type:
+ tmdb_id = cached_tmdb_id
+ meta = await media_cache.get_tmdb_meta(tmdb_id, media_type)
if meta is None:
result, ratio = await self._tmdb_search(tmdb_client, entry, is_show)
diff --git a/packages/meshbay-node/tests/test_media_meta_request.py b/packages/meshbay-node/tests/test_media_meta_request.py
index a7355e8..a752825 100644
--- a/packages/meshbay-node/tests/test_media_meta_request.py
+++ b/packages/meshbay-node/tests/test_media_meta_request.py
@@ -112,6 +112,43 @@ async def test_two_episodes_in_the_same_season_folder_each_get_their_own_metadat
"two different shows sharing a season folder must not resolve to the same match")
+async def test_a_reclassified_file_ignores_its_stale_cached_match(media_cache):
+ """
+ A file's classification (movie vs show) comes from its IndexEntry's
+ season/episode — set by index-time enrichment, which can change its
+ mind on a later scan (a filename-parsing fix reclassifying a whole
+ folder from "movie" to "tv", say) without media_cache's file->tmdb
+ mapping knowing anything happened: that cache is keyed by the file's
+ content hash alone, unchanged by any such reclassification. Found
+ live: exactly this scenario left every affected file answering with
+ its stale, wrong-kind-of-match forever, since the cache was trusted
+ before ever comparing media_type against what the entry resolves to
+ now.
+ """
+ index = GroupIndex(group_id="g" * 32, sk_node=Ed25519PrivateKey.generate())
+ entry = IndexEntry(id="id-1", name="ep.mkv", path="shows/Show/Specials",
+ size=1, type="video", added_at=0,
+ display_title="Some Show", season=0, episode=1)
+ index.add_entry(entry)
+ # Pre-populate the cache exactly as it would be left over from before
+ # entry.season/episode existed — a movie search matched to some
+ # unrelated title, cached by this file's content hash.
+ await media_cache.set_file_tmdb("id-1", "stale-movie-id", "movie")
+ await media_cache.set_tmdb_meta("stale-movie-id", "movie", {
+ "title": "An Unrelated Movie", "original_title": "An Unrelated Movie",
+ "release_date": "1999-01-01", "confidence": 1.0,
+ })
+ client = FakeTmdbClient()
+ session = _session(index, media_cache, client)
+
+ await session._do_media_meta_request({"file_id": "id-1"})
+
+ resp = session.sent[0]
+ assert resp["title"] == "Some Show"
+ assert ("tv", "Some Show") in client.searched
+ assert resp["tmdb_id"] != "stale-movie-id"
+
+
async def test_missing_file_id_is_refused(media_cache):
index = GroupIndex(group_id="g" * 32, sk_node=Ed25519PrivateKey.generate())
session = _session(index, media_cache, FakeTmdbClient())