From d6776f9f575e1415791ec9ffaed871dc8d32d1ab Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Sat, 20 Jun 2026 12:38:03 -0700 Subject: [PATCH] video enrichment: detail backfill is TMDB-only (fix TVDB 404 spam + double-processing) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The details backfill queue is keyed on tmdb_id, but the gate (hasattr match) let the TVDB worker run it too — feeding TMDB ids to TVDB's /series/{id}/extended (→ 404 on every show) and double-processing each show (TMDB backfilled it, TVDB then 404'd but still logged 'Backfilled'). Gate on self.service=='tmdb' so only the TMDB worker runs it. Regression test: the TVDB worker no-ops (never calls its client, leaves the item pending). 95 enrichment tests pass. --- core/video/enrichment/worker.py | 8 ++++++-- tests/test_video_enrichment.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/core/video/enrichment/worker.py b/core/video/enrichment/worker.py index f44e513c..30c6151f 100644 --- a/core/video/enrichment/worker.py +++ b/core/video/enrichment/worker.py @@ -248,8 +248,12 @@ class VideoEnrichmentWorker: that's missing details-only fields (status, network, tagline, rating…) and gap-fill them. The matcher skips server-pre-matched items, so without this their `status` stays blank — and the watchlist's airing-default can't see - them. TMDB-only. Returns True if it did work (so the loop rate-limits).""" - if not hasattr(self.client, "match"): + them. Returns True if it did work (so the loop rate-limits). + + TMDB-ONLY: `status` (+ network/tagline/…) come from TMDB and the queue is + keyed on tmdb_id. Running it on the TVDB worker would feed a TMDB id to + TVDB (→ 404 on every show) and double-process the queue.""" + if self.service != "tmdb" or not hasattr(self.client, "match"): return False item = self.db.detail_backfill_next("show") or self.db.detail_backfill_next("movie") if not item: diff --git a/tests/test_video_enrichment.py b/tests/test_video_enrichment.py index bdc626b4..8d714477 100644 --- a/tests/test_video_enrichment.py +++ b/tests/test_video_enrichment.py @@ -196,6 +196,19 @@ def test_worker_detail_backfill_fills_status(db): assert db.detail_backfill_pending_count() == 0 # done → not re-picked +def test_detail_backfill_is_tmdb_only(db): + # The queue is keyed on tmdb_id, so only the TMDB worker may run it — the TVDB + # worker would feed a TMDB id to TVDB (404s) and double-process. It must no-op. + sid = db.upsert_show_tree("plex", {"server_id": "s1", "title": "S", "tmdb_id": 7, "seasons": []}) + db.enrichment_apply("tmdb", "show", sid, matched=True, external_id=7) + db.mark_episodes_synced(sid) + client = FakeClient({"id": 7, "metadata": {"status": "Returning Series"}}) + tvdb = VideoEnrichmentWorker(db, "tvdb", client) + assert tvdb._detail_backfill_one() is False + assert client.calls == [] # never called the client + assert db.detail_backfill_pending_count() == 1 # still pending (untouched) + + def test_detail_backfill_marks_done_even_when_status_absent(db): # If TMDB returns no status, we still mark it attempted so it isn't re-fetched # forever (bounded: one attempt per item).