Files
rippy/docker/api/clients/omdb.py
T
Hitonabi 85f352b14f
Ampel / ampel (push) Failing after 36s
OMDb-Qualitaets-Gate: ohne Poster UND Plot = Nicht-Treffer, Kette laeuft weiter
Der Datenmuell-Eintrag ("Episode 110 - ...") war der EINZIGE s=-Treffer
fuer den vollen Titel und brach die Kandidaten-Kette bei 0.8 ab, bevor
"Evangelion" allein probiert wurde. N/A-Werte werden jetzt normalisiert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:13:03 +02:00

119 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""OMDb API Client — Fallback-Quelle für die Metadaten-Erkennung.
Kommt zum Zug, wenn TMDB nichts (Sicheres) findet. Kostenloser Key von
https://www.omdbapi.com/apikey.aspx (1000 Anfragen/Tag reichen fürs Heimlab).
"""
from typing import Dict, Optional
import requests
from cache import get, set as cache_set
from config import settings
OMDB_BASE_URL = "https://www.omdbapi.com/"
def parse_runtime_minutes(runtime: str) -> int:
"""OMDb liefert '142 min' als Text → Minuten als Zahl (pure Funktion, testbar)."""
if not runtime:
return 0
teil = runtime.strip().split(" ")[0]
return int(teil) if teil.isdigit() else 0
def parse_year(year: str) -> Optional[int]:
"""OMDb-Jahr kann '1999' oder '20052012' (Serie) sein → erstes Jahr."""
if not year:
return None
erste = year.strip()[:4]
return int(erste) if erste.isdigit() else None
class OMDbClient:
def __init__(self):
# DB-Einstellung (Settings-UI/Wizard) gewinnt gegen die Env-Variable
from db import get_settings
self.api_key = get_settings().get("omdbApiKey") or settings.omdb_api_key
self.session = requests.Session()
def lookup(self, title: str, year: Optional[int] = None) -> Optional[Dict]:
"""Sucht per Titel (t=), liefert normalisierte Metadaten oder None."""
if not self.api_key:
return None
cache_key = f"omdb:{title.lower()}:{year or ''}"
cached = get(cache_key)
if cached:
return cached
params = {"apikey": self.api_key, "t": title, "r": "json"}
if year:
params["y"] = str(year)
try:
response = self.session.get(OMDB_BASE_URL, params=params, timeout=10)
response.raise_for_status()
data = response.json()
# t= ist exakt — wenn leer, unscharfe Suche (s=) und den besten
# Treffer per imdbID nachladen
if data.get("Response") != "True":
suche = self.session.get(
OMDB_BASE_URL,
params={"apikey": self.api_key, "s": title, "r": "json"},
timeout=10,
).json()
treffer = (suche.get("Search") or []) if suche.get("Response") == "True" else []
if treffer:
# Bester Kandidat statt blind Treffer #1: echte Filme vor
# Episoden/Reviews, mit Poster vor ohne, kurzer Titel vor
# langem (Zusatz-Geschwafel wie "Episode 110 - …" verliert).
def rang(t: Dict):
return (
0 if t.get("Type") == "movie" else (1 if t.get("Type") == "series" else 2),
0 if t.get("Poster") not in (None, "", "N/A") else 1,
len(t.get("Title") or ""),
)
bester = sorted(treffer, key=rang)[0]
data = self.session.get(
OMDB_BASE_URL,
params={"apikey": self.api_key, "i": bester["imdbID"], "r": "json"},
timeout=10,
).json()
except Exception as e:
print(f"OMDb API Error: {e}")
return None
if data.get("Response") != "True":
return None
plot = data.get("Plot", "")
if plot == "N/A":
plot = ""
poster = data.get("Poster", "")
if poster == "N/A":
poster = ""
# Qualitäts-Gate: Einträge ohne Poster UND ohne Beschreibung sind
# fast immer Datenmüll (Review-Episoden etc.) — als Nicht-Treffer
# werten, damit die Kandidaten-Kette weiterprobiert.
if not poster and not plot:
return None
ergebnis = {
"type": "tv" if data.get("Type") == "series" else "movie",
"id": data.get("imdbID", ""),
"title": data.get("Title", title),
"year": parse_year(data.get("Year", "")),
"overview": plot,
"poster_path": poster,
"backdrop_path": "",
"runtime": parse_runtime_minutes(data.get("Runtime", "")),
"genres": [g.strip() for g in data.get("Genre", "").split(",") if g.strip() and g.strip() != "N/A"],
"source": "omdb",
}
cache_set(cache_key, ergebnis)
return ergebnis