summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-26 17:13:32 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-26 17:13:32 +0200
commitca62b4123b854fc9ece258e14c84c895aaa275cd (patch)
tree458b8901cbd590f12463c8623b54ae91b5c42b88 /packages
parentdb7f81fd08742847f3ebea061c75530b7b31b934 (diff)
parent3d03692b6cc5e47c4bb49b86707a50cdaa5408f2 (diff)
downloadmeshbay-ca62b4123b854fc9ece258e14c84c895aaa275cd.tar.gz
Merge branch 'fix/tmdb-override-metadata'
An operator's "fix match" correction only ever recorded the file->tmdb_id mapping, never the metadata that id actually names — so the very next lookup found nothing cached under the new id and fell straight through to a fresh automatic search, silently reproducing the original wrong match. Invisible for two shows whose own title happened to be enough for that fallback search to land on the right answer anyway; surfaced on a movie whose own title search kept landing on the same wrong match regardless of the override. Verified live after this fix: the same "fix match" action, redone, made the correction stick this time.
Diffstat (limited to 'packages')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py18
-rw-r--r--packages/meshbay-node/tests/test_tmdb_override_policy.py58
2 files changed, 76 insertions, 0 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..f95e59e 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -3105,6 +3105,24 @@ class WebRTCPeerSession:
if entry is None or media_cache is None:
self._send({"type": "error", "detail": "File or media cache not available"})
return
+ # This only ever recorded the file->tmdb_id mapping, never the
+ # metadata tmdb_id names — _do_media_meta_request's cache check
+ # (entry, cache) both agree on media_type, so it trusted the
+ # mapping — but found nothing under this *new* id in tmdb_meta
+ # (nothing had ever fetched it), and silently fell through to a
+ # fresh search using the file's own title, exactly the one that
+ # produced the wrong match in the first place. Confirmed live: an
+ # override "stuck" for shows only because their own title happened
+ # to be enough for that fallback search to land on the right
+ # answer anyway, coincidentally — never because the override itself
+ # was actually being honored — and was invisible until a movie
+ # whose own title search kept landing on the same wrong result
+ # exposed it. Fetching and storing the real metadata up front is
+ # what makes the *override* the thing a later lookup finds.
+ tmdb_client = self._ctx.get("tmdb_client")
+ if tmdb_client is not None:
+ meta = await self._tmdb_build_meta(tmdb_client, tmdb_id, media_type, {})
+ await media_cache.set_tmdb_meta(tmdb_id, media_type, meta)
target_title = entry.display_title or entry.name
matched = [e for e in ctx["index"].entries
if e.type == "video" and (e.display_title or e.name) == target_title]
diff --git a/packages/meshbay-node/tests/test_tmdb_override_policy.py b/packages/meshbay-node/tests/test_tmdb_override_policy.py
index cd1cee1..c2f28e6 100644
--- a/packages/meshbay-node/tests/test_tmdb_override_policy.py
+++ b/packages/meshbay-node/tests/test_tmdb_override_policy.py
@@ -195,5 +195,63 @@ async def test_override_updates_every_entry_sharing_the_display_title(tmp_path):
await media_cache.close()
+class _FakeTmdbClient:
+ """Just enough for _tmdb_build_meta to run end to end — a fixed,
+ deterministic response, not a search stub (the override already has a
+ chosen tmdb_id; nothing here should need to search for anything)."""
+
+ async def movie_details(self, tmdb_id, language=None):
+ return {"title": "The Corrected Title", "overview": "A correct overview.",
+ "poster_path": "/poster.jpg", "genres": [{"name": "Drama"}]}
+
+ async def tv_details(self, tmdb_id, language=None):
+ return await self.movie_details(tmdb_id, language)
+
+ async def movie_credits(self, tmdb_id):
+ return {"cast": [], "crew": []}
+
+ async def tv_credits(self, tmdb_id):
+ return {"cast": [], "crew": []}
+
+
+async def test_override_stores_the_chosen_matchs_metadata_not_just_its_id(tmp_path):
+ """
+ The real bug this guards: only the file->tmdb_id mapping ever got
+ recorded, never the metadata the chosen id actually names.
+ _do_media_meta_request's cache check agrees the mapping is fresh (same
+ media_type) but finds nothing under that id in tmdb_meta — nothing had
+ ever fetched it — and falls through to a brand new search using the
+ file's own title, reproducing the very match the override was meant to
+ replace. Confirmed live: this stayed invisible for shows whose own
+ title happened to be enough for that fallback search to land on the
+ right answer anyway, and surfaced on a movie whose own title kept
+ landing on the same wrong match regardless of the override.
+ """
+ session = _session(tmp_path, "op", operator="op")
+ index = session._ctx["index"]
+ entry = _entry("shared", "movie.mkv", "Some Movie's Own Wrong Title")
+ index.add_entry(entry)
+
+ media_cache = MediaCache(db_path=tmp_path / "media_cache.db")
+ await media_cache.open()
+ try:
+ session._ctx["media_cache"] = media_cache
+ session._ctx["tmdb_client"] = _FakeTmdbClient()
+ session._verify_admin_sig = lambda transcript, sig: _true()
+ session._peer_registry = lambda: {}
+
+ await session._admin_exec_tmdb_override(
+ {"subject": f"file_id={entry.id},tmdb_id=999,media_type=movie"},
+ b"transcript", b"sig")
+
+ meta = await media_cache.get_tmdb_meta("999", "movie")
+ assert meta is not None, (
+ "the override must store the metadata its chosen id actually names, "
+ "not just the file->tmdb_id mapping")
+ assert meta["title"] == "The Corrected Title"
+ finally:
+ await media_cache.close()
+
+
async def _true():
return True