Add docker actions, arr profile enforcer, monitor caching, and web file symlink
Web files now served via symlink to the git repo so git pull changes survive reboots without rebuilding the txz. Also includes: docker pull/rebuild/restart with live log streaming, arr_profile_enforcer for Sonarr/Radarr quality profiles, monitor page cache fix (background writer now in cron), and ARR_KIDS/SONARR/RADARR profile name vars in master.conf.
This commit is contained in:
Executable
+194
@@ -0,0 +1,194 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================================
|
||||
# ========================= Arr Profile Enforcer ==============================================
|
||||
# ==============================================================================================
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Ensures every series/movie in Sonarr and Radarr is on the correct quality
|
||||
# profile based on its root folder. Safe to re-run — only touches items whose
|
||||
# current profile is wrong.
|
||||
#
|
||||
# RULES
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Root folder path contains "kids" OR "anime" → kids profile
|
||||
# All other root folders → default profile
|
||||
#
|
||||
# Profile names are looked up by name from the API at runtime, so profile IDs
|
||||
# do not need to be hardcoded and work across hosts.
|
||||
#
|
||||
# ── USAGE ────────────────────────────────────────────────────────────────────
|
||||
# arr_profile_enforcer.sh [--dry-run] [--sonarr-only] [--radarr-only]
|
||||
#
|
||||
# ── CONFIGURATION ────────────────────────────────────────────────────────────
|
||||
# master.conf
|
||||
# ARR_KIDS_PROFILE_NAME — profile name for kids/anime (default: "Kids shows")
|
||||
# ARR_SONARR_DEFAULT_PROFILE — default Sonarr profile name (default: "Any")
|
||||
# ARR_RADARR_DEFAULT_PROFILE — default Radarr profile name (default: "Any (mine)")
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
DRY_RUN=false
|
||||
RUN_SONARR=true
|
||||
RUN_RADARR=true
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--sonarr-only) RUN_RADARR=false ;;
|
||||
--radarr-only) RUN_SONARR=false ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/../load_config.sh"
|
||||
detect_hosts
|
||||
|
||||
KIDS_PROFILE_NAME="${ARR_KIDS_PROFILE_NAME:-Kids shows}"
|
||||
SONARR_DEFAULT="${ARR_SONARR_DEFAULT_PROFILE:-Any}"
|
||||
RADARR_DEFAULT="${ARR_RADARR_DEFAULT_PROFILE:-Any (mine)}"
|
||||
|
||||
# ── Helpers ──────────────────────────────────────────────────────────────────
|
||||
_arr_get() {
|
||||
local url="$1" key="$2" endpoint="$3"
|
||||
curl -sf --max-time 30 -H "X-Api-Key: $key" "$url/api/v3/$endpoint"
|
||||
}
|
||||
|
||||
_arr_put() {
|
||||
local url="$1" key="$2" endpoint="$3" body="$4"
|
||||
curl -sf --max-time 60 -X PUT \
|
||||
-H "X-Api-Key: $key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$body" \
|
||||
"$url/api/v3/$endpoint"
|
||||
}
|
||||
|
||||
_profile_id_by_name() {
|
||||
local profiles_json="$1" name="$2"
|
||||
php -r '
|
||||
$profiles = json_decode(file_get_contents("php://stdin"), true);
|
||||
$name = $argv[1];
|
||||
foreach ($profiles as $p) {
|
||||
if (strcasecmp($p["name"], $name) === 0) { echo $p["id"]; exit; }
|
||||
}
|
||||
exit(1);
|
||||
' "$name" <<< "$profiles_json"
|
||||
}
|
||||
|
||||
_is_kids_path() {
|
||||
local path="$1"
|
||||
local dir
|
||||
dir=$(basename "$(dirname "$path")")
|
||||
[[ "$dir" == *kids* || "$dir" == *anime* ]]
|
||||
}
|
||||
|
||||
# ── Core enforcer ─────────────────────────────────────────────────────────────
|
||||
# _enforce <label> <url> <api_key> <items_endpoint> <id_field> <editor_endpoint>
|
||||
# <kids_profile_id> <default_profile_id>
|
||||
_enforce() {
|
||||
local label="$1" url="$2" key="$3" items_ep="$4"
|
||||
local id_field="$5" editor_ep="$6"
|
||||
local kids_id="$7" default_id="$8"
|
||||
|
||||
local items
|
||||
items=$(_arr_get "$url" "$key" "$items_ep") || {
|
||||
error "[$label] Cannot reach $url"
|
||||
return 1
|
||||
}
|
||||
|
||||
local to_kids=() to_default=()
|
||||
|
||||
while IFS='|' read -r id profile_id path; do
|
||||
if _is_kids_path "$path"; then
|
||||
[[ "$profile_id" -ne "$kids_id" ]] && to_kids+=("$id")
|
||||
else
|
||||
[[ "$profile_id" -ne "$default_id" ]] && to_default+=("$id")
|
||||
fi
|
||||
done < <(php -r '
|
||||
$items = json_decode(file_get_contents("php://stdin"), true);
|
||||
foreach ($items as $r) echo $r["id"]."|".$r["qualityProfileId"]."|".$r["path"]."\n";
|
||||
' <<< "$items")
|
||||
|
||||
local total=$(( ${#to_kids[@]} + ${#to_default[@]} ))
|
||||
log "arr_profile_enforcer" "[$label] ${#to_kids[@]} → $KIDS_PROFILE_NAME | ${#to_default[@]} → default | $total to fix"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo " [$label] DRY RUN — would set $KIDS_PROFILE_NAME on ${#to_kids[@]}, default on ${#to_default[@]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_bulk_update() {
|
||||
local profile_id="$1" prof_label="$2"
|
||||
shift 2
|
||||
local ids=("$@")
|
||||
[[ ${#ids[@]} -eq 0 ]] && return 0
|
||||
|
||||
local ids_json
|
||||
ids_json=$(printf '%s,' "${ids[@]}")
|
||||
ids_json="[${ids_json%,}]"
|
||||
|
||||
local result
|
||||
result=$(_arr_put "$url" "$key" "$editor_ep" \
|
||||
"{\"${id_field}\":${ids_json},\"qualityProfileId\":${profile_id}}") || {
|
||||
error "[$label] Bulk update failed for $prof_label"
|
||||
return 1
|
||||
}
|
||||
|
||||
local updated
|
||||
updated=$(php -r 'echo count(json_decode(file_get_contents("php://stdin"), true));' <<< "$result")
|
||||
log "arr_profile_enforcer" "[$label] Set $prof_label on $updated items"
|
||||
}
|
||||
|
||||
_bulk_update "$kids_id" "$KIDS_PROFILE_NAME" "${to_kids[@]+"${to_kids[@]}"}"
|
||||
_bulk_update "$default_id" "default" "${to_default[@]+"${to_default[@]}"}"
|
||||
}
|
||||
|
||||
# ── Sonarr ────────────────────────────────────────────────────────────────────
|
||||
if [[ "$RUN_SONARR" == true ]]; then
|
||||
require_var SONARR_URL
|
||||
require_var SONARR_API_KEY
|
||||
|
||||
SONARR_PROFILES=$(_arr_get "$SONARR_URL" "$SONARR_API_KEY" "qualityprofile") || {
|
||||
error "Cannot reach Sonarr at $SONARR_URL"
|
||||
exit 1
|
||||
}
|
||||
|
||||
SONARR_KIDS_ID=$(_profile_id_by_name "$SONARR_PROFILES" "$KIDS_PROFILE_NAME") || {
|
||||
error "Sonarr profile not found: '$KIDS_PROFILE_NAME'"
|
||||
exit 1
|
||||
}
|
||||
SONARR_DEFAULT_ID=$(_profile_id_by_name "$SONARR_PROFILES" "$SONARR_DEFAULT") || {
|
||||
error "Sonarr profile not found: '$SONARR_DEFAULT'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
_enforce "Sonarr" "$SONARR_URL" "$SONARR_API_KEY" \
|
||||
"series" "seriesIds" "series/editor" \
|
||||
"$SONARR_KIDS_ID" "$SONARR_DEFAULT_ID"
|
||||
fi
|
||||
|
||||
# ── Radarr ────────────────────────────────────────────────────────────────────
|
||||
if [[ "$RUN_RADARR" == true ]]; then
|
||||
require_var RADARR_URL
|
||||
require_var RADARR_API_KEY
|
||||
|
||||
RADARR_PROFILES=$(_arr_get "$RADARR_URL" "$RADARR_API_KEY" "qualityprofile") || {
|
||||
error "Cannot reach Radarr at $RADARR_URL"
|
||||
exit 1
|
||||
}
|
||||
|
||||
RADARR_KIDS_ID=$(_profile_id_by_name "$RADARR_PROFILES" "$KIDS_PROFILE_NAME") || {
|
||||
error "Radarr profile not found: '$KIDS_PROFILE_NAME'"
|
||||
exit 1
|
||||
}
|
||||
RADARR_DEFAULT_ID=$(_profile_id_by_name "$RADARR_PROFILES" "$RADARR_DEFAULT") || {
|
||||
error "Radarr profile not found: '$RADARR_DEFAULT'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
_enforce "Radarr" "$RADARR_URL" "$RADARR_API_KEY" \
|
||||
"movie" "movieIds" "movie/editor" \
|
||||
"$RADARR_KIDS_ID" "$RADARR_DEFAULT_ID"
|
||||
fi
|
||||
|
||||
log "arr_profile_enforcer" "Done"
|
||||
Reference in New Issue
Block a user