added and testin a downloar reset script
This commit is contained in:
@@ -0,0 +1,425 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================================
|
||||
# downloaders_reset.sh
|
||||
# ==============================================================================================
|
||||
# PURPOSE: Daily maintenance reset for all download clients.
|
||||
# Clears stuck states, purges old history, and prepares downloaders for a
|
||||
# clean daily cycle. Run before container restarts in daily maintenance.
|
||||
#
|
||||
# DOWNLOADERS COVERED:
|
||||
# slskd — clears stuck/errored searches, dead transfer records,
|
||||
# purges expired failed imports
|
||||
# SABnzbd — clears completed and failed history older than retention period,
|
||||
# removes stalled/paused queue items
|
||||
# qBittorrent — last chance failsafe delete for torrents older than
|
||||
# QBIT_FAILSAFE_MIN_DAYS regardless of ratio
|
||||
#
|
||||
# USAGE:
|
||||
# bash downloaders_reset.sh # live run
|
||||
# bash downloaders_reset.sh --dry-run # preview only, no changes made
|
||||
#
|
||||
# SCHEDULE: Run before downloader restarts in daily_sync_maintenance.sh
|
||||
# Ensures all downloaders start each day in a clean known state
|
||||
#
|
||||
# SAFETY:
|
||||
# Always --dry-run first before scheduling
|
||||
# slskd transfer cleanup skips any user with InProgress or Queued transfers
|
||||
# qBittorrent only deletes if torrent age exceeds QBIT_FAILSAFE_MIN_DAYS
|
||||
# SABnzbd only deletes history older than RETENTION_DAYS
|
||||
#
|
||||
# TO ADD A NEW DOWNLOADER:
|
||||
# Add URL, API key, and any specific variables in Configuration section
|
||||
# Add a new section block following the same pattern as existing downloaders
|
||||
# ==============================================================================================
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ Configuration ━━━
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
# Retention period in days — applies to ALL cleanup sections:
|
||||
# slskd failed imports, SABnzbd completed history, SABnzbd failed history
|
||||
RETENTION_DAYS=7
|
||||
|
||||
# ━━━ slskd ━━━
|
||||
SLSKD_URL="http://localhost:8980"
|
||||
SLSKD_API_KEY="4bF9kL2mNpQrT7vWxYz1A3dEgHjKoRsU"
|
||||
|
||||
# Path to Soularr failed imports folder
|
||||
# Created by Soularr when Lidarr rejects a downloaded album
|
||||
SLSKD_FAILED_IMPORTS_DIR="/mnt/user/Temp_Storage/Slskd/completed/failed_imports"
|
||||
|
||||
# ━━━ SABnzbd ━━━
|
||||
SABNZBD_URL="http://localhost:8180"
|
||||
SABNZBD_API_KEY="8bfefe41d83b4d50883e32859b55ca9a"
|
||||
|
||||
# ━━━ qBittorrent ━━━
|
||||
QBIT_URL="http://localhost:8080"
|
||||
QBIT_USERNAME="admin"
|
||||
QBIT_PASSWORD="changeme"
|
||||
|
||||
# Last chance failsafe — deletes torrents older than this many days
|
||||
# regardless of ratio. qBittorrent's own rules handle normal cleanup
|
||||
# (ratio >= 1.25 OR 45 days inactive). This catches anything missed
|
||||
# after an extended period. Set ratio gate to 0 to disable ratio check
|
||||
# and use age as the only condition.
|
||||
QBIT_FAILSAFE_MIN_DAYS=180
|
||||
QBIT_FAILSAFE_MIN_RATIO=0
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
DRY_RUN=false
|
||||
[[ "$1" == "--dry-run" ]] && DRY_RUN=true
|
||||
|
||||
$DRY_RUN && echo "🧪 DRY RUN MODE — no changes will be made"
|
||||
echo ""
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ slskd — Clear Stuck Searches ━━━
|
||||
# Clears searches in Completed/Errored state left behind by Soularr crashes.
|
||||
# Prevents 409 Conflict error on next Soularr startup.
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔍 slskd — Stuck Searches"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
SEARCHES=$(curl -s -X GET "$SLSKD_URL/api/v0/searches" \
|
||||
-H "X-Api-Key: $SLSKD_API_KEY")
|
||||
|
||||
IDS=$(echo "$SEARCHES" | grep -o '"id":"[^"]*","isComplete":true' | \
|
||||
grep -o '"id":"[^"]*"' | sed 's/"id":"//;s/"//')
|
||||
COUNT=$(echo "$IDS" | grep -c . 2>/dev/null || echo 0)
|
||||
|
||||
if [[ -z "$IDS" || "$COUNT" -eq 0 ]]; then
|
||||
echo "✅ No stuck searches found"
|
||||
else
|
||||
echo "📋 Found $COUNT stuck search(es)"
|
||||
if $DRY_RUN; then
|
||||
echo "🧪 Would delete search IDs:"
|
||||
echo "$IDS"
|
||||
else
|
||||
SUCCESS=0
|
||||
FAIL=0
|
||||
while IFS= read -r ID; do
|
||||
[[ -z "$ID" ]] && continue
|
||||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
"$SLSKD_URL/api/v0/searches/$ID" \
|
||||
-H "X-Api-Key: $SLSKD_API_KEY")
|
||||
if [[ "$RESULT" == "200" || "$RESULT" == "204" ]]; then
|
||||
echo " 🗑️ Cleared search: $ID"
|
||||
((SUCCESS++))
|
||||
else
|
||||
echo " ❌ Failed: $ID (HTTP $RESULT)"
|
||||
((FAIL++))
|
||||
fi
|
||||
done <<< "$IDS"
|
||||
echo "✅ Searches: $SUCCESS cleared, $FAIL failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ slskd — Clear Dead Transfer Records ━━━
|
||||
# Removes completed/errored/aborted transfer records per user.
|
||||
# Prevents Soularr 404 loop when polling a user whose transfer
|
||||
# record no longer exists after a crash or disconnect.
|
||||
# NEVER removes transfers that are InProgress or Queued.
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔍 slskd — Dead Transfer Records"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
TRANSFERS=$(curl -s -X GET "$SLSKD_URL/api/v0/transfers/downloads" \
|
||||
-H "X-Api-Key: $SLSKD_API_KEY")
|
||||
|
||||
USERNAMES=$(echo "$TRANSFERS" | grep -o '"username":"[^"]*"' | \
|
||||
sed 's/"username":"//;s/"//' | sort -u)
|
||||
|
||||
if [[ -z "$USERNAMES" ]]; then
|
||||
echo "✅ No transfer records found"
|
||||
else
|
||||
SUCCESS=0
|
||||
SKIPPED=0
|
||||
FAIL=0
|
||||
|
||||
while IFS= read -r USER; do
|
||||
[[ -z "$USER" ]] && continue
|
||||
|
||||
USER_STATES=$(echo "$TRANSFERS" | grep -o \
|
||||
"\"username\":\"$USER\"[^}]*\"state\":\"[^\"]*\"" | \
|
||||
grep -o '"state":"[^"]*"')
|
||||
|
||||
ACTIVE=$(echo "$USER_STATES" | grep -c "InProgress\|Queued")
|
||||
|
||||
if [[ "$ACTIVE" -gt 0 ]]; then
|
||||
echo " ⏳ Skipping $USER — $ACTIVE active/queued transfer(s)"
|
||||
((SKIPPED++))
|
||||
continue
|
||||
fi
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "🧪 Would clear transfers for: $USER"
|
||||
((SUCCESS++))
|
||||
continue
|
||||
fi
|
||||
|
||||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
"$SLSKD_URL/api/v0/transfers/downloads/$USER" \
|
||||
-H "X-Api-Key: $SLSKD_API_KEY")
|
||||
|
||||
if [[ "$RESULT" == "200" || "$RESULT" == "204" ]]; then
|
||||
echo " 🗑️ Cleared transfers for: $USER"
|
||||
((SUCCESS++))
|
||||
else
|
||||
echo " ❌ Failed to clear: $USER (HTTP $RESULT)"
|
||||
((FAIL++))
|
||||
fi
|
||||
done <<< "$USERNAMES"
|
||||
|
||||
echo "✅ Transfers: $SUCCESS cleared, $SKIPPED skipped (active), $FAIL failed"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ slskd — Purge Expired Failed Imports ━━━
|
||||
# Removes albums Soularr downloaded but Lidarr rejected.
|
||||
# Soularr moves these to failed_imports/ and never cleans them up.
|
||||
# Retention controlled by RETENTION_DAYS variable.
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔍 slskd — Failed Imports (older than ${RETENTION_DAYS} days)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
if [[ ! -d "$SLSKD_FAILED_IMPORTS_DIR" ]]; then
|
||||
echo "⚠️ Directory not found: $SLSKD_FAILED_IMPORTS_DIR"
|
||||
else
|
||||
OLD_IMPORTS=$(find "$SLSKD_FAILED_IMPORTS_DIR" \
|
||||
-mindepth 1 -maxdepth 1 -mtime +${RETENTION_DAYS})
|
||||
IMPORT_COUNT=$(echo "$OLD_IMPORTS" | grep -c . 2>/dev/null || echo 0)
|
||||
|
||||
if [[ -z "$OLD_IMPORTS" || "$IMPORT_COUNT" -eq 0 ]]; then
|
||||
echo "✅ No expired failed imports found"
|
||||
else
|
||||
echo "📋 Found $IMPORT_COUNT expired failed import(s)"
|
||||
if $DRY_RUN; then
|
||||
echo "🧪 Would delete:"
|
||||
echo "$OLD_IMPORTS"
|
||||
else
|
||||
find "$SLSKD_FAILED_IMPORTS_DIR" \
|
||||
-mindepth 1 -maxdepth 1 -mtime +${RETENTION_DAYS} \
|
||||
-exec rm -rf {} \;
|
||||
echo "🗑️ Purged $IMPORT_COUNT expired failed import(s)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ SABnzbd — Clear Completed History ━━━
|
||||
# Removes completed download history older than RETENTION_DAYS.
|
||||
# Keeps history clean without losing recent job records.
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔍 SABnzbd — Completed History (older than ${RETENTION_DAYS} days)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
CUTOFF=$(( $(date +%s) - (RETENTION_DAYS * 86400) ))
|
||||
|
||||
HISTORY=$(curl -s \
|
||||
"$SABNZBD_URL/api?mode=history&output=json&limit=1000&apikey=$SABNZBD_API_KEY")
|
||||
|
||||
COMPLETED_IDS=$(echo "$HISTORY" | grep -o '"nzo_id":"[^"]*"' | \
|
||||
sed 's/"nzo_id":"//;s/"//')
|
||||
|
||||
if [[ -z "$COMPLETED_IDS" ]]; then
|
||||
echo "✅ No completed history found"
|
||||
else
|
||||
DELETED=0
|
||||
SKIPPED=0
|
||||
while IFS= read -r NZO_ID; do
|
||||
[[ -z "$NZO_ID" ]] && continue
|
||||
JOB_TIME=$(echo "$HISTORY" | grep -A5 "$NZO_ID" | \
|
||||
grep -o '"completed":[0-9]*' | grep -o '[0-9]*')
|
||||
[[ -z "$JOB_TIME" ]] && continue
|
||||
[[ "$JOB_TIME" -gt "$CUTOFF" ]] && ((SKIPPED++)) && continue
|
||||
if $DRY_RUN; then
|
||||
echo "🧪 Would delete completed job: $NZO_ID"
|
||||
((DELETED++))
|
||||
else
|
||||
curl -s \
|
||||
"$SABNZBD_URL/api?mode=history&name=delete&value=$NZO_ID&apikey=$SABNZBD_API_KEY" \
|
||||
> /dev/null
|
||||
echo " 🗑️ Deleted: $NZO_ID"
|
||||
((DELETED++))
|
||||
fi
|
||||
done <<< "$COMPLETED_IDS"
|
||||
echo "✅ Completed: $DELETED deleted, $SKIPPED within retention period"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ SABnzbd — Clear Failed History ━━━
|
||||
# Removes failed download history older than RETENTION_DAYS.
|
||||
# Failed jobs older than retention period serve no purpose.
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔍 SABnzbd — Failed History (older than ${RETENTION_DAYS} days)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
FAILED=$(curl -s \
|
||||
"$SABNZBD_URL/api?mode=history&output=json&limit=1000&failed_only=1&apikey=$SABNZBD_API_KEY")
|
||||
|
||||
FAILED_IDS=$(echo "$FAILED" | grep -o '"nzo_id":"[^"]*"' | \
|
||||
sed 's/"nzo_id":"//;s/"//')
|
||||
|
||||
if [[ -z "$FAILED_IDS" ]]; then
|
||||
echo "✅ No failed history found"
|
||||
else
|
||||
DELETED=0
|
||||
SKIPPED=0
|
||||
while IFS= read -r NZO_ID; do
|
||||
[[ -z "$NZO_ID" ]] && continue
|
||||
JOB_TIME=$(echo "$FAILED" | grep -A5 "$NZO_ID" | \
|
||||
grep -o '"completed":[0-9]*' | grep -o '[0-9]*')
|
||||
[[ -z "$JOB_TIME" ]] && continue
|
||||
[[ "$JOB_TIME" -gt "$CUTOFF" ]] && ((SKIPPED++)) && continue
|
||||
if $DRY_RUN; then
|
||||
echo "🧪 Would delete failed job: $NZO_ID"
|
||||
((DELETED++))
|
||||
else
|
||||
curl -s \
|
||||
"$SABNZBD_URL/api?mode=history&name=delete&value=$NZO_ID&apikey=$SABNZBD_API_KEY" \
|
||||
> /dev/null
|
||||
echo " 🗑️ Deleted: $NZO_ID"
|
||||
((DELETED++))
|
||||
fi
|
||||
done <<< "$FAILED_IDS"
|
||||
echo "✅ Failed: $DELETED deleted, $SKIPPED within retention period"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ SABnzbd — Remove Stalled Queue Items ━━━
|
||||
# Removes paused queue items that are no longer progressing.
|
||||
# Active downloading items are never touched.
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔍 SABnzbd — Stalled Queue Items"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
QUEUE=$(curl -s \
|
||||
"$SABNZBD_URL/api?mode=queue&output=json&apikey=$SABNZBD_API_KEY")
|
||||
|
||||
STALLED_IDS=$(echo "$QUEUE" | grep -o '"nzo_id":"[^"]*"' | \
|
||||
sed 's/"nzo_id":"//;s/"//')
|
||||
|
||||
if [[ -z "$STALLED_IDS" ]]; then
|
||||
echo "✅ No stalled queue items found"
|
||||
else
|
||||
DELETED=0
|
||||
SKIPPED=0
|
||||
while IFS= read -r NZO_ID; do
|
||||
[[ -z "$NZO_ID" ]] && continue
|
||||
STATUS=$(echo "$QUEUE" | grep -A10 "$NZO_ID" | \
|
||||
grep -o '"status":"[^"]*"' | sed 's/"status":"//;s/"//')
|
||||
[[ "$STATUS" != "Paused" ]] && ((SKIPPED++)) && continue
|
||||
if $DRY_RUN; then
|
||||
echo "🧪 Would remove stalled item: $NZO_ID"
|
||||
((DELETED++))
|
||||
else
|
||||
curl -s \
|
||||
"$SABNZBD_URL/api?mode=queue&name=delete&value=$NZO_ID&apikey=$SABNZBD_API_KEY" \
|
||||
> /dev/null
|
||||
echo " 🗑️ Removed: $NZO_ID"
|
||||
((DELETED++))
|
||||
fi
|
||||
done <<< "$STALLED_IDS"
|
||||
echo "✅ Queue: $DELETED removed, $SKIPPED active (skipped)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ━━━ qBittorrent — Last Chance Failsafe Cleanup ━━━
|
||||
# Deletes torrents older than QBIT_FAILSAFE_MIN_DAYS.
|
||||
# qBittorrent's own rules handle normal cleanup (ratio >= 1.25 OR 45 days
|
||||
# inactive). This catches anything missed after extended time.
|
||||
# QBIT_FAILSAFE_MIN_RATIO=0 disables ratio gate — age is the only condition.
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔍 qBittorrent — Failsafe Cleanup"
|
||||
echo " Age > ${QBIT_FAILSAFE_MIN_DAYS} days"
|
||||
[[ "$QBIT_FAILSAFE_MIN_RATIO" != "0" ]] && \
|
||||
echo " Ratio >= ${QBIT_FAILSAFE_MIN_RATIO}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Login to qBittorrent and get session cookie
|
||||
QBIT_COOKIE=$(curl -s -c - \
|
||||
"$QBIT_URL/api/v2/auth/login" \
|
||||
--data "username=$QBIT_USERNAME&password=$QBIT_PASSWORD" | \
|
||||
grep SID | awk '{print "SID="$NF}')
|
||||
|
||||
if [[ -z "$QBIT_COOKIE" ]]; then
|
||||
echo "❌ Failed to authenticate with qBittorrent — check URL, username, password"
|
||||
else
|
||||
TORRENTS=$(curl -s \
|
||||
"$QBIT_URL/api/v2/torrents/info" \
|
||||
-H "Cookie: $QBIT_COOKIE")
|
||||
|
||||
HASHES=$(echo "$TORRENTS" | grep -o '"hash":"[^"]*"' | \
|
||||
sed 's/"hash":"//;s/"//')
|
||||
|
||||
NOW=$(date +%s)
|
||||
CUTOFF_AGE=$(( NOW - (QBIT_FAILSAFE_MIN_DAYS * 86400) ))
|
||||
|
||||
DELETED=0
|
||||
SKIPPED=0
|
||||
|
||||
while IFS= read -r HASH; do
|
||||
[[ -z "$HASH" ]] && continue
|
||||
|
||||
# Get added_on timestamp and ratio for this torrent
|
||||
ADDED=$(echo "$TORRENTS" | grep -A30 "\"hash\":\"$HASH\"" | \
|
||||
grep -o '"added_on":[0-9]*' | grep -o '[0-9]*' | head -1)
|
||||
RATIO=$(echo "$TORRENTS" | grep -A30 "\"hash\":\"$HASH\"" | \
|
||||
grep -o '"ratio":[0-9.]*' | grep -o '[0-9.]*' | head -1)
|
||||
NAME=$(echo "$TORRENTS" | grep -A30 "\"hash\":\"$HASH\"" | \
|
||||
grep -o '"name":"[^"]*"' | sed 's/"name":"//;s/"//' | head -1)
|
||||
|
||||
[[ -z "$ADDED" ]] && continue
|
||||
|
||||
# Check age gate
|
||||
[[ "$ADDED" -gt "$CUTOFF_AGE" ]] && ((SKIPPED++)) && continue
|
||||
|
||||
# Check ratio gate if enabled
|
||||
if [[ "$QBIT_FAILSAFE_MIN_RATIO" != "0" ]]; then
|
||||
RATIO_OK=$(echo "$RATIO >= $QBIT_FAILSAFE_MIN_RATIO" | bc -l 2>/dev/null)
|
||||
[[ "$RATIO_OK" != "1" ]] && ((SKIPPED++)) && continue
|
||||
fi
|
||||
|
||||
AGE_DAYS=$(( (NOW - ADDED) / 86400 ))
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "🧪 Would delete: $NAME (${AGE_DAYS} days old, ratio: $RATIO)"
|
||||
((DELETED++))
|
||||
else
|
||||
curl -s -X POST \
|
||||
"$QBIT_URL/api/v2/torrents/delete" \
|
||||
-H "Cookie: $QBIT_COOKIE" \
|
||||
--data "hashes=$HASH&deleteFiles=false"
|
||||
echo " 🗑️ Deleted: $NAME (${AGE_DAYS} days old, ratio: $RATIO)"
|
||||
((DELETED++))
|
||||
fi
|
||||
done <<< "$HASHES"
|
||||
|
||||
echo "✅ qBittorrent: $DELETED deleted, $SKIPPED skipped"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ Downloaders reset complete"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
Reference in New Issue
Block a user