import asyncio import httpx class TMDBClient: BASE_URL = "https://api.themoviedb.org/3" POSTER_BASE = "https://image.tmdb.org/t/p/w342" def __init__(self, api_key: str): self.api_key = api_key async def _fetch(self, client: httpx.AsyncClient, path: str, params: dict) -> dict | None: try: r = await client.get(f"{self.BASE_URL}{path}", params={"api_key": self.api_key, **params}, timeout=10) if r.status_code == 200: return r.json() except Exception: pass return None async def get_movie_details(self, client: httpx.AsyncClient, movie: dict) -> dict: tmdb_id = movie.get("tmdb_id") imdb_id = movie.get("imdb_id") # Resolve TMDB ID from IMDB if needed if not tmdb_id and imdb_id: data = await self._fetch(client, f"/find/{imdb_id}", {"external_source": "imdb_id"}) if data: results = data.get("movie_results", []) if results: tmdb_id = results[0]["id"] movie["tmdb_id"] = tmdb_id if not tmdb_id: movie.update({"title_fr": movie["title"], "overview": "", "poster": None}) return movie # Fetch French details details = await self._fetch(client, f"/movie/{tmdb_id}", {"language": "fr-FR"}) # Fallback to English overview if French is empty if details and not details.get("overview"): en = await self._fetch(client, f"/movie/{tmdb_id}", {"language": "en-US"}) if en: details["overview"] = en.get("overview", "") if details: poster = details.get("poster_path") movie.update({ "title_fr": details.get("title") or movie["title"], "overview": details.get("overview") or "", "poster": f"{self.POSTER_BASE}{poster}" if poster else None, }) else: movie.update({"title_fr": movie["title"], "overview": "", "poster": None}) return movie async def enrich_movies(self, movies: list) -> list: sem = asyncio.Semaphore(5) async with httpx.AsyncClient() as client: async def fetch_one(movie): async with sem: return await self.get_movie_details(client, movie) return list(await asyncio.gather(*[fetch_one(m) for m in movies]))