From 7b8fc1d99b8bae86945c406706d3cb5aa1fe575e Mon Sep 17 00:00:00 2001 From: Rephl3x Date: Fri, 23 Jan 2026 11:35:59 +1300 Subject: [PATCH] Fix cache titles via Jellyseerr media lookup --- backend/app/db.py | 22 ++++++++++--- backend/app/routers/requests.py | 56 ++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/backend/app/db.py b/backend/app/db.py index 5fe0560..c3f2ee3 100644 --- a/backend/app/db.py +++ b/backend/app/db.py @@ -458,7 +458,7 @@ def get_request_cache_by_id(request_id: int) -> Optional[Dict[str, Any]]: with _connect() as conn: row = conn.execute( """ - SELECT request_id, updated_at + SELECT request_id, updated_at, title FROM requests_cache WHERE request_id = ? """, @@ -468,7 +468,7 @@ def get_request_cache_by_id(request_id: int) -> Optional[Dict[str, Any]]: logger.debug("requests_cache miss: request_id=%s", request_id) return None logger.debug("requests_cache hit: request_id=%s updated_at=%s", row[0], row[1]) - return {"request_id": row[0], "updated_at": row[1]} + return {"request_id": row[0], "updated_at": row[1], "title": row[2]} def get_request_cache_payload(request_id: int) -> Optional[Dict[str, Any]]: @@ -545,7 +545,7 @@ def get_request_cache_overview(limit: int = 50) -> list[Dict[str, Any]]: with _connect() as conn: rows = conn.execute( """ - SELECT request_id, media_id, media_type, status, title, year, requested_by, created_at, updated_at + SELECT request_id, media_id, media_type, status, title, year, requested_by, created_at, updated_at, payload_json FROM requests_cache ORDER BY updated_at DESC, request_id DESC LIMIT ? @@ -554,13 +554,27 @@ def get_request_cache_overview(limit: int = 50) -> list[Dict[str, Any]]: ).fetchall() results: list[Dict[str, Any]] = [] for row in rows: + title = row[4] + if not title and row[9]: + try: + payload = json.loads(row[9]) + if isinstance(payload, dict): + media = payload.get("media") or {} + title = ( + (media.get("title") if isinstance(media, dict) else None) + or (media.get("name") if isinstance(media, dict) else None) + or payload.get("title") + or payload.get("name") + ) + except json.JSONDecodeError: + title = row[4] results.append( { "request_id": row[0], "media_id": row[1], "media_type": row[2], "status": row[3], - "title": row[4], + "title": title, "year": row[5], "requested_by": row[6], "created_at": row[7], diff --git a/backend/app/routers/requests.py b/backend/app/routers/requests.py index bd7370c..7c7926f 100644 --- a/backend/app/routers/requests.py +++ b/backend/app/routers/requests.py @@ -265,6 +265,16 @@ async def _hydrate_title_from_tmdb( return None, None +async def _hydrate_media_details(client: JellyseerrClient, media_id: Optional[int]) -> Optional[Dict[str, Any]]: + if not media_id: + return None + try: + details = await client.get_media(int(media_id)) + except httpx.HTTPStatusError: + return None + return details if isinstance(details, dict) else None + + async def _hydrate_artwork_from_tmdb( client: JellyseerrClient, media_type: Optional[str], tmdb_id: Optional[int] ) -> tuple[Optional[str], Optional[str]]: @@ -389,6 +399,28 @@ async def _sync_all_requests(client: JellyseerrClient) -> int: if isinstance(details, dict): payload = _parse_request_payload(details) item = details + if not payload.get("title") and payload.get("media_id"): + media_details = await _hydrate_media_details(client, payload.get("media_id")) + if isinstance(media_details, dict): + media_title = media_details.get("title") or media_details.get("name") + if media_title: + payload["title"] = media_title + if not payload.get("year") and media_details.get("year"): + payload["year"] = media_details.get("year") + if not payload.get("tmdb_id") and media_details.get("tmdbId"): + payload["tmdb_id"] = media_details.get("tmdbId") + if not payload.get("media_type") and media_details.get("mediaType"): + payload["media_type"] = media_details.get("mediaType") + if isinstance(item, dict): + existing_media = item.get("media") + if isinstance(existing_media, dict): + merged = dict(media_details) + for key, value in existing_media.items(): + if value is not None: + merged[key] = value + item["media"] = merged + else: + item["media"] = media_details poster_path, backdrop_path = _extract_artwork_paths(item) if cache_mode == "cache" and not (poster_path or backdrop_path): details = await _get_request_details(client, request_id) @@ -483,13 +515,35 @@ async def _sync_delta_requests(client: JellyseerrClient) -> int: if isinstance(request_id, int): cached = get_request_cache_by_id(request_id) incoming_updated = payload.get("updated_at") - if cached and incoming_updated and cached.get("updated_at") == incoming_updated: + if cached and incoming_updated and cached.get("updated_at") == incoming_updated and cached.get("title"): continue if not payload.get("title") or not payload.get("media_id"): details = await _get_request_details(client, request_id) if isinstance(details, dict): payload = _parse_request_payload(details) item = details + if not payload.get("title") and payload.get("media_id"): + media_details = await _hydrate_media_details(client, payload.get("media_id")) + if isinstance(media_details, dict): + media_title = media_details.get("title") or media_details.get("name") + if media_title: + payload["title"] = media_title + if not payload.get("year") and media_details.get("year"): + payload["year"] = media_details.get("year") + if not payload.get("tmdb_id") and media_details.get("tmdbId"): + payload["tmdb_id"] = media_details.get("tmdbId") + if not payload.get("media_type") and media_details.get("mediaType"): + payload["media_type"] = media_details.get("mediaType") + if isinstance(item, dict): + existing_media = item.get("media") + if isinstance(existing_media, dict): + merged = dict(media_details) + for key, value in existing_media.items(): + if value is not None: + merged[key] = value + item["media"] = merged + else: + item["media"] = media_details poster_path, backdrop_path = _extract_artwork_paths(item) if cache_mode == "cache" and not (poster_path or backdrop_path): details = await _get_request_details(client, request_id)