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:
+59
-23
@@ -270,6 +270,32 @@ for i in $(seq 0 $(( _srv_count - 1 ))); do
|
||||
done < <(echo "${SRV_USERS[$i]}" | jq -r '.[] | [.Id, .Name] | @tsv' 2>/dev/null)
|
||||
done
|
||||
|
||||
# ── Step 2.5: Pre-build provider ID → item ID lookup map ─────────────────────
|
||||
# AnyProviderIdEquals is broken in Jellyfin 10.11+ (ignores the filter entirely).
|
||||
# Pre-fetching all items' provider IDs once and using a local map avoids the broken
|
||||
# per-item API call and is faster overall.
|
||||
declare -A PROV_LOOKUP # "si|tvdb.{id}" | "si|imdb.{id}" | "si|mb.{id}" → item_id
|
||||
|
||||
for _psi in $(seq 0 $(( _srv_count - 1 ))); do
|
||||
[[ -z "${SRV_URL[$_psi]}" ]] && continue
|
||||
log "Building provider ID map for ${SRV_NAME[$_psi]}..."
|
||||
_raw_prov=$(_api_get "${SRV_URL[$_psi]}" "${SRV_KEY[$_psi]}" \
|
||||
"Items?Recursive=true&IncludeItemTypes=${SYNC_TYPES}&Fields=ProviderIds&ExcludeLocationTypes=Virtual" 2>/dev/null)
|
||||
[[ -z "$_raw_prov" ]] && continue
|
||||
while IFS=$'\t' read -r _pid _ptvdb _pimdb _ptmdb _pmbtrack; do
|
||||
[[ "$_ptvdb" != "null" && -n "$_ptvdb" ]] && PROV_LOOKUP["${_psi}|tvdb.${_ptvdb}"]="$_pid"
|
||||
[[ "$_pimdb" != "null" && -n "$_pimdb" ]] && PROV_LOOKUP["${_psi}|imdb.${_pimdb}"]="$_pid"
|
||||
[[ "$_ptmdb" != "null" && -n "$_ptmdb" ]] && PROV_LOOKUP["${_psi}|tmdb.${_ptmdb}"]="$_pid"
|
||||
[[ "$_pmbtrack" != "null" && -n "$_pmbtrack" ]] && PROV_LOOKUP["${_psi}|mb.${_pmbtrack}"]="$_pid"
|
||||
done < <(echo "$_raw_prov" | jq -r '.Items[] | [
|
||||
.Id,
|
||||
(.ProviderIds.Tvdb // "null"),
|
||||
(.ProviderIds.Imdb // "null"),
|
||||
(.ProviderIds.Tmdb // "null"),
|
||||
(.ProviderIds.MusicBrainzTrackId // "null")
|
||||
] | @tsv' 2>/dev/null)
|
||||
done
|
||||
|
||||
# ── Step 3: Sync per matched user ────────────────────────────────────────────
|
||||
_date_filter=""
|
||||
if [[ "$SYNC_DAYS" -gt 0 ]]; then
|
||||
@@ -386,23 +412,28 @@ for lname in "${!USER_MAP[@]}"; do
|
||||
[[ "$_has_entries" == false ]] && continue
|
||||
|
||||
# Find the authoritative server: newest LastPlayedDate epoch
|
||||
# Tie-break: higher PlayCount, then higher Ticks
|
||||
# Tie-break: higher PlayCount, then higher Ticks, then Played=true
|
||||
# Init at -1 so servers with epoch=0 (batch-marks with null LastPlayedDate) can win
|
||||
_auth_si=""
|
||||
_auth_epoch=0
|
||||
_auth_pcount=0
|
||||
_auth_ticks=0
|
||||
_auth_epoch=-1
|
||||
_auth_pcount=-1
|
||||
_auth_ticks=-1
|
||||
_auth_pf="false"
|
||||
|
||||
for _si in "${!E_SIDX[@]}"; do
|
||||
_e="${E_EPOCH[$_si]:-0}"
|
||||
_pc="${E_PCOUNT[$_si]:-0}"
|
||||
_tk="${E_TICKS[$_si]:-0}"
|
||||
_pf="${E_PLAYED[$_si]:-false}"
|
||||
if [[ "$_e" -gt "$_auth_epoch" ]] || \
|
||||
[[ "$_e" -eq "$_auth_epoch" && "$_pc" -gt "$_auth_pcount" ]] || \
|
||||
[[ "$_e" -eq "$_auth_epoch" && "$_pc" -eq "$_auth_pcount" && "$_tk" -gt "$_auth_ticks" ]]; then
|
||||
[[ "$_e" -eq "$_auth_epoch" && "$_pc" -eq "$_auth_pcount" && "$_tk" -gt "$_auth_ticks" ]] || \
|
||||
[[ "$_e" -eq "$_auth_epoch" && "$_pc" -eq "$_auth_pcount" && "$_tk" -eq "$_auth_ticks" && "$_pf" == "true" && "$_auth_pf" != "true" ]]; then
|
||||
_auth_si="$_si"
|
||||
_auth_epoch="$_e"
|
||||
_auth_pcount="$_pc"
|
||||
_auth_ticks="$_tk"
|
||||
_auth_pf="$_pf"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -422,12 +453,16 @@ for lname in "${!USER_MAP[@]}"; do
|
||||
# Skip if they already have the same/newer state
|
||||
_skip=false
|
||||
if [[ "$_auth_played" == "true" ]]; then
|
||||
# Played: skip if target is already marked played with same/newer date
|
||||
[[ "$_their_epoch" -ge "$_auth_epoch" && "$_their_played" == "true" ]] && _skip=true
|
||||
# Both servers already have this played — nothing to propagate regardless of dates.
|
||||
# Date-based comparison caused a ping-pong: syncing without a DatePlayed param lets
|
||||
# the target server stamp the current time, making it the new authority next cycle.
|
||||
[[ "$_their_played" == "true" ]] && _skip=true
|
||||
else
|
||||
# Resume only: skip if target already has same or more ticks
|
||||
# (posting ticks via /UserData doesn't set LastPlayedDate, so epoch comparison is useless here)
|
||||
[[ "${E_TICKS[$_si]:-0}" -ge "${_auth_ticks:-0}" && "$_their_played" == "false" ]] && _skip=true
|
||||
# Resume only: skip if target already has same or more ticks.
|
||||
# Allow 5-second tolerance (50_000_000 ticks) — Emby may round tick values slightly
|
||||
# differently on read, causing an exact-match check to miss and re-sync every cycle.
|
||||
_tick_gap=$(( ${_auth_ticks:-0} - ${E_TICKS[$_si]:-0} ))
|
||||
[[ "$_tick_gap" -le 50000000 && "${E_TICKS[$_si]:-0}" -gt 0 && "$_their_played" == "false" ]] && _skip=true
|
||||
fi
|
||||
if [[ "$_skip" == true ]]; then
|
||||
log " SKIP $_pkey → ${SRV_NAME[$_si]} already up to date"
|
||||
@@ -441,24 +476,25 @@ for lname in "${!USER_MAP[@]}"; do
|
||||
# Find item ID on target server by provider key if not in our map
|
||||
if [[ -z "$_iid" ]]; then
|
||||
_ptype="${_pkey%%:*}"
|
||||
_pval="${_pkey##*:}"
|
||||
case "$_ptype" in
|
||||
imdb) _search_field="imdb.${_pval}" ;;
|
||||
tmdb) _search_field="tmdb.${_pval##movie:}" ;;
|
||||
imdb)
|
||||
_pval="${_pkey#imdb:}"
|
||||
_iid="${PROV_LOOKUP[${_si}|imdb.${_pval}]:-}"
|
||||
;;
|
||||
tmdb)
|
||||
_pval="${_pkey#tmdb:movie:}"
|
||||
_iid="${PROV_LOOKUP[${_si}|tmdb.${_pval}]:-}"
|
||||
;;
|
||||
tvdb)
|
||||
# _pkey format: tvdb:ep:{tvdb_id}:s{season}e{ep}
|
||||
# ##*: gives "s7e2" (wrong); strip prefix then first :
|
||||
# pkey format: tvdb:ep:{tvdb_id}:s{season}e{ep}
|
||||
_tvdb_num="${_pkey#tvdb:ep:}"; _tvdb_num="${_tvdb_num%%:*}"
|
||||
_search_field="tvdb.${_tvdb_num}"
|
||||
_iid="${PROV_LOOKUP[${_si}|tvdb.${_tvdb_num}]:-}"
|
||||
;;
|
||||
mb)
|
||||
_pval="${_pkey#mb:track:}"
|
||||
_iid="${PROV_LOOKUP[${_si}|mb.${_pval}]:-}"
|
||||
;;
|
||||
mb) _search_field="" ;; # skip music if not found
|
||||
esac
|
||||
|
||||
if [[ -n "$_search_field" ]]; then
|
||||
_iid=$(_api_get "${SRV_URL[$_si]}" "${SRV_KEY[$_si]}" \
|
||||
"Items?AnyProviderIdEquals=${_search_field}&Recursive=true&Fields=ProviderIds&ExcludeLocationTypes=Virtual&Limit=1" 2>/dev/null \
|
||||
| jq -r '.Items[0].Id // empty' 2>/dev/null)
|
||||
fi
|
||||
[[ -z "$_iid" ]] && log " SKIP $_pkey → ${SRV_NAME[$_si]} item not found on server" && continue
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user