Add media-server auto-detection — pick Emby or Jellyfin by actual running state
This commit is contained in:
@@ -439,6 +439,9 @@ is_in_list() {
|
|||||||
# EMBY_CONTAINER ← HOST*_EMBY_CONTAINER
|
# EMBY_CONTAINER ← HOST*_EMBY_CONTAINER
|
||||||
# EMBY_URL ← HOST*_EMBY_URL
|
# EMBY_URL ← HOST*_EMBY_URL
|
||||||
# EMBY_API_KEY ← HOST*_EMBY_API_KEY
|
# EMBY_API_KEY ← HOST*_EMBY_API_KEY
|
||||||
|
# JELLYFIN_CONTAINER ← HOST*_JELLYFIN_CONTAINER
|
||||||
|
# JELLYFIN_URL ← HOST*_JELLYFIN_URL
|
||||||
|
# JELLYFIN_API_KEY ← HOST*_JELLYFIN_API_KEY
|
||||||
# GITEA_API_TOKEN ← HOST*_GITEA_API_TOKEN
|
# GITEA_API_TOKEN ← HOST*_GITEA_API_TOKEN
|
||||||
#
|
#
|
||||||
# Arr-specific vars set by detect_hosts() when needed by arr scripts:
|
# Arr-specific vars set by detect_hosts() when needed by arr scripts:
|
||||||
@@ -531,6 +534,13 @@ detect_hosts() {
|
|||||||
EMBY_API_KEY_VAR="${MY_ID}_EMBY_API_KEY"
|
EMBY_API_KEY_VAR="${MY_ID}_EMBY_API_KEY"
|
||||||
EMBY_API_KEY="${!EMBY_API_KEY_VAR:-}"
|
EMBY_API_KEY="${!EMBY_API_KEY_VAR:-}"
|
||||||
|
|
||||||
|
JELLYFIN_CONTAINER_VAR="${MY_ID}_JELLYFIN_CONTAINER"
|
||||||
|
JELLYFIN_CONTAINER="${!JELLYFIN_CONTAINER_VAR:-}"
|
||||||
|
JELLYFIN_URL_VAR="${MY_ID}_JELLYFIN_URL"
|
||||||
|
JELLYFIN_URL="${!JELLYFIN_URL_VAR:-}"
|
||||||
|
JELLYFIN_API_KEY_VAR="${MY_ID}_JELLYFIN_API_KEY"
|
||||||
|
JELLYFIN_API_KEY="${!JELLYFIN_API_KEY_VAR:-}"
|
||||||
|
|
||||||
GITEA_API_TOKEN_VAR="${MY_ID}_GITEA_API_TOKEN"
|
GITEA_API_TOKEN_VAR="${MY_ID}_GITEA_API_TOKEN"
|
||||||
GITEA_API_TOKEN="${!GITEA_API_TOKEN_VAR:-}"
|
GITEA_API_TOKEN="${!GITEA_API_TOKEN_VAR:-}"
|
||||||
|
|
||||||
@@ -1363,6 +1373,77 @@ emby_api() {
|
|||||||
echo "$body"
|
echo "$body"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Media Server Detection ────────────────────────────────────────────────────
|
||||||
|
# Picks whichever media server is actually running right now, so callers don't have to
|
||||||
|
# hardcode Emby or Jellyfin. Emby is preferred when both are up (it's the primary player on
|
||||||
|
# every host; Jellyfin is mainly kept around for its working ffprobe binary — see
|
||||||
|
# HOST*_FFPROBE_CONTAINER). Either container being merely *configured* in host*.conf doesn't
|
||||||
|
# count — this checks Docker's actual running state, so a host with Jellyfin installed but
|
||||||
|
# stopped correctly falls through to Emby (or to nothing, if neither is up).
|
||||||
|
#
|
||||||
|
# Requires detect_hosts() to have already run (needs EMBY_*/JELLYFIN_* aliases).
|
||||||
|
# Sets: MEDIA_SERVER ("Emby"|"Jellyfin"|""), MEDIA_SERVER_URL, MEDIA_SERVER_API_KEY.
|
||||||
|
# Returns 1 and leaves MEDIA_SERVER="" if neither configured container is running.
|
||||||
|
# Usage: detect_media_server [docker_timeout_seconds]
|
||||||
|
detect_media_server() {
|
||||||
|
local docker_timeout="${1:-10}"
|
||||||
|
MEDIA_SERVER="" MEDIA_SERVER_URL="" MEDIA_SERVER_API_KEY=""
|
||||||
|
|
||||||
|
local emby_up=false jellyfin_up=false
|
||||||
|
|
||||||
|
if [[ -n "${EMBY_CONTAINER:-}" ]]; then
|
||||||
|
[[ "$(timeout "$docker_timeout" docker inspect -f '{{.State.Running}}' \
|
||||||
|
"$EMBY_CONTAINER" 2>/dev/null)" == "true" ]] && emby_up=true
|
||||||
|
fi
|
||||||
|
if [[ -n "${JELLYFIN_CONTAINER:-}" ]]; then
|
||||||
|
[[ "$(timeout "$docker_timeout" docker inspect -f '{{.State.Running}}' \
|
||||||
|
"$JELLYFIN_CONTAINER" 2>/dev/null)" == "true" ]] && jellyfin_up=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$emby_up" == true && "$jellyfin_up" == true ]]; then
|
||||||
|
log "Both Emby and Jellyfin are running — using Emby (primary)"
|
||||||
|
elif [[ "$emby_up" == true ]]; then
|
||||||
|
log "Emby is running, Jellyfin is not — using Emby"
|
||||||
|
elif [[ "$jellyfin_up" == true ]]; then
|
||||||
|
log "Jellyfin is running, Emby is not — using Jellyfin"
|
||||||
|
else
|
||||||
|
warn "Neither Emby nor Jellyfin is running — no media server available"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$emby_up" == true ]]; then
|
||||||
|
MEDIA_SERVER="Emby"
|
||||||
|
MEDIA_SERVER_URL="$EMBY_URL"
|
||||||
|
MEDIA_SERVER_API_KEY="$EMBY_API_KEY"
|
||||||
|
else
|
||||||
|
MEDIA_SERVER="Jellyfin"
|
||||||
|
MEDIA_SERVER_URL="$JELLYFIN_URL"
|
||||||
|
MEDIA_SERVER_API_KEY="$JELLYFIN_API_KEY"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# General REST call against whichever server detect_media_server() picked. Jellyfin accepts
|
||||||
|
# the same X-Emby-Token header Emby uses (inherited from its Emby-fork lineage — confirmed
|
||||||
|
# live 2026-07-19), so one call shape covers both backends.
|
||||||
|
# Usage: detect_media_server && media_server_api <endpoint> [timeout_seconds]
|
||||||
|
# Prints response body on 200; returns 1 on HTTP error, curl failure, or no server detected.
|
||||||
|
media_server_api() {
|
||||||
|
local endpoint="$1" max_time="${2:-30}"
|
||||||
|
if [[ -z "${MEDIA_SERVER:-}" ]]; then
|
||||||
|
error "media_server_api called with no MEDIA_SERVER set — call detect_media_server first"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local response http_code body
|
||||||
|
response=$(curl -sf --max-time "$max_time" \
|
||||||
|
-H "X-Emby-Token: $MEDIA_SERVER_API_KEY" \
|
||||||
|
-w "\n%{http_code}" \
|
||||||
|
"${MEDIA_SERVER_URL}/${endpoint}" 2>/dev/null)
|
||||||
|
http_code=$(echo "$response" | tail -1)
|
||||||
|
body=$(echo "$response" | head -n -1)
|
||||||
|
[[ "$http_code" != "200" ]] && { error "$MEDIA_SERVER API HTTP $http_code: $endpoint"; return 1; }
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
|
||||||
# Stop containers on the REMOTE server via SSH.
|
# Stop containers on the REMOTE server via SSH.
|
||||||
# Reads CRITICAL_CONTAINER_NAMES — set from profile arrays by rsync.sh.
|
# Reads CRITICAL_CONTAINER_NAMES — set from profile arrays by rsync.sh.
|
||||||
# Tracks running state in RUNNING_CONTAINERS for restart after sync.
|
# Tracks running state in RUNNING_CONTAINERS for restart after sync.
|
||||||
|
|||||||
Reference in New Issue
Block a user