Add full banner headers to all scripts across the codebase

Every script now has the established header format: PURPOSE with ─────── separator,
OPERATIONAL MODEL, DESIGN PRINCIPLES, OPERATIONAL SAFEGUARDS, CONFIGURATION, and
RUNTIME MODES — structured with full ====== banner sections throughout.

Orchestrators converted from compact ── inline format to full banners. Stale
emby-fallback and dirty sync references removed from Plugin/user_script_plug-in.sh.
This commit is contained in:
Gmer4Lfe
2026-06-26 18:50:05 -04:00
parent 1003bee72a
commit f92ee4064b
51 changed files with 1822 additions and 563 deletions
+18
View File
@@ -28,6 +28,24 @@
# 3. Trigger new search — finds a different release automatically
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Hands-Free Recovery
# The script completes the full recovery cycle autonomously — blocklist, remove,
# re-search. No operator decision required. A failed import at midnight resolves
# itself before morning without any intervention.
#
# Age Gate Before Action
# Items newer than ARR_IMPORT_RECOVERY_AGE are skipped. Arrs have their own
# retry logic — acting immediately would race against it. The age gate gives
# the arr time to self-resolve before this script escalates.
#
# Blocklist First
# The bad release is blocklisted before removal and re-search. Without this,
# the re-search can re-grab the same release that just failed.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+19
View File
@@ -27,6 +27,25 @@
# classification these would be deleted — breaking Lidarr and Emby display.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# API as Ground Truth
# What Lidarr tracks is authoritative. Files not in the API response are
# orphans — Lidarr has no record of them and they serve no purpose.
# The script never infers ownership from directory structure alone.
#
# Age Gate Before Deletion
# Files under LIDARR_ORPHAN_AGE are left alone regardless of tracked status.
# Lidarr's import pipeline writes files before registering them — acting
# immediately would delete files mid-import.
#
# Seven-Gate Safety Model
# Multiple independent sanity checks must all pass before any file is touched.
# No single check is trusted in isolation — a misconfigured path returning an
# empty API response must not result in a wiped library.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+19
View File
@@ -25,6 +25,25 @@
# album/artist directories.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Additive Only
# The script only adds missing files — it never overwrites existing artwork
# or touches audio files. Re-running after a partial fetch completes exactly
# where it left off with no side effects.
#
# Source Fallback Chain
# Multiple sources are tried in order of quality preference. fanart.tv is
# primary; fallbacks exist so partial coverage is better than none. A failed
# primary never blocks the fallback from running.
#
# External API Courtesy
# Rate limiting and parallel job caps prevent hammering fanart.tv and other
# external APIs. Burst behaviour during large initial runs would risk
# temporary blocks that break future scheduled fetches.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+18
View File
@@ -43,6 +43,24 @@
# Media profile also removes: *.iso *.lrc
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Pre-Scan Cleanup
# Runs before arr cleanup scripts so orphan detection only encounters actual
# media files. Scene debris and download artifacts would otherwise appear as
# untracked files and inflate false-positive orphan counts.
#
# Profile Separation
# Anime and media share different cleanup patterns because their content
# differs. *.lrc (lyrics) and *.iso belong in media cleanup but not anime.
# Separate profiles prevent cross-contamination of rules.
#
# Conservative by Default
# Only explicitly listed patterns are removed. The script never guesses
# at file intent — if a pattern is not in the list, the file is untouched.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+18
View File
@@ -14,6 +14,24 @@
# or unRAID environment resets after updates.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Daily Failsafe, Not Enforcer
# Wrong ownership is a symptom of something else — a misconfigured container,
# a manual copy, an rsync without --chown. This script corrects the symptom
# daily rather than hunting the root cause. A persistent high correction count
# is the signal to investigate the source.
#
# Runs First in the Window
# Arr cleanup scripts depend on correct ownership to rename and delete files.
# Permissions must be correct before cleanup runs — ordering is not optional.
#
# Separate Passes for Directories and Files
# Directories need execute permission for traversal; files do not. Applying
# the same mode to both is a common mistake this script avoids by design.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+32
View File
@@ -33,6 +33,38 @@
# The higher tick count wins.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Newest Timestamp Wins
# No merge logic, no conflict resolution — the server with the most recent
# LastPlayedDate is simply authoritative. Simple rules produce predictable
# outcomes users can reason about.
#
# No Data Loss
# The sync only pushes state forward — it never clears a Played flag or
# resets a resume position to zero. A watch record on any server always
# propagates outward, never disappears.
#
# Provider ID Matching
# Items are matched by external IDs (IMDb, TVDB, MusicBrainz), not by
# title or file path. This makes matching robust across library reorganisation,
# renames, and multi-server path differences.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# PLAY_SYNC_ENABLED gate — exits cleanly when disabled; no partial runs
# PARTNERSHIP gate — skips remote sync when partnership is inactive
# Per-server reachability — unreachable servers are skipped individually;
# one offline server does not abort the entire sync
# User match required — a user missing from a server is skipped for that
# server; no cross-account state pollution
# acquire_lock — prevents concurrent runs from racing on the same
# items during the 30-minute critical window
#
# ==============================================================================================
# CONFIGURATION (host*.conf, aliased by detect_hosts)
# ==============================================================================================
#
+36 -17
View File
@@ -60,27 +60,23 @@
# Configure HOST*_LASTFM_API_KEY in host*.conf
#
# ==============================================================================================
# CONFIGURATION (master.conf)
# DESIGN PRINCIPLES
# ==============================================================================================
#
# LIDARR_DISCOVERY_THRESHOLD — minimum score for Stage 1 seeds and Stage 2 adds (default: 70)
# LIDARR_DISCOVERY_LOOKBACK_DAYS — Emby play history window in days (default: 7)
# LIDARR_DISCOVERY_MIN_PLAYS — min plays to be evaluated in Stage 1 (default: 3)
# LIDARR_DISCOVERY_MAX_ADDS — max seeds (Stage 1) and max adds (Stage 2) (default: 5)
# LIDARR_DISCOVERY_USER_CAP_PCT — max % any one user contributes to play weight (default: 35)
# LIDARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a Stage 2 reject (default: 30)
# LIDARR_DISCOVERY_HISTORY — history/state file path
# Playback as Intent Signal
# What users actually listen to is a stronger signal than what they follow or
# own. The scoring model weights demonstrated listening behaviour — recency,
# play count, user breadth — over passive library membership.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
# Selective by Design
# 05 adds per week is the target, not bulk imports. A high score threshold
# combined with MAX_ADDS ensures only high-confidence recommendations are
# acted on. Volume is not the goal — meaningful discovery is.
#
# playback_aware_lidarr_discovery.sh — normal run
# playback_aware_lidarr_discovery.sh --dry-run — score and rank, no Lidarr changes
# playback_aware_lidarr_discovery.sh --log — verbose output
# playback_aware_lidarr_discovery.sh --status — show config and exit
#
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
# Two-Stage Filtering
# Stage 1 rejects weak seeds before they drive Stage 2. Low-quality seeds
# produce low-quality similar-artist recommendations. Filtering at the seed
# stage improves the entire output, not just the top of the list.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
@@ -110,6 +106,29 @@
# runs. Safe to delete — next run starts fresh with no memory.
#
# ==============================================================================================
# CONFIGURATION (master.conf)
# ==============================================================================================
#
# LIDARR_DISCOVERY_THRESHOLD — minimum score for Stage 1 seeds and Stage 2 adds (default: 70)
# LIDARR_DISCOVERY_LOOKBACK_DAYS — Emby play history window in days (default: 7)
# LIDARR_DISCOVERY_MIN_PLAYS — min plays to be evaluated in Stage 1 (default: 3)
# LIDARR_DISCOVERY_MAX_ADDS — max seeds (Stage 1) and max adds (Stage 2) (default: 5)
# LIDARR_DISCOVERY_USER_CAP_PCT — max % any one user contributes to play weight (default: 35)
# LIDARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a Stage 2 reject (default: 30)
# LIDARR_DISCOVERY_HISTORY — history/state file path
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# playback_aware_lidarr_discovery.sh — normal run
# playback_aware_lidarr_discovery.sh --dry-run — score and rank, no Lidarr changes
# playback_aware_lidarr_discovery.sh --log — verbose output
# playback_aware_lidarr_discovery.sh --status — show config and exit
#
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
#
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+38 -19
View File
@@ -59,29 +59,23 @@
# Free key at: https://www.themoviedb.org/settings/api
#
# ==============================================================================================
# CONFIGURATION (master.conf)
# DESIGN PRINCIPLES
# ==============================================================================================
#
# RADARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
# RADARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 30)
# RADARR_DISCOVERY_MAX_SEEDS — max seed movies from Stage 1 (default: 5)
# RADARR_DISCOVERY_MAX_ADDS — max movies to add per run (default: 5)
# RADARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 100)
# RADARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 60 = 6.0/10)
# RADARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected movie (default: 60)
# RADARR_DISCOVERY_SEED_LIBRARIES — Emby library names to draw seeds from (default: ("Movies"))
# RADARR_DISCOVERY_HISTORY — history/state file path
# Playback as Intent Signal
# Recently watched movies are a stronger signal than what is in the library or
# on watchlists. The scoring model weights demonstrated viewing behaviour —
# recency, rating, vote confidence — over passive ownership.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
# Selective by Design
# 05 adds per run is the target, not bulk imports. A score threshold combined
# with MAX_ADDS ensures only high-confidence recommendations are acted on.
# Volume is not the goal — meaningful discovery is.
#
# playback_aware_radarr_discovery.sh — normal run
# playback_aware_radarr_discovery.sh --dry-run — score and rank, no Radarr changes
# playback_aware_radarr_discovery.sh --log — verbose output
# playback_aware_radarr_discovery.sh --status — show config and exit
#
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
# Two-Stage Filtering
# Stage 1 rejects weak seeds before they drive Stage 2. Low-quality or
# low-confidence watched movies produce poor recommendations. Filtering at
# the seed stage improves the entire output.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
@@ -111,6 +105,31 @@
# runs. Safe to delete — next run starts fresh with no memory.
#
# ==============================================================================================
# CONFIGURATION (master.conf)
# ==============================================================================================
#
# RADARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
# RADARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 30)
# RADARR_DISCOVERY_MAX_SEEDS — max seed movies from Stage 1 (default: 5)
# RADARR_DISCOVERY_MAX_ADDS — max movies to add per run (default: 5)
# RADARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 100)
# RADARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 60 = 6.0/10)
# RADARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected movie (default: 60)
# RADARR_DISCOVERY_SEED_LIBRARIES — Emby library names to draw seeds from (default: ("Movies"))
# RADARR_DISCOVERY_HISTORY — history/state file path
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# playback_aware_radarr_discovery.sh — normal run
# playback_aware_radarr_discovery.sh --dry-run — score and rank, no Radarr changes
# playback_aware_radarr_discovery.sh --log — verbose output
# playback_aware_radarr_discovery.sh --status — show config and exit
#
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
#
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+40 -21
View File
@@ -66,31 +66,23 @@
# Free key at: https://www.themoviedb.org/settings/api
#
# ==============================================================================================
# CONFIGURATION (master.conf)
# DESIGN PRINCIPLES
# ==============================================================================================
#
# SONARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
# SONARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 14)
# SONARR_DISCOVERY_MAX_SEEDS — max seed series from Stage 1 (default: 5)
# SONARR_DISCOVERY_MAX_ADDS — max shows to add per run (default: 3)
# SONARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 50)
# SONARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 65 = 6.5/10)
# SONARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected show (default: 60)
# SONARR_DISCOVERY_USER_EPISODE_CAP — max episodes per user in seed scoring (default: 8)
# SONARR_DISCOVERY_MONITOR_MODE — Sonarr monitor mode on add: "all" or "future" (default: "all")
# SONARR_EMBY_LIBRARIES — Emby library names to draw seeds from
# SONARR_DISCOVERY_HISTORY — history/state file path
# Playback as Intent Signal
# Recently watched episodes are a stronger signal than what is in the library.
# User diversity across a series is weighted above a single user binge —
# broad household interest is a better predictor of a good addition than
# one person's session.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
# Selective by Design
# 03 adds per run is the target. TV is a larger commitment than movies —
# a lower MAX_ADDS cap reflects that. Volume is not the goal.
#
# playback_aware_sonarr_discovery.sh — normal run
# playback_aware_sonarr_discovery.sh --dry-run — score and rank, no Sonarr changes
# playback_aware_sonarr_discovery.sh --log — verbose output
# playback_aware_sonarr_discovery.sh --status — show config and exit
#
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
# Two-Stage Filtering
# Stage 1 rejects weak seeds before they drive Stage 2. A poorly-watched
# or niche series produces poor recommendations. Filtering at the seed
# stage improves the entire output.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
@@ -125,6 +117,33 @@
# runs. Safe to delete — next run starts fresh with no memory.
#
# ==============================================================================================
# CONFIGURATION (master.conf)
# ==============================================================================================
#
# SONARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
# SONARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 14)
# SONARR_DISCOVERY_MAX_SEEDS — max seed series from Stage 1 (default: 5)
# SONARR_DISCOVERY_MAX_ADDS — max shows to add per run (default: 3)
# SONARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 50)
# SONARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 65 = 6.5/10)
# SONARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected show (default: 60)
# SONARR_DISCOVERY_USER_EPISODE_CAP — max episodes per user in seed scoring (default: 8)
# SONARR_DISCOVERY_MONITOR_MODE — Sonarr monitor mode on add: "all" or "future" (default: "all")
# SONARR_EMBY_LIBRARIES — Emby library names to draw seeds from
# SONARR_DISCOVERY_HISTORY — history/state file path
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# playback_aware_sonarr_discovery.sh — normal run
# playback_aware_sonarr_discovery.sh --dry-run — score and rank, no Sonarr changes
# playback_aware_sonarr_discovery.sh --log — verbose output
# playback_aware_sonarr_discovery.sh --status — show config and exit
#
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
#
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+19
View File
@@ -31,6 +31,25 @@
# Emby removes ghost entries immediately — no user-facing file-not-found errors.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# API as Ground Truth
# What Radarr tracks is authoritative. Files not in the API response are
# orphans — Radarr has no record of them and they serve no purpose.
# The script never infers ownership from directory structure alone.
#
# Age Gate Before Deletion
# Files under RADARR_ORPHAN_AGE are left alone regardless of tracked status.
# Radarr's import pipeline writes files before registering them — acting
# immediately would delete files mid-import.
#
# Emby Cleanup Is Part of the Job
# Deleting a file without telling Emby leaves ghost entries that show as
# broken items. Triggering the Emby clean is not optional — it completes
# the deletion from the user's perspective.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+19
View File
@@ -24,6 +24,25 @@
# Per-deletion output is always visible — deletions are never silently swallowed.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Health Error Hygiene
# status="deleted" entries can never be monitored or downloaded — they only
# generate persistent health errors. Removing them is maintenance, not
# data loss: the content never existed on disk for most of these entries.
#
# Conservative File Handling
# Files are not deleted by default because most TMDb-removed entries are
# announced-but-never-released films with no files. The --delete-files flag
# is an explicit opt-in, not the default path.
#
# Exclusion List Prevents Re-add
# Removed entries are added to Radarr's import exclusion list by default.
# Without this, the same deleted entry can be re-added by lists or searches
# and immediately generate the same health error again.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+19
View File
@@ -31,6 +31,25 @@
# Emby removes ghost entries immediately — no user-facing file-not-found errors.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# API as Ground Truth
# What Sonarr tracks is authoritative. Files not in the API response are
# orphans — Sonarr has no record of them and they serve no purpose.
# The script never infers ownership from directory structure alone.
#
# Age Gate Before Deletion
# Files under SONARR_ORPHAN_AGE are left alone regardless of tracked status.
# Sonarr's import pipeline writes files before registering them — acting
# immediately would delete files mid-import.
#
# Emby Cleanup Is Part of the Job
# Deleting a file without telling Emby leaves ghost entries that show as
# broken items. Triggering the Emby clean is not optional — it completes
# the deletion from the user's perspective.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+19
View File
@@ -23,6 +23,25 @@
# Per-deletion output is always visible — deletions are never silently swallowed.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Health Error Hygiene
# status="deleted" series can never be monitored or downloaded — they only
# generate persistent health errors. Removing them is maintenance, not
# data loss: most have no associated files.
#
# Conservative File Handling
# Files are not deleted by default. A TVDB-removed series may still have
# episodes on disk that the user wants to keep. The --delete-files flag
# is an explicit opt-in, not the default path.
#
# Exclusion List Prevents Re-add
# Removed entries are added to Sonarr's import exclusion list by default.
# Without this, the same deleted series can be re-added by lists or searches
# and immediately generate the same health error again.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
+61
View File
@@ -23,6 +23,67 @@
# If WEBHOOK_PORT is 0: exits cleanly (disables the listener).
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# 1. Check WEBHOOK_PORT — exit cleanly if 0 (listener disabled)
# 2. Check WEBHOOK_SECRET — generate and persist one if empty
# 3. exec node webhook_listener.js — replaces this process; PID stays the same
#
# exec is intentional: array_started.sh tracks the PID of this script to check
# whether the listener is running. exec preserves that PID across the hand-off
# to node.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Node.js Over php -S
# php -S on Unraid PHP 8.4 silently drops POST request bodies — webhooks arrive
# empty and the handler has no payload to act on. Node.js handles POST bodies
# correctly and has no equivalent silent-drop behaviour.
#
# Runs Outside nginx
# The listener binds directly to WEBHOOK_PORT — no nginx proxy, no session auth.
# The shared secret in the URL query string is the only gate. This keeps the
# webhook path independent of the auth stack.
#
# exec Preserves PID
# The script execs into node rather than forking it. array_started.sh stores the
# PID of this script to check liveness — exec ensures that PID continues to
# refer to the running node process after the hand-off.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# WEBHOOK_PORT=0 gate — exits cleanly before any setup if the listener is disabled
# Secret auto-generate — WEBHOOK_SECRET generated via openssl rand if empty;
# persisted to master.conf immediately so restarts reuse it
# Shared secret gate — webhook URL must include ?key=<WEBHOOK_SECRET>;
# requests without a valid key are rejected by the Node.js server
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# master.conf
#
# WEBHOOK_PORT — port the listener binds to; 0 = disabled
# WEBHOOK_SECRET — shared secret for URL auth; auto-generated if empty
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# start_webhook_listener.sh
# Started automatically at array start via ARRAY_START_SCRIPTS.
# Exits immediately if WEBHOOK_PORT=0.
#
# To stop:
# pkill -f webhook_listener.js
#
# ==============================================================================================
set -uo pipefail
+51 -2
View File
@@ -15,7 +15,56 @@
# begin searching — a search it will never win because we already have it.
#
# ==============================================================================================
# USAGE
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Closes the Propagation Window
# arr_sync runs every 4 hours. Without this handler, a remote node that already
# has the old version sees the upgrade tagged in arr_sync but the new file not
# yet on disk, and initiates a redundant quality search — a search it will never
# win because this host already has the file. Immediate push eliminates that window.
#
# Rescan as Ground Truth
# Pushing the file is not enough — the remote arr must also be told the file
# exists. Triggering a rescan makes the remote accept the pushed file as the
# current version without starting a new search.
#
# Cache-First API Key Lookup
# Remote arr API keys are read from conf if cached; otherwise fetched via SSH
# from the remote's config.xml. This avoids storing secrets redundantly while
# keeping API calls fast on hosts where the key is already known.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Arg validation — exits with usage message if arr_type or item_path missing
# Path existence — exits if item_path is not a directory on disk
# Tailscale resolution — skips a node if its Tailscale IP cannot be resolved
# rsync exit check — rescan is only triggered if rsync succeeded; a failed
# transfer does not cause the remote arr to scan a partial file
# SSH fallback — if no cached API key, falls back to SSH to read config.xml
# on the remote rather than failing the rescan step
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
#
# HOST* — host names used to discover remote nodes
# HOST*_SONARR_API_KEY — cached API key for direct HTTP rescan (optional)
# HOST*_RADARR_API_KEY — cached API key for direct HTTP rescan (optional)
# HOST*_LIDARR_API_KEY — cached API key for direct HTTP rescan (optional)
#
# master.conf
#
# SSH_KEY — SSH key path for rsync and SSH fallback
# ARR_SYNC_CONNECT_TIMEOUT — SSH connect timeout in seconds
# DOCKER_APPDATA_BASE — base path for reading arr config.xml on remote
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# upgrade_webhook_handler.sh <arr_type> <item_path>
@@ -25,7 +74,7 @@
# (series.path from Sonarr, movie.folderPath from Radarr,
# artist.path from Lidarr)
#
# Called by webhook.php — not intended for direct invocation outside testing.
# Called by webhook_listener.js — not intended for direct invocation outside testing.
#
# ==============================================================================================