"""OpenSubtitles.com REST API v1 client. Auth: Api-Key header (from opensubtitles.com/consumer/apps). Endpoints used: GET /api/v1/subtitles?tmdb_id=…&languages=… POST /api/v1/download {file_id} → returns temporary download link """ import requests from typing import List, Dict, Optional BASE = "https://api.opensubtitles.com/api/v1" UA = "Kino/1.0" def _headers(api_key: str) -> dict: return { "Api-Key": api_key, "Content-Type": "application/json", "Accept": "application/json", "User-Agent": UA, } def search(api_key: str, tmdb_id: Optional[int] = None, query: Optional[str] = None, languages: str = "en", episode_number: Optional[int] = None, season_number: Optional[int] = None) -> List[Dict]: params = {"languages": languages} if tmdb_id: params["tmdb_id"] = tmdb_id if query: params["query"] = query if season_number is not None: params["season_number"] = season_number if episode_number is not None: params["episode_number"] = episode_number r = requests.get(f"{BASE}/subtitles", params=params, headers=_headers(api_key), timeout=20) r.raise_for_status() out = [] for item in (r.json() or {}).get("data", [])[:30]: attrs = item.get("attributes") or {} files = attrs.get("files") or [] if not files: continue f = files[0] out.append({ "file_id": f.get("file_id"), "language": attrs.get("language", ""), "label": attrs.get("release", "") or f.get("file_name", ""), "release": attrs.get("release", ""), "fps": float(attrs.get("fps") or 0), "downloads": int(attrs.get("download_count") or 0), }) return out def get_download_link(api_key: str, file_id: int) -> str: r = requests.post(f"{BASE}/download", json={"file_id": int(file_id)}, headers=_headers(api_key), timeout=20) r.raise_for_status() return (r.json() or {}).get("link", "") def download_content(link: str) -> str: """Fetch subtitle file content as text.""" r = requests.get(link, timeout=30, headers={"User-Agent": UA}) r.raise_for_status() # OpenSubtitles serves .srt files (usually) return r.content.decode("utf-8", errors="ignore")