init
This commit is contained in:
117
backend/app/tmdb.py
Normal file
117
backend/app/tmdb.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import httpx
|
||||
|
||||
from .config import (
|
||||
GENRE_HORROR,
|
||||
LANGUAGE,
|
||||
PROVIDER_NETFLIX,
|
||||
REGION,
|
||||
TMDB_BASE,
|
||||
TMDB_TOKEN,
|
||||
)
|
||||
|
||||
HEADERS = {"Authorization": f"Bearer {TMDB_TOKEN}", "accept": "application/json"}
|
||||
|
||||
DISCOVER_PARAMS = {
|
||||
"with_watch_providers": PROVIDER_NETFLIX,
|
||||
"watch_region": REGION,
|
||||
"with_genres": GENRE_HORROR,
|
||||
"with_watch_monetization_types": "flatrate",
|
||||
"language": LANGUAGE,
|
||||
"include_adult": "false",
|
||||
# JustWatch hängt unbekannte Titel gelegentlich an die Netflix-ID eines
|
||||
# gleichnamigen bekannten Films. Betroffen waren ausschließlich Einträge
|
||||
# mit ein oder zwei Stimmen — "LIENZO" bekam die ID von "Die Leinwand".
|
||||
"vote_count.gte": 3,
|
||||
}
|
||||
|
||||
DETAIL_PARAMS = {
|
||||
"language": LANGUAGE,
|
||||
"append_to_response": "images,videos,translations,watch/providers",
|
||||
"include_image_language": "de,en,null",
|
||||
"include_video_language": "de,en",
|
||||
}
|
||||
|
||||
|
||||
def client():
|
||||
return httpx.AsyncClient(base_url=TMDB_BASE, headers=HEADERS, timeout=30.0)
|
||||
|
||||
|
||||
async def discover_page(http, page):
|
||||
resp = await http.get("/discover/movie", params={**DISCOVER_PARAMS, "page": page})
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
|
||||
async def discover_all(http):
|
||||
"""Alle Netflix-DE-Horrorfilme durchpaginieren."""
|
||||
first = await discover_page(http, 1)
|
||||
movies = list(first["results"])
|
||||
for page in range(2, first["total_pages"] + 1):
|
||||
data = await discover_page(http, page)
|
||||
movies.extend(data["results"])
|
||||
return movies
|
||||
|
||||
|
||||
async def detail(http, movie_id):
|
||||
resp = await http.get(f"/movie/{movie_id}", params=DETAIL_PARAMS)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
|
||||
def pick_overview(data):
|
||||
"""Deutsche Synopsis, sonst englische aus den Translations."""
|
||||
if data.get("overview"):
|
||||
return data["overview"]
|
||||
for entry in data.get("translations", {}).get("translations", []):
|
||||
if entry.get("iso_639_1") == "en":
|
||||
text = entry.get("data", {}).get("overview")
|
||||
if text:
|
||||
return text
|
||||
return ""
|
||||
|
||||
|
||||
def pick_backdrops(data):
|
||||
"""Alle Szenenbilder. Duplikate wirft der Sync raus.
|
||||
|
||||
Ein gesetztes iso_639_1 heißt: auf dem Bild steht Text, meist der
|
||||
Filmtitel. Das sind Werbemotive, keine Szenen. Hat ein Film nur solche
|
||||
Bilder, nehmen wir sie trotzdem — sonst bliebe die Detailseite leer.
|
||||
"""
|
||||
backdrops = data.get("images", {}).get("backdrops", [])
|
||||
textfree = [b for b in backdrops if b.get("iso_639_1") is None]
|
||||
usable = textfree or backdrops
|
||||
ranked = sorted(
|
||||
usable,
|
||||
key=lambda b: (-(b.get("vote_average") or 0), -(b.get("width") or 0)),
|
||||
)
|
||||
return [b["file_path"] for b in ranked]
|
||||
|
||||
|
||||
def pick_watch_link(data):
|
||||
"""TMDBs Watch-Seite für Deutschland.
|
||||
|
||||
Sie führt zur echten Netflix-Titelseite. Eine Netflix-Suche nach dem
|
||||
TMDB-Titel geht ins Leere, sobald Netflix den Film anders nennt —
|
||||
"LIENZO" heißt dort "CANVAS".
|
||||
"""
|
||||
providers = data.get("watch/providers", {}).get("results", {})
|
||||
return (providers.get(REGION) or {}).get("link")
|
||||
|
||||
|
||||
def pick_trailer(data):
|
||||
"""Trailer bevorzugt, sonst Teaser. Sprache egal."""
|
||||
videos = [
|
||||
v for v in data.get("videos", {}).get("results", []) if v.get("site") == "YouTube"
|
||||
]
|
||||
if not videos:
|
||||
return None
|
||||
order = {"Trailer": 0, "Teaser": 1, "Clip": 2}
|
||||
|
||||
def rank(video):
|
||||
return (
|
||||
order.get(video.get("type"), 3),
|
||||
0 if video.get("iso_639_1") == "de" else 1,
|
||||
0 if video.get("official") else 1,
|
||||
)
|
||||
|
||||
return sorted(videos, key=rank)[0]["key"]
|
||||
Reference in New Issue
Block a user