From eccd465f8954bfd49c3f7d8f02446bc2aa5c4338 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 26 Sep 2026 14:28:13 +0200 Subject: fix(node,client): query TMDB only once a language is chosen, in that language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TMDB fiches are fetched lazily, on browse, and the fetch used to run whatever the moment it was first triggered — routinely before the operator had opened settings and picked a language, so it queried in TMDB's English default. Then the fiche was cached by tmdb_id alone, with no note of language and a 30-day TTL, so switching to the intended language afterwards changed nothing: the English fiche was served until it expired. The operator's only recourse was to find and wipe the cache by hand (found live 2026-09-26: a whole library indexed in English although "Français" had been chosen). Two rules now, both there to make the first fetch the right language rather than English-then-corrected, and to stop the doubled requests that eventually get a node rate-limited: - No language configured, no query. media_meta_req/season_meta_req answer confidence 0 and make no TMDB call while tmdb_language is unset; the fetch waits for the operator's choice, so the first (and only) query is in it. English is now a first-class choice (en-US), not the default of skipping the setting. - Changing the language wipes the metadata cache (ops.set_tmdb_config), so the new language takes effect on an already-browsed library. The file->tmdb matches are language-independent and kept. The client refetches on the tmdb_config_ack that carries the new language, so the grid updates without a page reload. docs/MESHBAY_DESIGN.md §9.7 states both rules; tests cover the gate and the cache wipe, and two existing handler harnesses now declare a language. Co-Authored-By: Claude Opus 4.8 --- .../transport/webrtc/apps/video_meta.py | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'packages/meshbay-node/src/meshbay_node/transport/webrtc/apps') diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc/apps/video_meta.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc/apps/video_meta.py index 9222ad8..834bac4 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/webrtc/apps/video_meta.py +++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc/apps/video_meta.py @@ -169,6 +169,15 @@ class VideoMetaMixin: await media_cache.put_thumb(thumb_hash, synthetic_id, content) return thumb_hash + def _tmdb_language(self) -> str: + """ + The node-wide TMDB query language, or "" when the operator has not + chosen one yet. Read from the live daemon state (kept current by + tmdb_config_ack, same source the handshake ack reads), not the DB, so + it is a cheap in-memory lookup on the hot metadata path. + """ + return (self._ctx.get("daemon_state") or {}).get("tmdb_language") or "" + async def _do_media_meta_request(self, msg: dict) -> None: """ docs/MESHBAY_DESIGN.md §9.7: TMDB metadata for one file, resolved from @@ -250,6 +259,19 @@ class VideoMetaMixin: self._send({"type": MNP.MEDIA_META_RESP, "v": MNP_VERSION, "file_id": file_id, "confidence": 0}) return + if not self._tmdb_language(): + # No query language chosen yet: hold off entirely rather than + # search now. TMDB would answer in its English default, which + # is both the wrong language and a wasted call — the whole + # library fetched now would be thrown away and refetched the + # moment a language is set, doubling the request count against + # TMDB's rate limit. Waiting until the operator has chosen one + # is what makes the first (and only) fetch the chosen language + # (docs/MESHBAY_DESIGN.md §9.7). The client refetches on the + # tmdb_config_ack that carries the new language. + self._send({"type": MNP.MEDIA_META_RESP, "v": MNP_VERSION, + "file_id": file_id, "confidence": 0}) + return result, ratio = await self._tmdb_search(tmdb_client, entry, is_show) if result is None or ratio < 0.6: self._send({"type": MNP.MEDIA_META_RESP, "v": MNP_VERSION, @@ -315,6 +337,15 @@ class VideoMetaMixin: details = await media_cache.get_season_meta(tmdb_id, season) if details is None: + if not self._tmdb_language(): + # Same gate as media_meta_req above: no query language yet + # means no TMDB call (docs/MESHBAY_DESIGN.md §9.7). A show is + # only matched once a language is set, so this is normally + # unreachable, but a client holding a tmdb_id from an earlier + # session must not reopen an English fetch either. + self._send({"type": MNP.SEASON_META_RESP, "v": MNP_VERSION, + "tmdb_id": tmdb_id, "season": season, "confidence": 0}) + return fetched = await tmdb_client.tv_season(tmdb_id, season) if fetched is None: self._send({"type": MNP.SEASON_META_RESP, "v": MNP_VERSION, -- cgit v1.2.3