Poll MoveMovie command to completion instead of trusting DB fields
Mirrors the Sonarr fix — same MoveMovieService one-at-a-time drain architecture, never confirmed live on the Radarr side but the DB-instant/move-async split is identical, so the same batch-verification race applies.
This commit is contained in:
@@ -343,11 +343,14 @@ fi
|
||||
# like Castlevania/Legend of Korra legitimately live in the anime root without matching the
|
||||
# anime signal — or on JUNK (those need removal from Radarr, not a file move).
|
||||
#
|
||||
# One movie at a time, verified after each, matching the lesson learned doing this by hand
|
||||
# for Sonarr earlier: a rapid-fire batch of moves raced Radarr's own file-move worker and
|
||||
# left two series' files at an intermediate path while the API still reported success. A
|
||||
# short sleep plus a real re-fetch-and-check after every single move catches that here
|
||||
# before it can compound across dozens of movies.
|
||||
# One movie at a time, verified after each. moveFiles=true flips the DB (rootFolderPath/
|
||||
# hasFile) instantly, but the physical move is a separate async MoveMovie command Radarr's
|
||||
# own MoveMovieService drains one at a time internally — same architecture that raced on the
|
||||
# Sonarr side (episodeFileCount reported at the new path via API while the real files were
|
||||
# still sitting at the old one, MoveSeries command queued behind ~20 others). Never confirmed
|
||||
# live on the Radarr side, but the same DB-write-is-instant/move-is-async split applies, so
|
||||
# each move here polls its own MoveMovie command to "completed" before the DB-field check
|
||||
# runs, mirroring the Sonarr fix.
|
||||
if [[ "$MOVE_MODE" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━ $ICON_SYNC Move Mode — Forward Misplacements ━━━"
|
||||
@@ -451,6 +454,48 @@ if [[ "$MOVE_MODE" == true ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
# moveFiles=true flips rootFolderPath/hasFile in the DB instantly, but the actual
|
||||
# physical move is a separate async MoveMovie command that Radarr's MoveMovieService
|
||||
# drains one at a time internally — mirrors the confirmed Sonarr race (see header
|
||||
# comment above Move Mode). Poll the actual command to completion before trusting the
|
||||
# DB-field check below.
|
||||
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: $RADARR_API_KEY" \
|
||||
"${RADARR_URL}/api/v3/command" 2>/dev/null | \
|
||||
jq -r --argjson mid "$id" \
|
||||
'[.[] | select(.name == "MoveMovie" and .body.movieId == $mid)] | 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 MoveMovie command — stopping (review before re-running)"
|
||||
(( FAILED++ ))
|
||||
break
|
||||
fi
|
||||
|
||||
info " → $title: MoveMovie command $move_cmd_id queued, waiting for completion..."
|
||||
move_status="" move_polled=0
|
||||
while [[ "$move_polled" -lt "$RADARR_MOVE_POLL_TIMEOUT" ]]; do
|
||||
move_status=$(curl -sf --max-time 10 -H "X-Api-Key: $RADARR_API_KEY" \
|
||||
"${RADARR_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 — MoveMovie command $move_cmd_id ended as '${move_status:-timed out after ${RADARR_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.
|
||||
|
||||
@@ -1209,6 +1209,8 @@
|
||||
# protects against API returning partial data on a bad day
|
||||
RADARR_TRACKED_COUNT_FILE="$DATA_DIR/radarr_tracked.count"
|
||||
RADARR_IMPORT_SCAN_TIMEOUT=600 # seconds to wait for pre-flight import scan
|
||||
RADARR_MOVE_POLL_TIMEOUT=3600 # seconds to wait for a single async MoveMovie command to
|
||||
# reach "completed" — mirrors SONARR_MOVE_POLL_TIMEOUT
|
||||
RADARR_EXTENSIONS=("mkv" "mp4" "avi" "m4v" "wmv" "mov")
|
||||
RADARR_PROTECTED_PATTERNS=(
|
||||
# Subtitles
|
||||
|
||||
Reference in New Issue
Block a user