diff --git a/Media/arrs_failed_stalled_recovery.sh b/Media/arrs_failed_stalled_recovery.sh index 8ca7445..e7b95c8 100644 --- a/Media/arrs_failed_stalled_recovery.sh +++ b/Media/arrs_failed_stalled_recovery.sh @@ -6,9 +6,13 @@ # across Sonarr, Radarr, and Lidarr. Blocklists the bad release and triggers # a new search — hands free recovery while you sleep. # -# Targets two problem types from the queue API: -# importFailed — downloaded successfully but arr couldn't import the file -# stalled — download stuck with no connections or no progress +# Targets four problem types from the queue API: +# importFailed — downloaded successfully but arr couldn't import the file +# importPending — downloaded, stuck waiting to import (won't self-resolve) +# error status — serious failure not covered by importFailed/importPending +# stalled — download stuck with no connections or no progress +# +# Never touches items with state "downloading" or "imported" — safe to run anytime. # # Action per problem item: # 1. Blocklist the release — prevents re-grabbing the same bad release @@ -168,17 +172,27 @@ process_arr() { total_records=$(echo "$queue_data" | jq '.totalRecords // 0' 2>/dev/null) info "Queue: $total_records total items" - # Filter for problem items — importFailed or stalled + # Filter for problem items — never touch downloading or imported + # importFailed = tried to import, actually failed + # importPending = downloaded, stuck waiting to import (won't self-resolve) + # error status = serious failure not covered by above states + # stalled = download stuck with no connections or progress local problem_items problem_items=$(echo "$queue_data" | jq -c ' .records // [] | .[] | select( - .trackedDownloadState == "importFailed" or - (.status == "warning" and ( - (.errorMessage // "" | ascii_downcase | contains("stalled")) or - (.statusMessages // [] | .[] | .messages // [] | .[] | ascii_downcase | contains("stalled")) - )) + .trackedDownloadState != "downloading" and + .trackedDownloadState != "imported" and + ( + .trackedDownloadState == "importFailed" or + .trackedDownloadState == "importPending" or + .trackedDownloadStatus == "error" or + (.status == "warning" and ( + (.errorMessage // "" | ascii_downcase | contains("stalled")) or + (.statusMessages // [] | .[] | .messages // [] | .[] | ascii_downcase | contains("stalled")) + )) + ) ) ' 2>/dev/null) @@ -201,14 +215,21 @@ process_arr() { title=$(echo "$item" | jq -r '.title // "Unknown"' 2>/dev/null) added=$(echo "$item" | jq -r '.added // empty' 2>/dev/null) - # Determine problem type - local tracked_state - tracked_state=$(echo "$item" | jq -r '.trackedDownloadState // ""' 2>/dev/null) - if [[ "$tracked_state" == "importFailed" ]]; then - problem_type="import failed" - else - problem_type="stalled" - fi + # Determine problem type for display + local tracked_state tracked_status + tracked_state=$(echo "$item" | jq -r '.trackedDownloadState // ""' 2>/dev/null) + tracked_status=$(echo "$item" | jq -r '.trackedDownloadStatus // ""' 2>/dev/null) + case "$tracked_state" in + importFailed) problem_type="import failed" ;; + importPending) problem_type="import pending/stuck" ;; + *) + if [[ "$tracked_status" == "error" ]]; then + problem_type="error" + else + problem_type="stalled" + fi + ;; + esac # Get media ID for search trigger (episode, movie, or album) case "$arr_type" in @@ -319,6 +340,8 @@ echo "$ICON_TIME Duration: $(format_duration $(( END - START )))" echo "$ICON_TRASH Actioned: $TOTAL_ACTIONED items blocklisted + searched" echo "$ICON_RUNNING Skipped: $TOTAL_SKIPPED items (too new or unreachable)" echo "" +echo " Problem types detected: importFailed | importPending | error | stalled" +echo "" for summary in "${ARR_SUMMARIES[@]}"; do echo " $ICON_SUMMARY $summary"