diff --git a/Arrs_Stack/radarr_classification_scan.sh b/Arrs_Stack/radarr_classification_scan.sh index 4d68f26..dfc656b 100755 --- a/Arrs_Stack/radarr_classification_scan.sh +++ b/Arrs_Stack/radarr_classification_scan.sh @@ -90,9 +90,17 @@ # RUNTIME MODES # ============================================================================================== # -# radarr_classification_scan.sh — normal run, prints report -# radarr_classification_scan.sh --log — verbose (per-movie TRACKED-style logging) -# radarr_classification_scan.sh --status — show config and exit +# radarr_classification_scan.sh — normal run, prints report +# radarr_classification_scan.sh --log — verbose (per-movie TRACKED-style logging) +# radarr_classification_scan.sh --status — show config and exit +# radarr_classification_scan.sh --remove-junk — delete + import-exclude bad-metadata entries +# radarr_classification_scan.sh --move — relocate forward misplacements (moves the +# file if one exists; for hasFile=false +# entries, just corrects rootFolderPath/path +# and triggers an immediate MoviesSearch) +# +# --remove-junk and --move can be combined in one run — junk is cleared first, then the +# move pass runs against the remaining (now junk-free) classification. # # ============================================================================================== @@ -100,11 +108,13 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../load_config.sh" -# --move is a script-local flag, not one parse_args recognizes — check the raw args before -# they get filtered into PARSED_ARGS. +# --move / --remove-junk are script-local flags, not ones parse_args recognizes — check the +# raw args before they get filtered into PARSED_ARGS. MOVE_MODE=false +REMOVE_JUNK_MODE=false for _arg in "$@"; do [[ "$_arg" == "--move" ]] && MOVE_MODE=true + [[ "$_arg" == "--remove-junk" ]] && REMOVE_JUNK_MODE=true done unset _arg @@ -145,6 +155,7 @@ if [[ "$SHOW_STATUS" == true ]]; then echo "$ICON_GEAR Kids studios: ${#RADARR_KIDS_STUDIOS[@]} curated" echo "$ICON_GEAR Junk min votes: ${RADARR_JUNK_MIN_VOTES:-15}" echo "$ICON_GEAR Move mode: $MOVE_MODE" + echo "$ICON_GEAR Remove-junk: $REMOVE_JUNK_MODE" echo "━━━━━━━━━━━━━━━━━━━━━━━" exit 0 fi @@ -246,6 +257,81 @@ echo "$ICON_PROTECTED Bad metadata (junk): $JUNK_COUNT (hasFile=false, no imdb echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" [[ "$ENABLE_LOGGING" != true ]] && echo " (run with --log for the per-title list)" +# ============================================================================================== +# ━━━ Remove-Junk Mode ━━━ +# ============================================================================================== +# Junk entries are bad/thin TMDb matches with hasFile=false — there's no release to blocklist +# (nothing was ever grabbed) and no file to delete, only a bad monitored record. The fix is +# removing the record and adding it to Radarr's import exclusion list (same mechanism +# radarr_tmdb_removed.sh already uses via RADARR_DROPPED_ADD_EXCLUSION) so the same bad TMDb +# match can't get re-added by a future Overseerr request or list sync. +if [[ "$REMOVE_JUNK_MODE" == true ]]; then + echo "" + echo "━━━ $ICON_TRASH Remove-Junk Mode ━━━" + + acquire_lock "wait" + trap "_release_all_locks" EXIT + + JUNK_TARGETS=$(echo "$RESULTS" | jq -c '[.[] | select(.is_junk)]') + JUNK_TARGET_COUNT=$(echo "$JUNK_TARGETS" | jq 'length') + + if [[ "$JUNK_TARGET_COUNT" -eq 0 ]]; then + info "No junk entries to remove" + else + warn "About to remove $JUNK_TARGET_COUNT junk entries — one at a time, verifying after each" + + JUNK_REMOVED=0 + JUNK_FAILED=0 + + while IFS= read -r item; do + id=$(echo "$item" | jq -r '.id') + title=$(echo "$item" | jq -r '.title') + + http_code=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \ + --max-time 15 \ + -H "X-Api-Key: $RADARR_API_KEY" \ + "${RADARR_URL}/api/v3/movie/${id}?deleteFiles=false&addImportExclusion=true" 2>/dev/null) + + if [[ "$http_code" != "200" && "$http_code" != "202" ]]; then + error " ✗ $title — API returned HTTP $http_code — stopping (review before re-running)" + (( JUNK_FAILED++ )) + break + fi + + sleep 1 + + # Verify — the movie should now be gone entirely (404). + verify_code=$(curl -sf -o /dev/null -w "%{http_code}" \ + -H "X-Api-Key: $RADARR_API_KEY" \ + "${RADARR_URL}/api/v3/movie/${id}" 2>/dev/null) + + if [[ "$verify_code" == "404" ]]; then + echo " $ICON_SUCCESS $title — removed and excluded" + (( JUNK_REMOVED++ )) + else + error " ✗ $title — verification failed (still returns HTTP $verify_code) — stopping" + (( JUNK_FAILED++ )) + break + fi + done < <(echo "$JUNK_TARGETS" | jq -c '.[]') + + if [[ "$JUNK_REMOVED" -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" + fi + + echo "" + echo "━━━━━ $ICON_SUMMARY REMOVE-JUNK SUMMARY ━━━━━" + echo "$ICON_SUCCESS Removed: $JUNK_REMOVED" + echo "$ICON_ERROR Failed: $JUNK_FAILED" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + fi + + _release_all_locks + trap - EXIT +fi + # ============================================================================================== # ━━━ Move Mode ━━━ # ==============================================================================================