Add --merge-run to rsync and --force to arr_sync for authoritative node compliance
This commit is contained in:
+108
-37
@@ -124,6 +124,7 @@ BLOCKLIST_ACTION=""
|
||||
BLOCKLIST_ARR=""
|
||||
BLOCKLIST_ID=""
|
||||
BLOCKLIST_REASON=""
|
||||
FORCE_MODE=false
|
||||
FILTERED_ARGS=()
|
||||
_skip_next=false
|
||||
for _arg in "$@"; do
|
||||
@@ -132,6 +133,7 @@ for _arg in "$@"; do
|
||||
--blocklist-add) BLOCKLIST_ACTION="add" ;;
|
||||
--blocklist-remove) BLOCKLIST_ACTION="remove" ;;
|
||||
--blocklist-list) BLOCKLIST_ACTION="list" ;;
|
||||
--force) FORCE_MODE=true ;;
|
||||
*) FILTERED_ARGS+=("$_arg") ;;
|
||||
esac
|
||||
done
|
||||
@@ -606,6 +608,56 @@ curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
REMOTE
|
||||
}
|
||||
|
||||
# Trigger a full library rescan on a remote arr — used after merge-run to force the arr
|
||||
# to accept what is now on disk as ground truth rather than chasing stale file versions.
|
||||
# Args: arr_type node_id node_ip port api_ver
|
||||
_trigger_rescan() {
|
||||
local arr_type="$1" node_id="$2" node_ip="$3" port="$4" api_ver="$5"
|
||||
local command_name
|
||||
case "$arr_type" in
|
||||
sonarr) command_name="RefreshSeries" ;;
|
||||
radarr) command_name="RefreshMovie" ;;
|
||||
lidarr) command_name="RefreshArtist" ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
|
||||
local encoded
|
||||
encoded=$(printf '{"name":"%s"}' "$command_name" | base64 -w0)
|
||||
local _kvar="${node_id}_${arr_type^^}_API_KEY"
|
||||
local cached_key="${!_kvar:-}"
|
||||
|
||||
local http_code
|
||||
if [[ -n "$cached_key" ]]; then
|
||||
local body
|
||||
body=$(printf '%s' "$encoded" | base64 -d)
|
||||
http_code=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "X-Api-Key: $cached_key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$body" \
|
||||
"http://${node_ip}:${port}/api/${api_ver}/command" 2>/dev/null)
|
||||
else
|
||||
local config_xml="${DOCKER_APPDATA_BASE}/${arr_type^}/config.xml"
|
||||
http_code=$(ssh -i "$SSH_KEY" -o ConnectTimeout="$ARR_SYNC_CONNECT_TIMEOUT" \
|
||||
root@"$node_ip" bash <<REMOTE 2>/dev/null
|
||||
KEY=\$(grep -oP '(?<=<ApiKey>)[^<]+' '${config_xml}' 2>/dev/null)
|
||||
[[ -z "\$KEY" ]] && exit 1
|
||||
BODY=\$(printf '%s' '${encoded}' | base64 -d)
|
||||
curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "X-Api-Key: \$KEY" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "\$BODY" \
|
||||
"http://localhost:${port}/api/${api_ver}/command"
|
||||
REMOTE
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "$http_code" == "201" || "$http_code" == "200" ]]; then
|
||||
log "${arr_type^}: triggered ${command_name} on ${node_id}"
|
||||
else
|
||||
warn "${arr_type^}: failed to trigger ${command_name} on ${node_id} (HTTP ${http_code:-timeout})"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── LOCAL HELPERS ─────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
@@ -841,45 +893,50 @@ _sync_arr() {
|
||||
log "${arr_type^}: $remote_count items on $node_name"
|
||||
|
||||
# ── Remote → Local: items on remote not in local ───────────────────────────────────────
|
||||
local to_add_local=()
|
||||
for stable_id in "${!remote_ids[@]}"; do
|
||||
[[ -n "${local_ids[$stable_id]:-}" ]] && continue
|
||||
if _is_blocklisted "$arr_type" "$stable_id"; then
|
||||
log "BLOCKLISTED [$arr_type] $stable_id — skipping"
|
||||
(( total_skipped++ ))
|
||||
continue
|
||||
fi
|
||||
to_add_local+=("$stable_id")
|
||||
done
|
||||
# Skipped in force mode — local arr is authoritative; remote tracking does not propagate back.
|
||||
if [[ "$FORCE_MODE" == false ]]; then
|
||||
local to_add_local=()
|
||||
for stable_id in "${!remote_ids[@]}"; do
|
||||
[[ -n "${local_ids[$stable_id]:-}" ]] && continue
|
||||
if _is_blocklisted "$arr_type" "$stable_id"; then
|
||||
log "BLOCKLISTED [$arr_type] $stable_id — skipping"
|
||||
(( total_skipped++ ))
|
||||
continue
|
||||
fi
|
||||
to_add_local+=("$stable_id")
|
||||
done
|
||||
|
||||
if [[ "${#to_add_local[@]}" -gt 0 ]]; then
|
||||
local local_defs
|
||||
local_defs=$(_local_defaults "$local_url" "$local_key" "$api_ver" "$arr_type")
|
||||
if [[ -z "$local_defs" ]]; then
|
||||
warn "${arr_type^}: could not fetch local defaults — skipping adds from $node_name"
|
||||
else
|
||||
for stable_id in "${to_add_local[@]}"; do
|
||||
IFS='|' read -r display_name _ <<< "${remote_ids[$stable_id]}"
|
||||
local payload
|
||||
payload=$(_build_payload "$arr_type" "$stable_id" "$display_name" \
|
||||
"true" "$local_defs")
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "DRY RUN: would add to local ${arr_type^}: $display_name ($stable_id)"
|
||||
(( total_added_local++ ))
|
||||
else
|
||||
local http_code
|
||||
http_code=$(_local_add "$local_url" "$local_key" "$api_ver" \
|
||||
"$endpoint" "$payload")
|
||||
if [[ "$http_code" == "201" ]] || [[ "$http_code" == "200" ]]; then
|
||||
log "Added to local ${arr_type^}: $display_name"
|
||||
local_ids["$stable_id"]="${display_name}|true"
|
||||
if [[ "${#to_add_local[@]}" -gt 0 ]]; then
|
||||
local local_defs
|
||||
local_defs=$(_local_defaults "$local_url" "$local_key" "$api_ver" "$arr_type")
|
||||
if [[ -z "$local_defs" ]]; then
|
||||
warn "${arr_type^}: could not fetch local defaults — skipping adds from $node_name"
|
||||
else
|
||||
for stable_id in "${to_add_local[@]}"; do
|
||||
IFS='|' read -r display_name _ <<< "${remote_ids[$stable_id]}"
|
||||
local payload
|
||||
payload=$(_build_payload "$arr_type" "$stable_id" "$display_name" \
|
||||
"true" "$local_defs")
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "DRY RUN: would add to local ${arr_type^}: $display_name ($stable_id)"
|
||||
(( total_added_local++ ))
|
||||
else
|
||||
warn "Failed to add to local ${arr_type^}: $display_name (HTTP $http_code)"
|
||||
local http_code
|
||||
http_code=$(_local_add "$local_url" "$local_key" "$api_ver" \
|
||||
"$endpoint" "$payload")
|
||||
if [[ "$http_code" == "201" ]] || [[ "$http_code" == "200" ]]; then
|
||||
log "Added to local ${arr_type^}: $display_name"
|
||||
local_ids["$stable_id"]="${display_name}|true"
|
||||
(( total_added_local++ ))
|
||||
else
|
||||
warn "Failed to add to local ${arr_type^}: $display_name (HTTP $http_code)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log "${arr_type^}: force mode — skipping remote→local (local is authoritative)"
|
||||
fi
|
||||
|
||||
# ── Local → Remote: items on local not on remote ───────────────────────────────────────
|
||||
@@ -923,9 +980,22 @@ _sync_arr() {
|
||||
fi
|
||||
fi
|
||||
|
||||
log " $node_name: +${#to_add_local[@]} local | +${#to_add_remote[@]} remote | $total_skipped blocklisted"
|
||||
# ── Force mode: trigger library rescan on remote ──────────────────────────────────────────
|
||||
# After files are settled by merge-run and tracking pushed by force arr-sync, the remote
|
||||
# arr rescans its library so it accepts the authoritative file versions as ground truth.
|
||||
if [[ "$FORCE_MODE" == true ]]; then
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "DRY RUN: would trigger ${arr_type^} rescan on $node_name"
|
||||
else
|
||||
_trigger_rescan "$arr_type" "$node_id" "$node_ip" "$port" "$api_ver"
|
||||
fi
|
||||
fi
|
||||
|
||||
unset remote_ids
|
||||
local _n_local=${#to_add_local[@]:-}; _n_local=${_n_local:-0}
|
||||
local _n_remote=${#to_add_remote[@]:-}; _n_remote=${_n_remote:-0}
|
||||
log " $node_name: +${_n_local} local | +${_n_remote} remote | $total_skipped blocklisted"
|
||||
|
||||
unset remote_ids to_add_local to_add_remote _n_local _n_remote
|
||||
declare -A remote_ids
|
||||
done
|
||||
|
||||
@@ -954,7 +1024,8 @@ echo "━━━━━ $ICON_SUMMARY ARR SYNC SUMMARY ━━━━━"
|
||||
echo "$ICON_HOST Node: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_SYNC Peers: ${REMOTE_NODES[*]}"
|
||||
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes were made"
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes were made"
|
||||
[[ "$FORCE_MODE" == true ]] && echo " Mode: force (local authoritative — remote→local skipped, rescan triggered)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user