Relocate + trigger search for hasFile=false forward-misplacements

Instead of skipping monitored-but-undownloaded titles entirely,
correct their DB pointer (rootFolderPath/path) now so Radarr saves
under the right root whenever it grabs a release, and fire an
immediate MoviesSearch rather than waiting for the next scheduled
one. Junk entries stay excluded — nothing to search for there.
This commit is contained in:
Gmer4Lfe
2026-07-17 12:11:30 -04:00
parent 1777c56efa
commit 6f7bb8a6aa
+50 -17
View File
@@ -268,12 +268,14 @@ if [[ "$MOVE_MODE" == true ]]; then
build_arr_path_map "RADARR"
# hasFile==false entries have nothing to physically move (some monitored titles simply
# were never downloaded — confirmed live: "Biohazard 4: Incubate" had hasFile=false even
# in the cache from before this run started, not something this script broke). Moving
# Radarr's DB pointer for those is harmless but pointless — skip them here; they still
# show up correctly in the report above as a classification finding.
MOVE_TARGETS=$(echo "$RESULTS" | jq -c '[.[] | select((.forward_anime_miss or .forward_kids_miss) and (.is_junk | not) and .hasFile)]')
# hasFile==false entries (monitored but never downloaded) have nothing to physically
# move — confirmed live: "Biohazard 4: Incubate" had hasFile=false even in the cache from
# before any of this ran, not something this script broke. There's still real value in
# fixing them, though: correct the DB pointer now (so Radarr saves to the right root
# whenever it does find a release) and kick off an immediate search rather than waiting
# for the next scheduled one. Junk entries are still excluded entirely — nothing to
# search for there, they need removal instead.
MOVE_TARGETS=$(echo "$RESULTS" | jq -c '[.[] | select((.forward_anime_miss or .forward_kids_miss) and (.is_junk | not))]')
MOVE_COUNT=$(echo "$MOVE_TARGETS" | jq 'length')
if [[ "$MOVE_COUNT" -eq 0 ]]; then
@@ -281,15 +283,17 @@ if [[ "$MOVE_MODE" == true ]]; then
exit 0
fi
warn "About to move $MOVE_COUNT movies — one at a time, verifying after each"
warn "About to process $MOVE_COUNT movies — one at a time, verifying after each"
MOVED=0
RELOCATED_SEARCH=0
FAILED=0
while IFS= read -r item; do
id=$(echo "$item" | jq -r '.id')
title=$(echo "$item" | jq -r '.title')
is_anime_flag=$(echo "$item" | jq -r '.is_anime')
had_file=$(echo "$item" | jq -r '.hasFile')
if [[ "$is_anime_flag" == "true" ]]; then
target_root="$RADARR_ANIME_ROOT"
else
@@ -317,7 +321,14 @@ if [[ "$MOVE_MODE" == true ]]; then
fi
new_path="${target_root}/${folder_name}"
info "$title: $old_path$new_path"
if [[ "$had_file" == "true" ]]; then
info "$title: $old_path$new_path (moving file)"
move_qs="?moveFiles=true"
else
info "$title: $old_path$new_path (no file — relocating + search)"
move_qs=""
fi
updated_movie=$(echo "$full_movie" | jq --arg root "$target_root" --arg path "$new_path" \
'.rootFolderPath = $root | .path = $path')
@@ -327,7 +338,7 @@ if [[ "$MOVE_MODE" == true ]]; then
-H "X-Api-Key: $RADARR_API_KEY" \
-H "Content-Type: application/json" \
-d "$updated_movie" \
"${RADARR_URL}/api/v3/movie/${id}?moveFiles=true" 2>/dev/null)
"${RADARR_URL}/api/v3/movie/${id}${move_qs}" 2>/dev/null)
if [[ "$http_code" != "200" && "$http_code" != "202" ]]; then
error "$title — API returned HTTP $http_code — stopping (review before re-running)"
@@ -342,21 +353,42 @@ if [[ "$MOVE_MODE" == true ]]; then
verify_root=$(echo "$verify_movie" | jq -r '.rootFolderPath')
verify_hasfile=$(echo "$verify_movie" | jq -r '.hasFile')
if [[ "$verify_root" == "$target_root" ]] && [[ "$verify_hasfile" == "true" ]]; then
echo " $ICON_SUCCESS $title — moved and verified"
(( MOVED++ ))
else
error "$title — verification failed (root: $verify_root, hasFile: $verify_hasfile) — stopping"
if [[ "$verify_root" != "$target_root" ]]; then
error " $title — verification failed (root: $verify_root) — stopping"
(( FAILED++ ))
break
fi
if [[ "$had_file" == "true" ]]; then
if [[ "$verify_hasfile" == "true" ]]; then
echo " $ICON_SUCCESS $title — moved and verified"
(( MOVED++ ))
else
error "$title — verification failed (root updated but hasFile now false) — stopping"
(( FAILED++ ))
break
fi
else
search_code=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
--max-time 30 \
-H "X-Api-Key: $RADARR_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"name\":\"MoviesSearch\",\"movieIds\":[${id}]}" \
"${RADARR_URL}/api/v3/command" 2>/dev/null)
if [[ "$search_code" == "200" || "$search_code" == "201" ]]; then
echo " $ICON_SUCCESS $title — relocated, search triggered"
else
warn " $title — relocated but search trigger returned HTTP $search_code (will pick up on next scheduled search)"
fi
(( RELOCATED_SEARCH++ ))
fi
done < <(echo "$MOVE_TARGETS" | jq -c '.[]')
# arr_get_tracked_data() is cache-first — every write above changed rootFolderPath, so the
# shared cache is now stale until the next scheduled arr_cache_prefill run (up to 30min).
# Every other script reading this cache (cleanup, discovery, etc.) would see wrong data
# until then — refresh it now with one more live fetch rather than leave that window open.
if [[ "$MOVED" -gt 0 ]]; then
if [[ "$(( MOVED + RELOCATED_SEARCH ))" -gt 0 ]]; then
info "Refreshing shared tracked-data cache..."
fresh_movies=$(arr_api "$RADARR_URL" "$RADARR_API_KEY" "v3" "movie" "Radarr")
[[ -n "$fresh_movies" ]] && arr_cache_write "radarr" "$fresh_movies"
@@ -364,8 +396,9 @@ if [[ "$MOVE_MODE" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY MOVE SUMMARY ━━━━━"
echo "$ICON_SUCCESS Moved: $MOVED"
echo "$ICON_ERROR Failed: $FAILED"
echo "$ICON_SUCCESS Moved (file relocated): $MOVED"
echo "$ICON_SUCCESS Relocated + search triggered: $RELOCATED_SEARCH"
echo "$ICON_ERROR Failed: $FAILED"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
fi