feat: arr-native distributed media sync + config architecture fixes

Arr sync (new):
- Media/arr_sync.sh — full mesh bidirectional sync across all HOST* nodes
  - Lidarr (MusicBrainz), Sonarr (TVDB), Radarr (TMDB) all handled in one script
  - Remote API keys read live from config.xml via SSH — never stored in conf files
  - Shared blocklist (DATA_DIR/arr_sync_blocklist.tsv) merged from all nodes at runtime
  - Graceful skip if arr not configured locally or not reachable on a remote node
  - --blocklist-add / --blocklist-remove / --blocklist-list management flags
- daily_sync_maintenance.sh — arr sync runs as explicit phase before rsync
- partnership_onboard.sh — Step 3 bootstraps merged library on both sides at onboard
- master.conf — ARR_SYNC_* config block, DOCKER_APPDATA_BASE

Rsync / cleanup:
- DEFAULT_RSYNC_OPTS — removed --delete; arr_cleanup.sh owns orphan enforcement
- lidarr_cleanup.sh — removed HOST1-only guard; runs on any node with Lidarr configured

Config architecture:
- HOST1/HOST2 hostnames moved from master_host*.conf → master.conf (not credentials)
- Sparse checkout now works correctly: each server only needs its own host conf
- detect_hosts() still resolves MY_ID + REMOTE_ID via master.conf hostname values

Bug fix:
- common.sh line 493 — watchdog toggle eval had broken quoting; all SYS_WATCHDOG_CHECK_*
  globals were silently set to empty instead of their configured values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gmer4Lfe
2026-05-09 09:53:40 -04:00
co-authored by Claude Sonnet 4.6
parent 009820e981
commit b65f572367
21 changed files with 1548 additions and 272 deletions
+9 -13
View File
@@ -43,9 +43,8 @@
# The user accepts full responsibility — this is 100% intentional by design.
#
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
# Lidarr runs on HOST1 only — music library is HOST1's source of truth.
# detect_hosts() sets MY_ID — if MY_ID != HOST1 script exits cleanly with no action.
# LIDARR_URL, LIDARR_API_KEY, LIDARR_MUSIC_ROOT aliased by detect_hosts() automatically.
# Runs on any node where Lidarr is configured — skips cleanly if LIDARR_URL/API_KEY not set.
# detect_hosts() aliases LIDARR_URL, LIDARR_API_KEY, LIDARR_MUSIC_ROOT from HOST*_LIDARR_*.
#
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
# acquire_lock "wait" — large scans take time, wait for previous run to finish
@@ -155,9 +154,9 @@ acquire_lock "wait"
# detect_hosts() sets MY_ID and aliases LIDARR_URL, LIDARR_API_KEY, LIDARR_MUSIC_ROOT
detect_hosts
# Lidarr is HOST1 only — exit cleanly on any other host
if [[ "$MY_ID" != "HOST1" ]]; then
log "Lidarr runs on HOST1 only — skipping on $MY_ID ($LOCAL_SERVER_NAME)"
# Skip if Lidarr is not configured on this host
if [[ -z "${LIDARR_URL:-}" ]] || [[ -z "${LIDARR_API_KEY:-}" ]]; then
log "Lidarr not configured on $MY_ID ($LOCAL_SERVER_NAME) — skipping"
exit 0
fi
@@ -182,8 +181,7 @@ if [[ ! -d "$LIDARR_MUSIC_ROOT" ]]; then
exit 1
fi
log "Lidarr URL: $LIDARR_URL"
log "Music root: $LIDARR_MUSIC_ROOT"
echo " $MY_ID ($LOCAL_SERVER_NAME) — $LIDARR_URL"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be deleted"
[[ "$I_KNOW" == true ]] && warn "OVERRIDE — --i-know-what-im-doing active"
@@ -310,7 +308,7 @@ fi
# Safety Layer 3 — API version check
check_arr_version "$LIDARR_URL" "$LIDARR_API_KEY" "v1" "$LIDARR_VERSION_MAJOR" "Lidarr" || exit 1
log "Querying Lidarr API: $LIDARR_URL"
echo " Querying Lidarr API..."
# Fetch all artists
ARTIST_RESPONSE=$(lidarr_api "artist") || {
@@ -371,7 +369,7 @@ if [[ "$TRACKED_COUNT" -eq 0 ]]; then
exit 1
fi
warn "Lidarr tracks $TRACKED_COUNT files across $ARTIST_COUNT artists"
echo " $ARTIST_COUNT artists | $TRACKED_COUNT tracked files"
# Safety Layer 6 — percentage drop vs last known count
if [[ -f "$LIDARR_TRACKED_COUNT_FILE" ]]; then
@@ -399,9 +397,7 @@ echo "$TRACKED_COUNT" > "$LIDARR_TRACKED_COUNT_FILE"
# ==============================================================================================
echo ""
echo "━━━ $ICON_CLEAN Scanning Music Root ━━━"
log "Root: $LIDARR_MUSIC_ROOT"
log "Orphan age: ${LIDARR_ORPHAN_AGE} days"
log "Protected: ${LIDARR_PROTECTED_PATTERNS[*]}"
echo " Root: $LIDARR_MUSIC_ROOT | Orphan age: ${LIDARR_ORPHAN_AGE} days"
echo ""
START=$(date +%s)