diff --git a/src/lib/server/omdb.ts b/src/lib/server/omdb.ts index 740870d..8079abb 100644 --- a/src/lib/server/omdb.ts +++ b/src/lib/server/omdb.ts @@ -4,6 +4,9 @@ import type { MediaDetails } from '#lib/media/types'; const cacheDuration = 24 * 60 * 60 * 1000; const imdbIdPattern = /tt\d+/; const missingValue = 'N/A'; +const omdbUrl = 'https://www.omdbapi.com/'; +const omdbDemoUrl = 'http://www.omdbapi.com/demo.aspx/'; +const rateLimitedStatus = 401; interface OmdbResponse { Response: 'True' | 'False'; @@ -35,13 +38,21 @@ function toMediaDetails(result: OmdbResponse): MediaDetails { }; } +async function requestOmdb(imdbId: string): Promise { + const params = new URLSearchParams({ apikey: OMDB_API_KEY ?? '', i: imdbId, plot: 'short' }); + const response = await fetch(`${omdbUrl}?${params}`); + if (response.status !== rateLimitedStatus) return response; + + const demoParams = new URLSearchParams({ i: imdbId, token: 'demo' }); + return fetch(`${omdbDemoUrl}?${demoParams}`); +} + async function fetchDetails(imdbId: string): Promise { const cached = detailsCache.get(imdbId); if (cached && Date.now() - cached.fetchedAt < cacheDuration) return cached.details; try { - const params = new URLSearchParams({ apikey: OMDB_API_KEY ?? '', i: imdbId, plot: 'short' }); - const response = await fetch(`https://www.omdbapi.com/?${params}`); + const response = await requestOmdb(imdbId); if (!response.ok) throw new Error(`OMDb returned ${response.status}`); const result: OmdbResponse = await response.json(); diff --git a/tools/content/imdb.py b/tools/content/imdb.py index cc99414..50c283e 100644 --- a/tools/content/imdb.py +++ b/tools/content/imdb.py @@ -1,6 +1,7 @@ import json import os import re +import urllib.error import urllib.parse import urllib.request @@ -8,6 +9,9 @@ from jsonfile import PROJECT_ROOT IMDB_ID_PATTERN = re.compile(r"tt\d{7,9}") ENV_FILE = PROJECT_ROOT / ".env" +OMDB_URL = "https://www.omdbapi.com/" +OMDB_DEMO_URL = "http://www.omdbapi.com/demo.aspx/" +RATE_LIMITED_STATUS = 401 def imdb_id(text: str) -> str | None: @@ -31,14 +35,22 @@ def omdb_api_key() -> str | None: return None +def request_json(url: str, parameters: dict) -> dict: + with urllib.request.urlopen(f"{url}?{urllib.parse.urlencode(parameters)}", timeout=10) as response: + return json.load(response) + + def lookup(identifier: str) -> dict | None: api_key = omdb_api_key() if not api_key: return None - query = urllib.parse.urlencode({"apikey": api_key, "i": identifier}) try: - with urllib.request.urlopen(f"https://www.omdbapi.com/?{query}", timeout=10) as response: - result = json.load(response) + try: + result = request_json(OMDB_URL, {"apikey": api_key, "i": identifier}) + except urllib.error.HTTPError as error: + if error.code != RATE_LIMITED_STATUS: + raise + result = request_json(OMDB_DEMO_URL, {"i": identifier, "token": "demo"}) except (OSError, ValueError): return None return result if result.get("Response") == "True" else None