Files
LVM-Recovery-09.2026/batch.sh
T

175 lines
3.7 KiB
Bash

#!/usr/bin/env bash
set -uo pipefail
usage() {
cat <<'EOF'
Usage:
./batch.sh scan LIST.txt [--base '/source/base']
./batch.sh recover LIST.txt [--base '/source/base']
Examples:
./batch.sh scan priority.txt
./batch.sh recover priority.txt
./batch.sh scan priority.txt --base '/Media'
./batch.sh recover priority.txt --base '/Media'
EOF
}
if [[ $# -lt 2 || $# -gt 4 ]]; then
usage >&2
exit 2
fi
MODE="$1"
LIST_FILE="$2"
shift 2
BASE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--base)
[[ $# -ge 2 ]] || { echo "ERROR: --base requires a path." >&2; exit 2; }
BASE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "ERROR: Unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
done
case "$MODE" in
scan|recover) ;;
*) echo "ERROR: mode must be 'scan' or 'recover'." >&2; exit 2 ;;
esac
[[ -f "$LIST_FILE" ]] || { echo "ERROR: list file not found: $LIST_FILE" >&2; exit 1; }
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
WORKER="$SCRIPT_DIR/${MODE}.sh"
[[ -x "$WORKER" ]] || {
echo "ERROR: expected executable worker beside batch.sh: $WORKER" >&2
exit 1
}
if [[ -n "$BASE" ]]; then
[[ "$BASE" == /* ]] || BASE="/$BASE"
BASE="${BASE%/}"
fi
if [[ -d /mnt/recovery && -w /mnt/recovery ]]; then
LOG_DIR="/mnt/recovery"
else
LOG_DIR="$(cd -- "$(dirname -- "$LIST_FILE")" && pwd)"
fi
STAMP="$(date '+%Y%m%d-%H%M%S')"
LIST_BASENAME="$(basename -- "$LIST_FILE")"
LIST_STEM="${LIST_BASENAME%.*}"
LOG_FILE="$LOG_DIR/batch-${MODE}-${LIST_STEM}-${STAMP}.log"
SUMMARY_FILE="$LOG_DIR/batch-${MODE}-${LIST_STEM}-${STAMP}-summary.txt"
declare -A SEEN=()
total=0
ok=0
failed=0
ignored=0
{
echo "Generic Plex Salvage Batch"
echo "=========================="
echo "Mode: $MODE"
echo "List: $LIST_FILE"
echo "Base: ${BASE:-<none>}"
echo "Started: $(date -Is)"
echo
} | tee -a "$LOG_FILE"
while IFS= read -r raw || [[ -n "$raw" ]]; do
line="${raw%$'\r'}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
if [[ -z "$line" || "$line" == \#* ]]; then
((ignored+=1))
continue
fi
if [[ "$line" == /* ]]; then
source_path="$line"
else
if [[ -z "$BASE" ]]; then
((total+=1))
((failed+=1))
echo "FAILED: relative entry requires --base: $line" | tee -a "$LOG_FILE"
continue
fi
source_path="$BASE/$line"
fi
while [[ "$source_path" == *"//"* ]]; do
source_path="${source_path//\/\//\/}"
done
if [[ -n "${SEEN[$source_path]+x}" ]]; then
((ignored+=1))
echo "SKIP duplicate: $source_path" | tee -a "$LOG_FILE"
continue
fi
SEEN["$source_path"]=1
((total+=1))
{
echo "----------------------------------------------------------------"
echo "[$total] ${MODE^^}: $source_path"
echo "Started: $(date -Is)"
} | tee -a "$LOG_FILE"
set +e
"$WORKER" "$source_path" 2>&1 | tee -a "$LOG_FILE"
rc=${PIPESTATUS[0]}
set -e
if [[ $rc -eq 0 ]]; then
((ok+=1))
echo "RESULT: OK ($source_path)" | tee -a "$LOG_FILE"
else
((failed+=1))
echo "RESULT: FAILED rc=$rc ($source_path)" | tee -a "$LOG_FILE"
fi
echo "Finished: $(date -Is)" | tee -a "$LOG_FILE"
echo | tee -a "$LOG_FILE"
done < "$LIST_FILE"
{
echo "GENERIC PLEX SALVAGE BATCH SUMMARY"
echo "=================================="
echo "Mode: $MODE"
echo "List: $LIST_FILE"
echo "Base: ${BASE:-<none>}"
echo "Completed: $(date -Is)"
echo
echo "Processed: $total"
echo "Succeeded: $ok"
echo "Failed: $failed"
echo "Ignored: $ignored"
echo
echo "Full log: $LOG_FILE"
} | tee "$SUMMARY_FILE"
echo
echo "Summary: $SUMMARY_FILE"
[[ $failed -eq 0 ]]