"""Die echte Netflix-Titel-ID aus TMDBs Watch-Seite lesen. Die API liefert nur den Anbieternamen. Die Titel-ID steht ausschließlich im HTML der Watch-Seite, URL-kodiert im Weiterleitungslink von JustWatch. Ohne sie bliebe nur eine Netflix-Titelsuche, und die geht ins Leere, sobald Netflix den Film anders nennt: "LIENZO" heißt dort "Die Leinwand". """ import asyncio import logging import re import httpx log = logging.getLogger("netflix") TITLE_ID = re.compile(r"netflix\.com%2Ftitle%2F(\d+)") UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/131.0" # Zu dichte Abrufe drosselt TMDB. Die Seite kommt dann mit Status 200, aber # ohne Anbieterblock. Filme ohne ID versucht der nächste Sync erneut. PAUSE = 1.5 def watch_url(movie_id): return f"https://www.themoviedb.org/movie/{movie_id}/watch?locale=DE" def title_url(netflix_id): return f"https://www.netflix.com/de/title/{netflix_id}" async def fetch_ids(movie_ids): """Seriell, mit Pause. Gibt {tmdb_id: netflix_id} für alles Gefundene.""" found = {} async with httpx.AsyncClient( headers={"User-Agent": UA}, timeout=60.0, follow_redirects=True ) as http: for index, movie_id in enumerate(movie_ids, 1): try: resp = await http.get(watch_url(movie_id)) # Mehrere IDs bedeuten mehrere Fassungen — Godzilla Minus One # liegt in Farbe und in Schwarzweiß. Die erste Kachel ist die # Hauptfassung. ids = TITLE_ID.findall(resp.text) if ids: found[movie_id] = ids[0] except httpx.HTTPError: log.warning("Watch-Seite nicht erreichbar: %s", movie_id) if index % 50 == 0: log.info(" %s/%s Watch-Seiten gelesen", index, len(movie_ids)) await asyncio.sleep(PAUSE) return found