video enrichment: detail backfill is TMDB-only (fix TVDB 404 spam + double-processing)

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.
This commit is contained in:
BoulderBadgeDad 2026-06-20 12:38:03 -07:00
parent 57f254acaf
commit d6776f9f57
2 changed files with 19 additions and 2 deletions

View file

@ -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:

View file

@ -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).