Fix sparse request metadata hydration

This commit is contained in:
2026-08-31 15:21:47 +12:00
parent 372f4a1bfc
commit 82d87d968e
3 changed files with 162 additions and 2 deletions
+61
View File
@@ -400,6 +400,43 @@ def _parse_request_payload(item: Dict[str, Any]) -> Dict[str, Any]:
}
def _merge_request_media_details(
request_payload: Dict[str, Any], details: Dict[str, Any]
) -> Dict[str, Any]:
"""Fill display metadata omitted by Seerr's request mutation/detail payloads."""
merged = dict(request_payload)
media = request_payload.get("media")
media = dict(media) if isinstance(media, dict) else {}
media_type = _normalize_media_type(
media.get("mediaType") or request_payload.get("mediaType") or request_payload.get("type")
)
title = details.get("title") or details.get("name")
if title and not (media.get("title") or media.get("name")):
if media_type == "tv":
media["name"] = title
else:
media["title"] = title
date_value = details.get("releaseDate") or details.get("firstAirDate")
if not media.get("year") and isinstance(date_value, str) and date_value[:4].isdigit():
media["year"] = int(date_value[:4])
for camel_key, snake_key in (
("posterPath", "poster_path"),
("backdropPath", "backdrop_path"),
):
if not (media.get(camel_key) or media.get(snake_key)):
value = details.get(camel_key) or details.get(snake_key)
if value:
media[camel_key] = value
if media_type and not media.get("mediaType"):
media["mediaType"] = media_type
merged["media"] = media
return merged
def _extract_artwork_paths(item: Dict[str, Any]) -> tuple[Optional[str], Optional[str]]:
media = item.get("media") or {}
poster_path = None
@@ -694,6 +731,15 @@ async def _get_request_details(client: JellyseerrClient, request_id: int) -> Opt
cache_key = f"request:{request_id}"
cached = _cache_get(cache_key)
if isinstance(cached, dict):
parsed_cached = _parse_request_payload(cached)
if parsed_cached.get("title"):
return cached
details = await _get_media_details(
client, parsed_cached.get("media_type"), parsed_cached.get("tmdb_id")
)
if isinstance(details, dict):
cached = _merge_request_media_details(cached, details)
_cache_set(cache_key, cached)
return cached
if _failure_cache_has(cache_key):
return None
@@ -703,6 +749,13 @@ async def _get_request_details(client: JellyseerrClient, request_id: int) -> Opt
_failure_cache_set(cache_key)
return None
if isinstance(fetched, dict):
parsed_fetched = _parse_request_payload(fetched)
if not parsed_fetched.get("title"):
details = await _get_media_details(
client, parsed_fetched.get("media_type"), parsed_fetched.get("tmdb_id")
)
if isinstance(details, dict):
fetched = _merge_request_media_details(fetched, details)
_cache_set(cache_key, fetched)
return fetched
return None
@@ -1757,6 +1810,13 @@ async def action_recheck(request_id: str, user: Dict[str, str] = Depends(get_cur
raise HTTPException(status_code=404, detail="Request not found in Seerr")
parsed = _parse_request_payload(fresh_request)
if not parsed.get("title"):
details = await _get_media_details(
seerr, parsed.get("media_type"), parsed.get("tmdb_id")
)
if isinstance(details, dict):
fresh_request = _merge_request_media_details(fresh_request, details)
parsed = _parse_request_payload(fresh_request)
if parsed.get("request_id") != int(request_id):
raise HTTPException(status_code=502, detail="Seerr returned an unexpected request record")
@@ -2305,6 +2365,7 @@ async def create_request(
if not isinstance(created, dict):
raise HTTPException(status_code=502, detail="Invalid response from Seerr request create")
created = _merge_request_media_details(created, details)
parsed = _parse_request_payload(created)
request_id = _quality_profile_id(parsed.get("request_id"))
status_code = parsed.get("status")
+18 -1
View File
@@ -59,6 +59,22 @@ def _pick_first(value: Any) -> Optional[Dict[str, Any]]:
return None
def _apply_arr_identity(snapshot: Snapshot, arr_item: Any) -> None:
"""Use the collector's authoritative identity when cached Seerr metadata is sparse."""
if not isinstance(arr_item, dict):
return
if snapshot.title in {None, "", "Unknown"}:
title = arr_item.get("title") or arr_item.get("seriesTitle")
if isinstance(title, str) and title.strip():
snapshot.title = title.strip()
if not snapshot.year:
year = arr_item.get("year")
try:
snapshot.year = int(year) if year else snapshot.year
except (TypeError, ValueError):
pass
def _normalize_media_title(value: Any) -> Optional[str]:
if not isinstance(value, str):
return None
@@ -821,7 +837,7 @@ async def build_snapshot(request_id: str) -> Snapshot:
poster_path = media.get("posterPath") or media.get("poster_path")
backdrop_path = media.get("backdropPath") or media.get("backdrop_path")
if snapshot.title in {None, "", "Unknown"} and allow_remote:
if snapshot.title in {None, "", "Unknown"} and jellyseerr.configured():
tmdb_id = jelly_request.get("media", {}).get("tmdbId")
if tmdb_id:
details = await _get_seerr_media_details(jellyseerr, snapshot.request_type, int(tmdb_id))
@@ -971,6 +987,7 @@ async def build_snapshot(request_id: str) -> Snapshot:
if arr_state is None:
arr_state = "unknown"
_apply_arr_identity(snapshot, arr_item)
timeline.append(TimelineHop(service="Sonarr/Radarr", status=arr_state, details=arr_details))
prowlarr_state = "unknown"