85 lines
3.0 KiB
Python
85 lines
3.0 KiB
Python
"""Freie Filmsuche über TMDB.
|
|
|
|
Der Katalog deckt nur Netflix und den Kanon ab. Für den Filmabend braucht
|
|
es auch Titel, die nirgends davon stehen — die kommen live von TMDB und
|
|
landen nicht in der Datenbank.
|
|
"""
|
|
|
|
import httpx
|
|
|
|
from .config import LANGUAGE, TMDB_BASE, TMDB_TOKEN
|
|
from .tmdb import DETAIL_PARAMS, pick_backdrops, pick_overview, pick_trailer
|
|
|
|
HEADERS = {"Authorization": f"Bearer {TMDB_TOKEN}", "accept": "application/json"}
|
|
|
|
|
|
def client():
|
|
return httpx.AsyncClient(base_url=TMDB_BASE, headers=HEADERS, timeout=30.0)
|
|
|
|
|
|
def as_result(item):
|
|
release = item.get("release_date") or ""
|
|
return {
|
|
"id": item["id"],
|
|
"title": item.get("title") or item.get("original_title") or "Ohne Titel",
|
|
"original_title": item.get("original_title"),
|
|
"year": int(release[:4]) if release[:4].isdigit() else None,
|
|
"release_date": release or None,
|
|
"overview": item.get("overview") or "",
|
|
"poster_path": item.get("poster_path"),
|
|
"vote_average": item.get("vote_average"),
|
|
"vote_count": item.get("vote_count"),
|
|
}
|
|
|
|
|
|
async def search(query, limit=24):
|
|
async with client() as http:
|
|
resp = await http.get(
|
|
"/search/movie",
|
|
params={"query": query, "language": LANGUAGE, "include_adult": "false"},
|
|
)
|
|
resp.raise_for_status()
|
|
results = resp.json().get("results", [])
|
|
|
|
# Ohne Poster taugt eine Kachel nichts, und Namenlose sind fast immer
|
|
# Dubletten oder Making-ofs
|
|
usable = [r for r in results if r.get("poster_path")]
|
|
usable.sort(key=lambda r: -(r.get("popularity") or 0))
|
|
return [as_result(r) for r in usable[:limit]]
|
|
|
|
|
|
async def detail(movie_id):
|
|
"""Vollbild-Daten für einen Treffer, der nicht im Katalog steht."""
|
|
async with client() as http:
|
|
resp = await http.get(f"/movie/{movie_id}", params=DETAIL_PARAMS)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
|
|
release = data.get("release_date") or ""
|
|
collection = data.get("belongs_to_collection") or {}
|
|
return {
|
|
"id": data["id"],
|
|
"title": data.get("title") or data.get("original_title") or "Ohne Titel",
|
|
"original_title": data.get("original_title"),
|
|
"release_date": release or None,
|
|
"year": int(release[:4]) if release[:4].isdigit() else None,
|
|
"overview": pick_overview(data),
|
|
"teaser": "",
|
|
"poster_path": data.get("poster_path"),
|
|
"backdrop_path": data.get("backdrop_path"),
|
|
"vote_average": data.get("vote_average"),
|
|
"vote_count": data.get("vote_count"),
|
|
"runtime": data.get("runtime"),
|
|
"genres": [g["name"] for g in data.get("genres", [])],
|
|
"images": pick_backdrops(data)[:12],
|
|
"trailer_key": pick_trailer(data),
|
|
"imdb_id": data.get("imdb_id"),
|
|
"netflix_id": None,
|
|
"netflix_url": None,
|
|
"on_netflix": 0,
|
|
"canon_group": None,
|
|
"canon_pos": None,
|
|
"collection_id": collection.get("id"),
|
|
"collection_name": collection.get("name"),
|
|
}
|