54 lines
1.8 KiB
Python
54 lines
1.8 KiB
Python
"""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
|