From 38d48bae3730a1d977fbaf904858afa8b2dfe477 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Mon, 15 Jun 2026 09:29:09 -0700 Subject: [PATCH] video: OMDb Test button shows the real reason (not just 'HTTP 401') OMDb returns a JSON body even on 401, so surface its actual Error: 'Invalid API key!' (nudges the user to click OMDb's activation email) vs 'Request limit reached!' (free-tier daily quota). The worker's pause note uses the same message. --- core/video/enrichment/clients.py | 22 ++++++++++++++++++---- tests/test_video_enrichment.py | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/core/video/enrichment/clients.py b/core/video/enrichment/clients.py index df740aac..d659f218 100644 --- a/core/video/enrichment/clients.py +++ b/core/video/enrichment/clients.py @@ -506,11 +506,20 @@ class OMDBClient: import requests try: r = requests.get(self.BASE, params={"apikey": self.api_key, "i": "tt0111161"}, timeout=12) - d = r.json() if r.status_code == 200 else {} + # OMDb returns a JSON body even on 401 — surface its actual Error so the + # user sees WHY ("Invalid API key!" = not activated/wrong key; + # "Request limit reached!" = free-tier daily quota, resets at midnight). + try: + d = r.json() or {} + except Exception: + d = {} if d.get("Response") == "True": return True, "OMDb connection OK" - if "invalid api key" in (d.get("Error") or "").lower(): - return False, "Invalid OMDb API key" + err = (d.get("Error") or "").strip() + if "invalid api key" in err.lower(): + return False, "Invalid OMDb API key — did you click the activation link OMDb emailed you?" + if err: + return False, "OMDb: " + err return False, "OMDb returned HTTP " + str(r.status_code) except Exception: logger.exception("OMDb test failed") @@ -524,7 +533,12 @@ class OMDBClient: # A bad/expired key is a 401 (sometimes a 200 with "Invalid API key!") — a # config problem that affects EVERY item, so flag it distinctly. if r.status_code == 401: - raise OMDbAuthError("OMDb rejected the API key (HTTP 401)") + err = "" + try: + err = ((r.json() or {}).get("Error") or "").strip() + except Exception: + pass + raise OMDbAuthError(err or "OMDb rejected the API key (HTTP 401)") r.raise_for_status() d = r.json() or {} if d.get("Response") != "True": diff --git a/tests/test_video_enrichment.py b/tests/test_video_enrichment.py index 87a0b5eb..ca048967 100644 --- a/tests/test_video_enrichment.py +++ b/tests/test_video_enrichment.py @@ -354,6 +354,22 @@ def test_omdb_worker_keeps_item_on_transient_error(db): assert w.stats["errors"] == 1 and w.paused is False # one blip doesn't pause +def test_omdb_test_surfaces_real_error(monkeypatch): + # The Test button should show OMDb's actual reason, not just "HTTP 401". + class _R: + status_code = 401 + def json(self): return {"Response": "False", "Error": "Request limit reached!"} + monkeypatch.setitem(sys.modules, "requests", types.SimpleNamespace(get=lambda u, **k: _R())) + ok, msg = OMDBClient("KEY").test() + assert ok is False and "Request limit reached!" in msg + + class _R2(_R): + def json(self): return {"Response": "False", "Error": "Invalid API key!"} + monkeypatch.setitem(sys.modules, "requests", types.SimpleNamespace(get=lambda u, **k: _R2())) + ok2, msg2 = OMDBClient("KEY").test() + assert ok2 is False and "activation" in msg2.lower() # nudges toward the email link + + def test_omdb_ratings_raises_on_invalid_key(monkeypatch): from core.video.enrichment.clients import OMDbAuthError