Delete download orphans the arr cannot match instead of holding them forever — past the age gate an unnameable entry will never import, and clearing it lets a monitored title search for a copy the arr can parse
This commit is contained in:
@@ -33,10 +33,15 @@
|
|||||||
# movie file → delete (the arr already refused it as not-an-upgrade)
|
# movie file → delete (the arr already refused it as not-an-upgrade)
|
||||||
# IMPORTABLE — parse API matches it but the library is missing episodes / the movie
|
# IMPORTABLE — parse API matches it but the library is missing episodes / the movie
|
||||||
# → trigger DownloadedEpisodesScan/DownloadedMoviesScan on the folder and
|
# → trigger DownloadedEpisodesScan/DownloadedMoviesScan on the folder and
|
||||||
# leave it; whatever imports gets swept as REDUNDANT next week, whatever
|
# leave it; whatever imports gets swept as REDUNDANT next run, whatever
|
||||||
# the arr rejects (XEM-blocked, season-span files) stays HELD for a human
|
# the arr rejects (XEM-blocked, season-span files) stays HELD for a human
|
||||||
# HELD — parse API can't match it (series/movie not in the arr) → report only,
|
# UNMATCHED — parse API can't match it (series/movie not in the arr) → delete. Past the
|
||||||
# deciding whether to add the series or bin the files is a human call
|
# age gate an entry the arr cannot even name is not going to import: if the
|
||||||
|
# title is in the library and monitored, clearing it lets the arr search a
|
||||||
|
# copy it can actually parse; if it is not in the library, nothing is
|
||||||
|
# tracking it and it is dead weight either way. Guarded — see safeguard 6
|
||||||
|
# HELD — IMPORTABLE entries the arr keeps refusing (XEM-blocked, season-span
|
||||||
|
# files), and everything skipped by a guard → report only, human call
|
||||||
#
|
#
|
||||||
# The queue fetch is a hard gate: if it fails, the whole arr is skipped — with no queue
|
# The queue fetch is a hard gate: if it fails, the whole arr is skipped — with no queue
|
||||||
# there is no way to tell tracked from orphaned, and guessing means deleting active imports.
|
# there is no way to tell tracked from orphaned, and guessing means deleting active imports.
|
||||||
@@ -49,8 +54,14 @@
|
|||||||
# 2. Download dir must exist and live under /mnt/ — refuses to walk anything else
|
# 2. Download dir must exist and live under /mnt/ — refuses to walk anything else
|
||||||
# 3. Queue fetch must succeed (see above)
|
# 3. Queue fetch must succeed (see above)
|
||||||
# 4. Age gate — nothing under DOWNLOAD_ORPHAN_AGE days is touched
|
# 4. Age gate — nothing under DOWNLOAD_ORPHAN_AGE days is touched
|
||||||
# 5. Deletion only for JUNK and parse-verified REDUNDANT — never for HELD/IMPORTABLE
|
# 5. Deletion only for JUNK, parse-verified REDUNDANT, and UNMATCHED — never for
|
||||||
# 6. Run total over DOWNLOAD_ORPHAN_MAX_DELETE_GB aborts the delete pass and notifies —
|
# IMPORTABLE or anything a guard has held
|
||||||
|
# 6. UNMATCHED deletes require the arr to report a non-empty library. An empty or
|
||||||
|
# restoring database answers every parse with "no match", which would condemn the
|
||||||
|
# entire download dir; the library is queried directly rather than inferred from the
|
||||||
|
# run's own matches, since a small batch that is legitimately all-unmatched is normal
|
||||||
|
# once daily runs have caught up and would otherwise read as a broken database
|
||||||
|
# 7. Run total over DOWNLOAD_ORPHAN_MAX_DELETE_GB aborts the delete pass and notifies —
|
||||||
# a queue fetch that returned partial data would classify live downloads as orphans,
|
# a queue fetch that returned partial data would classify live downloads as orphans,
|
||||||
# and an abnormally large delete total is the visible symptom of exactly that
|
# and an abnormally large delete total is the visible symptom of exactly that
|
||||||
# (--i-know-what-im-doing overrides, e.g. for a first run against a known backlog)
|
# (--i-know-what-im-doing overrides, e.g. for a first run against a known backlog)
|
||||||
@@ -157,10 +168,12 @@ for arr in sonarr radarr; do
|
|||||||
queue_endpoint="queue?pageSize=1000&includeUnknownSeriesItems=true"
|
queue_endpoint="queue?pageSize=1000&includeUnknownSeriesItems=true"
|
||||||
exts_var="SONARR_EXTENSIONS"
|
exts_var="SONARR_EXTENSIONS"
|
||||||
scan_command="DownloadedEpisodesScan"
|
scan_command="DownloadedEpisodesScan"
|
||||||
|
library_endpoint="series"
|
||||||
else
|
else
|
||||||
queue_endpoint="queue?pageSize=1000&includeUnknownMovieItems=true"
|
queue_endpoint="queue?pageSize=1000&includeUnknownMovieItems=true"
|
||||||
exts_var="RADARR_EXTENSIONS"
|
exts_var="RADARR_EXTENSIONS"
|
||||||
scan_command="DownloadedMoviesScan"
|
scan_command="DownloadedMoviesScan"
|
||||||
|
library_endpoint="movie"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
QUEUE_JSON=$(arr_api "$arr_url" "$arr_key" "v3" "$queue_endpoint" "${arr^}") || {
|
QUEUE_JSON=$(arr_api "$arr_url" "$arr_key" "v3" "$queue_endpoint" "${arr^}") || {
|
||||||
@@ -179,6 +192,8 @@ for arr in sonarr radarr; do
|
|||||||
DELETE_SIZES=()
|
DELETE_SIZES=()
|
||||||
DELETE_LABELS=()
|
DELETE_LABELS=()
|
||||||
SCAN_PATHS=()
|
SCAN_PATHS=()
|
||||||
|
UNMATCHED_PATHS=()
|
||||||
|
UNMATCHED_SIZES=()
|
||||||
arr_tracked=0; arr_recent=0; arr_held=0; arr_delete_mb=0
|
arr_tracked=0; arr_recent=0; arr_held=0; arr_delete_mb=0
|
||||||
|
|
||||||
while IFS= read -r entry; do
|
while IFS= read -r entry; do
|
||||||
@@ -231,8 +246,8 @@ for arr in sonarr radarr; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$matched" != true ]]; then
|
if [[ "$matched" != true ]]; then
|
||||||
echo " $ICON_WARN HELD (no match in ${arr^}): $base (${size_mb}M)"
|
UNMATCHED_PATHS+=("$entry")
|
||||||
arr_held=$((arr_held + 1))
|
UNMATCHED_SIZES+=("$size_mb")
|
||||||
elif (( missing == 0 )); then
|
elif (( missing == 0 )); then
|
||||||
DELETE_PATHS+=("$entry")
|
DELETE_PATHS+=("$entry")
|
||||||
DELETE_SIZES+=("$size_mb")
|
DELETE_SIZES+=("$size_mb")
|
||||||
@@ -244,6 +259,27 @@ for arr in sonarr radarr; do
|
|||||||
fi
|
fi
|
||||||
done < <(find "$dl_dir" -mindepth 1 -maxdepth 1 2>/dev/null)
|
done < <(find "$dl_dir" -mindepth 1 -maxdepth 1 2>/dev/null)
|
||||||
|
|
||||||
|
# An arr with an empty or still-restoring database answers every parse with "no match",
|
||||||
|
# which would turn the whole download dir into UNMATCHED and delete it. Confirm the
|
||||||
|
# library actually holds titles before trusting a no-match to mean what it says. This
|
||||||
|
# has to be asked of the arr directly — inferring health from the run's own matches
|
||||||
|
# fails on a small batch that is legitimately all-unmatched, which is the normal case
|
||||||
|
# once daily runs have caught up.
|
||||||
|
if (( ${#UNMATCHED_PATHS[@]} > 0 )); then
|
||||||
|
library_count=$(arr_api "$arr_url" "$arr_key" "v3" "$library_endpoint" "${arr^}" | jq 'length' 2>/dev/null)
|
||||||
|
if [[ ! "$library_count" =~ ^[0-9]+$ ]] || (( library_count == 0 )); then
|
||||||
|
warn " ${arr^}: library reports ${library_count:-no} titles — cannot trust 'no match', holding ${#UNMATCHED_PATHS[@]} unmatched"
|
||||||
|
arr_held=$((arr_held + ${#UNMATCHED_PATHS[@]}))
|
||||||
|
else
|
||||||
|
for i in "${!UNMATCHED_PATHS[@]}"; do
|
||||||
|
DELETE_PATHS+=("${UNMATCHED_PATHS[$i]}")
|
||||||
|
DELETE_SIZES+=("${UNMATCHED_SIZES[$i]}")
|
||||||
|
DELETE_LABELS+=("UNMATCHED")
|
||||||
|
arr_delete_mb=$((arr_delete_mb + UNMATCHED_SIZES[i]))
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if (( arr_delete_mb / 1024 > DOWNLOAD_ORPHAN_MAX_DELETE_GB )) && [[ "$I_KNOW" != true ]]; then
|
if (( arr_delete_mb / 1024 > DOWNLOAD_ORPHAN_MAX_DELETE_GB )) && [[ "$I_KNOW" != true ]]; then
|
||||||
error "${arr^}: delete total $((arr_delete_mb / 1024))G exceeds cap of ${DOWNLOAD_ORPHAN_MAX_DELETE_GB}G — aborting delete pass"
|
error "${arr^}: delete total $((arr_delete_mb / 1024))G exceeds cap of ${DOWNLOAD_ORPHAN_MAX_DELETE_GB}G — aborting delete pass"
|
||||||
notify "${arr^} download orphan delete total $((arr_delete_mb / 1024))G exceeds ${DOWNLOAD_ORPHAN_MAX_DELETE_GB}G cap on $(hostname) — possible partial queue data, nothing deleted. Re-run with --i-know-what-im-doing if legitimate." \
|
notify "${arr^} download orphan delete total $((arr_delete_mb / 1024))G exceeds ${DOWNLOAD_ORPHAN_MAX_DELETE_GB}G cap on $(hostname) — possible partial queue data, nothing deleted. Re-run with --i-know-what-im-doing if legitimate." \
|
||||||
|
|||||||
Reference in New Issue
Block a user