Bring script headers onto the template and close safeguard gaps
Headers claimed protections the code never had, and several destructive paths had no guard against a collapsed config value.
This commit is contained in:
@@ -62,6 +62,94 @@
|
||||
# logged in Sonarr's history as "UserInvokedSearch").
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Detect Always, Act Only on Request
|
||||
# A bare run probes and reports. Deleting a file the arr believes it has is a
|
||||
# destructive act, so it requires --remediate explicitly. The scan can be scheduled
|
||||
# weekly and read without any risk of it removing media on its own.
|
||||
#
|
||||
# One Probe Result Is Not Evidence
|
||||
# ffprobe can fail for reasons that have nothing to do with the file — a mid-write
|
||||
# import, an NFS blip, a container restart. Corruption must be observed
|
||||
# CORRUPTION_SCAN_STRIKE_LIMIT times consecutively before remediation acts, and a
|
||||
# single clean re-probe resets the counter.
|
||||
#
|
||||
# Skip-Cache Over Re-Probing
|
||||
# At 90k+ tracked files a full re-probe every run is not viable. Files unchanged by
|
||||
# mtime and size since they last verified clean are skipped, so each run spends its
|
||||
# time on what actually changed rather than re-proving the library from scratch.
|
||||
#
|
||||
# Delete the Record, Let the Arr Re-Acquire
|
||||
# Remediation removes the file record and explicitly triggers a search. The arr is
|
||||
# left to obtain a good copy through its normal path — this script never tries to
|
||||
# repair a file in place.
|
||||
#
|
||||
# Explicit Search, Not the Background Cycle
|
||||
# The re-search is triggered directly rather than left to the arr's own missing-search
|
||||
# cycle, because that cycle skips unmonitored items entirely and would silently leave
|
||||
# an unmonitored corrupt file deleted and never replaced.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root Enforcement
|
||||
# docker exec into the ffprobe container requires root.
|
||||
#
|
||||
# Lock Acquisition
|
||||
# acquire_lock prevents overlapping runs. Two instances would both probe and could
|
||||
# both count a strike against the same file, reaching the limit in half the intended
|
||||
# number of observations.
|
||||
#
|
||||
# Host Detection
|
||||
# detect_hosts() aliases the arr URLs, API keys and HOST*_FFPROBE_CONTAINER.
|
||||
#
|
||||
# jq Dependency Check
|
||||
# Fails fast if jq is missing — the tracked-file lists and every hasFile verification
|
||||
# are parsed with it.
|
||||
#
|
||||
# Report-Only Default
|
||||
# Nothing is deleted without --remediate.
|
||||
#
|
||||
# ffprobe Configuration Check
|
||||
# Exits cleanly if FFPROBE_CONTAINER / FFPROBE_BIN are unconfigured for this host.
|
||||
#
|
||||
# ffprobe Container Health Check
|
||||
# check_container_health() verifies the container is running and healthy before any
|
||||
# probing. Every probe is a docker exec into it — if it is stopped or unhealthy every
|
||||
# exec fails, every file reads as corrupt, and two consecutive runs would clear the
|
||||
# strike limit and hand --remediate the whole library to delete.
|
||||
#
|
||||
# API Reachability + Version Gate
|
||||
# check_api then check_arr_version per arr. A version mismatch skips that arr rather
|
||||
# than issuing deletes against an API whose file-record endpoints may have moved.
|
||||
#
|
||||
# Per-Arr Isolation
|
||||
# Sonarr and Radarr run sequentially, and one failing, unconfigured or version-
|
||||
# mismatched arr never blocks the other.
|
||||
#
|
||||
# Unmapped Path Skip
|
||||
# Files whose arr-side path cannot be mapped into the ffprobe container's mount
|
||||
# namespace are skipped and counted, never probed through a wrong path and never
|
||||
# treated as corrupt because the probe could not see them.
|
||||
#
|
||||
# Strike Threshold
|
||||
# CORRUPTION_SCAN_STRIKE_LIMIT consecutive corrupt detections are required before
|
||||
# --remediate deletes anything. A transient ffprobe failure cannot trigger a delete,
|
||||
# and a clean re-probe clears the counter.
|
||||
#
|
||||
# Post-Delete Verification
|
||||
# The DELETE response is never trusted. hasFile is re-checked and must have flipped
|
||||
# false before the re-search is issued, so a failed delete never leaves the arr
|
||||
# searching for something it still believes it has.
|
||||
#
|
||||
# Targeted Deletion
|
||||
# Only the specific episodefile/moviefile record for the corrupt file is removed —
|
||||
# never the series, movie, or any sibling file.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
@@ -174,6 +262,11 @@ if [[ -z "$FFPROBE_CONTAINER" || -z "$FFPROBE_BIN" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Every probe is a docker exec into this container. If it is stopped or unhealthy, every
|
||||
# exec fails, every file reads as corrupt, and two such runs would clear the strike limit
|
||||
# and hand --remediate an entire library to delete. Abort before probing anything.
|
||||
check_container_health "$FFPROBE_CONTAINER" "${DOCKER_TIMEOUT:-30}" "Arr Corruption Scan"
|
||||
|
||||
CORRUPTION_SCAN_STATE_FILE="${CORRUPTION_SCAN_STATE_FILE:-$DATA_DIR/corruption_scan_state.tsv}"
|
||||
mkdir -p "$(dirname "$CORRUPTION_SCAN_STATE_FILE")"
|
||||
touch "$CORRUPTION_SCAN_STATE_FILE"
|
||||
|
||||
Reference in New Issue
Block a user