From 438de766557f3141ef37f7f1a9bdeb1be78e97c3 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Mon, 22 Jun 2026 13:54:19 -0400 Subject: [PATCH] Fix PROV_LOOKUP build: paginate by type to avoid Jellyfin mixed-query sort corruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single mixed Movie,Episode,Audio query caused Jellyfin to reorder items unpredictably, pushing most episodes past the page limit and leaving PROV_LOOKUP empty — every Emby→Jellyfin push silently failed with 'item not found'. Also removes the ExcludeLocationTypes=Virtual param which caused the same corruption in JF. --- Media/play_state_sync.sh | 48 +++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/Media/play_state_sync.sh b/Media/play_state_sync.sh index dd31a19..71e811b 100755 --- a/Media/play_state_sync.sh +++ b/Media/play_state_sync.sh @@ -276,24 +276,42 @@ done # per-item API call and is faster overall. declare -A PROV_LOOKUP # "si|tvdb.{id}" | "si|imdb.{id}" | "si|mb.{id}" → item_id +# Jellyfin's mixed-type query (Movie,Episode,Audio) changes sort order unpredictably, +# pushing items to positions far beyond the first pages. Query each type separately so +# each list sorts within its own type and items appear where expected. +_PROV_PAGE=2000 +IFS=',' read -ra _prov_types <<< "$SYNC_TYPES" + for _psi in $(seq 0 $(( _srv_count - 1 ))); do [[ -z "${SRV_URL[$_psi]}" ]] && continue log "Building provider ID map for ${SRV_NAME[$_psi]}..." - _raw_prov=$(_api_get "${SRV_URL[$_psi]}" "${SRV_KEY[$_psi]}" \ - "Items?Recursive=true&IncludeItemTypes=${SYNC_TYPES}&Fields=ProviderIds&ExcludeLocationTypes=Virtual" 2>/dev/null) - [[ -z "$_raw_prov" ]] && continue - while IFS=$'\t' read -r _pid _ptvdb _pimdb _ptmdb _pmbtrack; do - [[ "$_ptvdb" != "null" && -n "$_ptvdb" ]] && PROV_LOOKUP["${_psi}|tvdb.${_ptvdb}"]="$_pid" - [[ "$_pimdb" != "null" && -n "$_pimdb" ]] && PROV_LOOKUP["${_psi}|imdb.${_pimdb}"]="$_pid" - [[ "$_ptmdb" != "null" && -n "$_ptmdb" ]] && PROV_LOOKUP["${_psi}|tmdb.${_ptmdb}"]="$_pid" - [[ "$_pmbtrack" != "null" && -n "$_pmbtrack" ]] && PROV_LOOKUP["${_psi}|mb.${_pmbtrack}"]="$_pid" - done < <(echo "$_raw_prov" | jq -r '.Items[] | [ - .Id, - (.ProviderIds.Tvdb // "null"), - (.ProviderIds.Imdb // "null"), - (.ProviderIds.Tmdb // "null"), - (.ProviderIds.MusicBrainzTrackId // "null") - ] | @tsv' 2>/dev/null) + for _prov_type in "${_prov_types[@]}"; do + _prov_start=0 + _prov_total=-1 + while true; do + _page=$(curl -sf --max-time 60 \ + -H "X-Emby-Token: ${SRV_KEY[$_psi]}" \ + "${SRV_URL[$_psi]%/}/Items?Recursive=true&IncludeItemTypes=${_prov_type}&Fields=ProviderIds&Limit=${_PROV_PAGE}&StartIndex=${_prov_start}" 2>/dev/null) + [[ -z "$_page" ]] && break + [[ "$_prov_total" -lt 0 ]] && _prov_total=$(echo "$_page" | jq '.TotalRecordCount // 0' 2>/dev/null || echo 0) + _prov_count=$(echo "$_page" | jq '.Items | length' 2>/dev/null || echo 0) + [[ "$_prov_count" -eq 0 ]] && break + while IFS=$'\t' read -r _pid _ptvdb _pimdb _ptmdb _pmbtrack; do + [[ "$_ptvdb" != "null" && -n "$_ptvdb" ]] && PROV_LOOKUP["${_psi}|tvdb.${_ptvdb}"]="$_pid" + [[ "$_pimdb" != "null" && -n "$_pimdb" ]] && PROV_LOOKUP["${_psi}|imdb.${_pimdb}"]="$_pid" + [[ "$_ptmdb" != "null" && -n "$_ptmdb" ]] && PROV_LOOKUP["${_psi}|tmdb.${_ptmdb}"]="$_pid" + [[ "$_pmbtrack" != "null" && -n "$_pmbtrack" ]] && PROV_LOOKUP["${_psi}|mb.${_pmbtrack}"]="$_pid" + done < <(echo "$_page" | jq -r '.Items[] | [ + .Id, + (.ProviderIds.Tvdb // "null"), + (.ProviderIds.Imdb // "null"), + (.ProviderIds.Tmdb // "null"), + (.ProviderIds.MusicBrainzTrackId // "null") + ] | @tsv' 2>/dev/null) + _prov_start=$(( _prov_start + _prov_count )) + [[ "$_prov_total" -gt 0 && "$_prov_start" -ge "$_prov_total" ]] && break + done + done done # ── Step 3: Sync per matched user ────────────────────────────────────────────