From 11c0c62a7eca2c0f3908558c4913211de0f5cc1d Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Fri, 26 Jun 2026 16:28:37 -0700 Subject: [PATCH] video enrichment: stop OMDb traceback spam from the detail/drawer path too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the earlier latch only covered _backfill_ratings. _fill_tmdb_ratings (hit by tmdb_full_detail → detail pages, the download drawer's meta endpoint, the airing automation's status lookups) still re-hit OMDb once per title and dumped a traceback each time once over quota. now it shares the same _omdb_blocked latch: checks it before calling, sets it + logs ONE quiet warning on OMDbAuthError. (the bulk OMDb worker already cools down + auto-resumes, so it was never the spammer.) tested. --- core/video/enrichment/engine.py | 8 ++++++- tests/test_video_refresh_airing_schedules.py | 25 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/core/video/enrichment/engine.py b/core/video/enrichment/engine.py index f0d97373..42ea5aac 100644 --- a/core/video/enrichment/engine.py +++ b/core/video/enrichment/engine.py @@ -567,13 +567,19 @@ class VideoEnrichmentEngine: def _fill_tmdb_ratings(self, d) -> None: imdb_id = d.get("imdb_id") ow = self.workers.get("omdb") - if not imdb_id or not ow or not getattr(ow.client, "enabled", False): + # share the same daily-limit latch as _backfill_ratings — once OMDb is over quota, + # every detail fetch would otherwise re-hit it and dump a traceback per title. + if (not imdb_id or not ow or not getattr(ow.client, "enabled", False) + or getattr(self, "_omdb_blocked", False)): return try: r = ow.client.ratings(imdb_id) or {} for k in ("imdb_rating", "rt_rating", "metacritic"): if r.get(k) is not None: d[k] = r[k] + except OMDbAuthError as e: + self._omdb_blocked = True + logger.warning("OMDb ratings paused for this run: %s", e) except Exception: logger.exception("tmdb_detail ratings failed for %s", imdb_id) diff --git a/tests/test_video_refresh_airing_schedules.py b/tests/test_video_refresh_airing_schedules.py index 6e20ab16..76d1bf13 100644 --- a/tests/test_video_refresh_airing_schedules.py +++ b/tests/test_video_refresh_airing_schedules.py @@ -151,6 +151,31 @@ def test_omdb_limit_latches_off_and_stops_hammering(): assert rc.n == 1 # only the first attempt ever reached OMDb +def test_tmdb_detail_ratings_share_the_same_latch(): + # the detail/drawer path (_fill_tmdb_ratings) must honour + set the SAME latch — it was + # the source of the per-title traceback spam on the download drawer / detail pages. + from core.video.enrichment.engine import VideoEnrichmentEngine + from core.video.enrichment.clients import OMDbAuthError + + class _RC: + enabled = True + + def __init__(self): + self.n = 0 + + def ratings(self, imdb): + self.n += 1 + raise OMDbAuthError("Request limit reached!") + + eng = VideoEnrichmentEngine.__new__(VideoEnrichmentEngine) + rc = _RC() + eng.workers = {"omdb": type("W", (), {"client": rc})()} + eng._fill_tmdb_ratings({"imdb_id": "tt1"}) # hits the limit → latches (no raise out) + assert getattr(eng, "_omdb_blocked", False) is True + eng._fill_tmdb_ratings({"imdb_id": "tt2"}) # short-circuits before calling OMDb + assert rc.n == 1 + + # ── wiring contract ─────────────────────────────────────────────────────────── def test_seeded_before_the_airing_automation(): import core.automation_engine as ae