Poll MoveSeries command to completion instead of trusting DB fields

Confirmed live: DB fields flip instantly on the moveFiles=true PUT but the
physical move drains async, one command at a time — a batch could report
every series moved while most were still sitting at the old path.
This commit is contained in:
Gmer4Lfe
2026-07-17 22:54:44 -04:00
parent 1e1129687f
commit 4f0cd1083a
2 changed files with 55 additions and 4 deletions
+51 -4
View File
@@ -76,10 +76,13 @@
# rootFolderPath/path corrected and an immediate SeriesSearch triggered rather than a file
# move (mirrors radarr_classification_scan.sh's handling of hasFile=false movies).
#
# One series at a time, verified after each — a rapid-fire batch of Sonarr moves is exactly
# what raced Sonarr's own file-move worker earlier this session doing this by hand (2 series
# reported "success" while their files sat at an intermediate path). A short sleep plus a
# real re-fetch-and-check after every single move catches that here before it can compound.
# One series at a time, verified after each. moveFiles=true flips the DB (rootFolderPath/
# episodeFileCount) instantly, but the physical move is a separate async MoveSeries command
# Sonarr drains one at a time internally — DB fields alone can report "moved" while the real
# files are still sitting at the old path behind other queued moves (confirmed live: "Full
# House" reported episodeFileCount:192 at the new path via API while the actual 75GB/192
# files hadn't moved yet). Each move polls its own MoveSeries command to "completed" before
# the DB-field check runs, so a batch can't compound the race the way a bare sleep-and-check did.
#
# ==============================================================================================
# DESIGN PRINCIPLES
@@ -355,6 +358,50 @@ if [[ "$MOVE_MODE" == true ]]; then
break
fi
# moveFiles=true flips rootFolderPath/episodeFileCount in the DB instantly, but the actual
# physical move is a separate async MoveSeries command that Sonarr drains one at a time
# internally — confirmed live: "Full House" showed episodeFileCount:192 at the new path via
# API while the real 75GB/192 files were still sitting at the old path, MoveSeries queued
# behind ~20 others. The DB-field check below cannot see that: poll the actual command to
# completion first, or a batch run can report every series "moved" while most are still
# mid-drain.
if [[ -n "$move_qs" ]]; then
move_cmd_id=""
for _ in 1 2 3 4 5; do
move_cmd_id=$(curl -sf --max-time 10 -H "X-Api-Key: $SONARR_API_KEY" \
"${SONARR_URL}/api/v3/command" 2>/dev/null | \
jq -r --argjson sid "$id" \
'[.[] | select(.name == "MoveSeries" and .body.seriesId == $sid)] | sort_by(.id) | last | .id // empty' \
2>/dev/null)
[[ -n "$move_cmd_id" ]] && break
sleep 1
done
if [[ -z "$move_cmd_id" ]]; then
error "$title — could not locate the MoveSeries command — stopping (review before re-running)"
(( FAILED++ ))
break
fi
info "$title: MoveSeries command $move_cmd_id queued, waiting for completion..."
move_status="" move_polled=0
while [[ "$move_polled" -lt "$SONARR_MOVE_POLL_TIMEOUT" ]]; do
move_status=$(curl -sf --max-time 10 -H "X-Api-Key: $SONARR_API_KEY" \
"${SONARR_URL}/api/v3/command/${move_cmd_id}" 2>/dev/null | \
jq -r '.status // empty' 2>/dev/null)
[[ "$move_status" == "completed" || "$move_status" == "failed" ]] && break
sleep 10
(( move_polled += 10 ))
[[ $(( move_polled % 60 )) -eq 0 ]] && log " still moving $title... (${move_polled}s elapsed)"
done
if [[ "$move_status" != "completed" ]]; then
error "$title — MoveSeries command $move_cmd_id ended as '${move_status:-timed out after ${SONARR_MOVE_POLL_TIMEOUT}s}' — stopping"
(( FAILED++ ))
break
fi
fi
sleep 3
# Never trust the PUT response alone — re-fetch and confirm the change actually landed.