"""TMDB API client. Uses v3 API with api_key query param.""" import requests from typing import List, Dict, Optional BASE = "https://api.themoviedb.org/3" IMG = "https://image.tmdb.org/t/p" def _img(path: Optional[str], size: str = "w500") -> str: if not path: return "" return f"{IMG}/{size}{path}" def search_movies(api_key: str, query: str) -> List[Dict]: if not api_key or not query.strip(): return [] r = requests.get(f"{BASE}/search/movie", params={"api_key": api_key, "query": query}, timeout=15) r.raise_for_status() data = r.json().get("results", []) or [] out = [] for m in data[:20]: year = None if m.get("release_date"): try: year = int(m["release_date"].split("-")[0]) except Exception: pass out.append({ "tmdb_id": m["id"], "title": m.get("title") or m.get("name") or "", "year": year, "overview": m.get("overview") or "", "poster_url": _img(m.get("poster_path"), "w500"), "backdrop_url": _img(m.get("backdrop_path"), "original"), }) return out def get_movie(api_key: str, tmdb_id: int) -> Dict: """Return rich movie details with cast/crew.""" r = requests.get( f"{BASE}/movie/{tmdb_id}", params={"api_key": api_key, "append_to_response": "credits"}, timeout=15, ) r.raise_for_status() m = r.json() year = None if m.get("release_date"): try: year = int(m["release_date"].split("-")[0]) except Exception: pass credits = m.get("credits", {}) or {} cast = [c["name"] for c in (credits.get("cast") or [])[:8] if c.get("name")] crew = credits.get("crew") or [] director = next((c["name"] for c in crew if c.get("job") == "Director"), "") genres = [g["name"] for g in (m.get("genres") or []) if g.get("name")] # Map TMDB certifications to our rating scale (best effort, US fallback) rating = "NR" try: rel = requests.get( f"{BASE}/movie/{tmdb_id}/release_dates", params={"api_key": api_key}, timeout=10, ).json() for r_country in rel.get("results", []): if r_country.get("iso_3166_1") == "US": for d in r_country.get("release_dates", []) or []: cert = (d.get("certification") or "").strip() if cert: rating = cert break break except Exception: pass return { "tmdb_id": m["id"], "title": m.get("title") or "", "year": year, "description": m.get("overview") or "", "duration_minutes": m.get("runtime") or 0, "rating": rating or "NR", "genres": genres, "cast": cast, "director": director, "poster_url": _img(m.get("poster_path"), "w500"), "backdrop_url": _img(m.get("backdrop_path"), "original"), }