"""Jellyfin API client.""" import os from datetime import date, datetime, timezone import requests JELLYFIN_HOST = os.environ["JELLYFIN_HOST"].rstrip("/") JELLYFIN_API_KEY = os.environ["JELLYFIN_API_KEY"] JELLYFIN_USER_ID = os.environ["JELLYFIN_USER_ID"] HEADERS = {"X-Emby-Token": JELLYFIN_API_KEY} def _get_one_piece_series_id(): """Return the Jellyfin item ID for One Piece.""" url = f"{JELLYFIN_HOST}/Users/{JELLYFIN_USER_ID}/Items" params = { "searchTerm": "One Piece", "IncludeItemTypes": "Series", "Recursive": "true", "Limit": 5, } resp = requests.get(url, headers=HEADERS, params=params, timeout=10) resp.raise_for_status() items = resp.json().get("Items", []) if not items: raise RuntimeError("One Piece series not found in Jellyfin") return items[0]["Id"] def get_last_watched_episode(up_to_date: date) -> dict: """Return the last watched One Piece episode up to (and including) up_to_date. Returns a dict with keys: season, episode, name, jellyfin_id, watched_at """ series_id = _get_one_piece_series_id() url = f"{JELLYFIN_HOST}/Users/{JELLYFIN_USER_ID}/Items" params = { "ParentId": series_id, "IncludeItemTypes": "Episode", "Recursive": "true", "Filters": "IsPlayed", "SortBy": "DatePlayed", "SortOrder": "Descending", "Fields": "UserData", "Limit": 200, } resp = requests.get(url, headers=HEADERS, params=params, timeout=15) resp.raise_for_status() items = resp.json().get("Items", []) cutoff = datetime(up_to_date.year, up_to_date.month, up_to_date.day, 23, 59, 59, tzinfo=timezone.utc) for item in items: played_str = item.get("UserData", {}).get("LastPlayedDate") if not played_str: continue played_dt = datetime.fromisoformat(played_str.replace("Z", "+00:00")) if played_dt <= cutoff: return { "season": item["ParentIndexNumber"], "episode": item["IndexNumber"], "name": item.get("Name", ""), "jellyfin_id": item["Id"], "watched_at": played_str, } raise RuntimeError(f"No watched episodes found up to {up_to_date}")