495 lines
22 KiB
Bash
495 lines
22 KiB
Bash
#!/bin/bash
|
|
# -----------------------------------------------------------------------------------------------
|
|
# --------------------------------- Transcode Manager ------------------------------------------
|
|
# -----------------------------------------------------------------------------------------------
|
|
# Manages Emby transcode storage using filesystem symlink indirection.
|
|
# ffmpeg resolves the symlink ONCE at session start — existing sessions are never affected.
|
|
# Only new sessions care about where the symlink currently points.
|
|
#
|
|
# Three operating modes — set TRANSCODE_MANAGER_MODE in Master.conf:
|
|
# smart — auto-flips between ramdisk and SSD based on usage thresholds (default)
|
|
# ramdisk — always uses ramdisk, never flips to SSD regardless of usage
|
|
# ssd — always uses SSD, never uses ramdisk
|
|
#
|
|
# Safety checks on every run regardless of mode:
|
|
# Ramdisk disappeared → auto-flip to SSD, notify warning
|
|
# SSD path missing → disable SSD fallback, notify warning
|
|
# Symlink broken → auto-recreate, notify
|
|
# Symlink missing → auto-recreate, notify
|
|
# Permissions drift → fix silently
|
|
# Emby not running → skip threshold checks, verify symlink only
|
|
#
|
|
# Session display shows active Emby streams with per-session storage location —
|
|
# useful during flip events when sessions are split across ramdisk and SSD.
|
|
#
|
|
# All configuration in Master.conf under Transcode Manager section.
|
|
# Supports --dry-run and --status.
|
|
# -----------------------------------------------------------------------------------------------
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
source "$SCRIPT_DIR/../Master.conf"
|
|
source "$SCRIPT_DIR/../common.sh"
|
|
|
|
parse_args "$@"
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# ━━━ $ICON_GEAR Setup ━━━
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR Setup ━━━"
|
|
|
|
if [[ "$EUID" -ne 0 ]]; then
|
|
error "Must be run as root"
|
|
exit 1
|
|
fi
|
|
|
|
success "Running as root"
|
|
|
|
case "$TRANSCODE_MANAGER_MODE" in
|
|
smart|ramdisk|ssd) info "$ICON_GEAR Mode: $TRANSCODE_MANAGER_MODE" ;;
|
|
*)
|
|
warn "Unknown TRANSCODE_MANAGER_MODE: $TRANSCODE_MANAGER_MODE — defaulting to smart"
|
|
TRANSCODE_MANAGER_MODE="smart"
|
|
;;
|
|
esac
|
|
|
|
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# ━━━ $ICON_SUMMARY Status ━━━
|
|
# -----------------------------------------------------------------------------------------------
|
|
if [[ "$SHOW_STATUS" == true ]]; then
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
|
echo "$ICON_GEAR Mode: $TRANSCODE_MANAGER_MODE"
|
|
echo "$ICON_RAM Ramdisk: $RAMDISK_PATH ($RAMDISK_SIZE)"
|
|
echo "$ICON_LINK Symlink: $TRANSCODE_LINK"
|
|
echo "$ICON_DISK SSD: $TRANSCODE_SSD"
|
|
echo "$ICON_RAM Warn at: ${RAMDISK_WARN_GB}GB"
|
|
echo "$ICON_RAM Low at: ${RAMDISK_LOW_GB}GB"
|
|
echo "$ICON_DISK SSD min free: ${RAMDISK_SSD_MIN_GB}GB"
|
|
echo "$ICON_RAM Flip warn: $TRANSCODE_FLIP_WARN per hour"
|
|
echo "$ICON_GEAR Check Emby: $TRANSCODE_CHECK_EMBY ($TRANSCODE_EMBY_CONTAINER)"
|
|
echo "$ICON_GEAR Dry Run: $DRY_RUN"
|
|
CURRENT_TARGET=$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "missing")
|
|
echo "$ICON_LINK Symlink now: $TRANSCODE_LINK → $CURRENT_TARGET"
|
|
if mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
|
|
USED_KB=$(df "$RAMDISK_PATH" --output=used | tail -1 | tr -d ' ')
|
|
USED_GB=$(awk "BEGIN {printf \"%.2f\", $USED_KB / 1048576}")
|
|
echo "$ICON_RAM Ramdisk now: ${USED_GB}GB used"
|
|
else
|
|
echo "$ICON_RAM Ramdisk: NOT MOUNTED"
|
|
fi
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
|
exit 0
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# HELPERS
|
|
# -----------------------------------------------------------------------------------------------
|
|
|
|
flip_symlink() {
|
|
local target="$1" reason="$2"
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would flip symlink to: $target ($reason)"
|
|
return 0
|
|
fi
|
|
ln -sfn "$target" "$TRANSCODE_LINK"
|
|
success "$ICON_LINK Symlink flipped to: $target ($reason)"
|
|
}
|
|
|
|
fix_permissions() {
|
|
local path="$1"
|
|
[[ ! -d "$path" ]] && return
|
|
chown -R "$TRANSCODE_OWNER" "$path" 2>/dev/null
|
|
chmod -R "$TRANSCODE_CHMOD" "$path" 2>/dev/null
|
|
log "Permissions fixed on $path"
|
|
}
|
|
|
|
get_ramdisk_used_gb() {
|
|
local used_kb
|
|
used_kb=$(df "$RAMDISK_PATH" --output=used 2>/dev/null | tail -1 | tr -d ' ')
|
|
awk "BEGIN {printf \"%.2f\", ${used_kb:-0} / 1048576}"
|
|
}
|
|
|
|
get_ramdisk_avail_gb() {
|
|
local avail_kb
|
|
avail_kb=$(df "$RAMDISK_PATH" --output=avail 2>/dev/null | tail -1 | tr -d ' ')
|
|
awk "BEGIN {printf \"%.2f\", ${avail_kb:-0} / 1048576}"
|
|
}
|
|
|
|
get_ssd_free_gb() {
|
|
local free_kb
|
|
free_kb=$(df "$TRANSCODE_SSD" --output=avail 2>/dev/null | tail -1 | tr -d ' ')
|
|
awk "BEGIN {printf \"%.2f\", ${free_kb:-0} / 1048576}"
|
|
}
|
|
|
|
get_flip_count() {
|
|
local state_file="/tmp/transcode_flip_state.db"
|
|
local current_hour
|
|
current_hour=$(date '+%Y-%m-%d-%H')
|
|
if [[ ! -f "$state_file" ]]; then echo "0"; return; fi
|
|
local stored_hour stored_count
|
|
stored_hour=$(awk -F'|' 'NR==1{print $1}' "$state_file" 2>/dev/null)
|
|
stored_count=$(awk -F'|' 'NR==1{print $2}' "$state_file" 2>/dev/null)
|
|
[[ "$stored_hour" == "$current_hour" ]] && echo "${stored_count:-0}" || echo "0"
|
|
}
|
|
|
|
increment_flip_count() {
|
|
local state_file="/tmp/transcode_flip_state.db"
|
|
local current_hour
|
|
current_hour=$(date '+%Y-%m-%d-%H')
|
|
local current_count
|
|
current_count=$(get_flip_count)
|
|
current_count=$((current_count + 1))
|
|
echo "${current_hour}|${current_count}" > "$state_file"
|
|
echo "$current_count"
|
|
}
|
|
|
|
# Query Emby API
|
|
emby_api() {
|
|
local endpoint="$1"
|
|
[[ -z "$EMBY_API_KEY" ]] && return 1
|
|
curl -sf \
|
|
--max-time 10 \
|
|
-H "X-Emby-Token: $EMBY_API_KEY" \
|
|
"${EMBY_URL}/${endpoint}" 2>/dev/null
|
|
}
|
|
|
|
# Determine which storage location a session is using
|
|
# Handles both short hex IDs (live TV) and long UUID format (standard transcodes)
|
|
get_session_location() {
|
|
local session_id="$1"
|
|
local transcode_temp="transcoding-temp"
|
|
local short_id="${session_id:0:6}" # Emby uses short 6-char IDs for live TV sessions
|
|
|
|
# Check ramdisk — exact match first, then short ID prefix match for live TV
|
|
if [[ -d "$RAMDISK_PATH/$transcode_temp/$session_id" ]] || \
|
|
ls -d "$RAMDISK_PATH/$transcode_temp/${short_id}"* 2>/dev/null | grep -q .; then
|
|
echo "ramdisk"
|
|
# Check SSD — same pattern
|
|
elif [[ -d "$TRANSCODE_SSD/$transcode_temp/$session_id" ]] || \
|
|
ls -d "$TRANSCODE_SSD/$transcode_temp/${short_id}"* 2>/dev/null | grep -q .; then
|
|
echo "ssd"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
# Map Emby media type to readable label
|
|
get_media_type_label() {
|
|
local type="$1"
|
|
case "$type" in
|
|
LiveTv) echo "Live TV" ;;
|
|
Episode) echo "TV Show" ;;
|
|
Movie) echo "Movie" ;;
|
|
Audio) echo "Music" ;;
|
|
MusicVideo) echo "Music Video" ;;
|
|
TvChannel) echo "Live TV" ;;
|
|
*) echo "$type" ;;
|
|
esac
|
|
}
|
|
|
|
# Map Emby play method to readable label
|
|
get_play_method_label() {
|
|
local method="$1"
|
|
case "$method" in
|
|
Transcode) echo "Transcode" ;;
|
|
DirectStream) echo "Direct Stream" ;;
|
|
DirectPlay) echo "Direct Play" ;;
|
|
*) echo "$method" ;;
|
|
esac
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# ━━━ $ICON_RAM Transcode Manager ━━━
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
echo "━━━ $ICON_RAM Transcode Manager — $(date '+%Y-%m-%d %H:%M:%S') ━━━"
|
|
echo "$ICON_GEAR Mode: $TRANSCODE_MANAGER_MODE"
|
|
echo ""
|
|
|
|
START=$(date +%s)
|
|
RAMDISK_HEALTHY=true
|
|
SSD_HEALTHY=true
|
|
EMBY_RUNNING=true
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# CHECK 1 — Emby running
|
|
# -----------------------------------------------------------------------------------------------
|
|
if [[ "$TRANSCODE_CHECK_EMBY" == true ]]; then
|
|
info "$ICON_CONTAINERS Checking $TRANSCODE_EMBY_CONTAINER..."
|
|
if ! docker inspect "$TRANSCODE_EMBY_CONTAINER" \
|
|
--format '{{.State.Running}}' 2>/dev/null | grep -q "true"; then
|
|
warn "$TRANSCODE_EMBY_CONTAINER is not running — skipping threshold checks"
|
|
EMBY_RUNNING=false
|
|
else
|
|
success "$TRANSCODE_EMBY_CONTAINER is running"
|
|
fi
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# CHECK 2 — Symlink integrity
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
info "$ICON_LINK Checking symlink integrity..."
|
|
|
|
CURRENT_TARGET=$(readlink "$TRANSCODE_LINK" 2>/dev/null)
|
|
|
|
if [[ -z "$CURRENT_TARGET" ]]; then
|
|
warn "$ICON_LINK Symlink missing — recreating pointing to ramdisk"
|
|
if [[ "$DRY_RUN" == false ]]; then
|
|
ln -sfn "$RAMDISK_PATH" "$TRANSCODE_LINK"
|
|
CURRENT_TARGET="$RAMDISK_PATH"
|
|
notify "Transcode symlink was missing on $(hostname) — recreated" "Transcode Manager" "warning"
|
|
fi
|
|
elif [[ ! -e "$CURRENT_TARGET" ]]; then
|
|
warn "$ICON_LINK Symlink target missing: $CURRENT_TARGET — resetting to ramdisk"
|
|
if [[ "$DRY_RUN" == false ]]; then
|
|
ln -sfn "$RAMDISK_PATH" "$TRANSCODE_LINK"
|
|
CURRENT_TARGET="$RAMDISK_PATH"
|
|
notify "Transcode symlink target was missing on $(hostname) — reset to ramdisk" "Transcode Manager" "warning"
|
|
fi
|
|
else
|
|
success "$ICON_LINK Symlink valid: $TRANSCODE_LINK → $CURRENT_TARGET"
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# CHECK 3 — Ramdisk health
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
info "$ICON_RAM Checking ramdisk..."
|
|
|
|
if ! mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
|
|
error "$ICON_RAM Ramdisk not mounted at $RAMDISK_PATH"
|
|
RAMDISK_HEALTHY=false
|
|
if [[ "$DRY_RUN" == false ]]; then
|
|
warn "Flipping symlink to SSD — ramdisk unavailable"
|
|
flip_symlink "$TRANSCODE_SSD" "ramdisk disappeared"
|
|
CURRENT_TARGET="$TRANSCODE_SSD"
|
|
notify "Ramdisk disappeared on $(hostname) — transcodes falling back to SSD. Run ramdisk_setup.sh to restore." "Transcode Manager" "warning"
|
|
fi
|
|
else
|
|
RAMDISK_SIZE_ACTUAL=$(df "$RAMDISK_PATH" --output=size -h | tail -1 | tr -d ' ')
|
|
success "$ICON_RAM Ramdisk mounted — size: $RAMDISK_SIZE_ACTUAL"
|
|
fix_permissions "$RAMDISK_PATH"
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# CHECK 4 — SSD health
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
info "$ICON_DISK Checking SSD fallback..."
|
|
|
|
if [[ ! -d "$TRANSCODE_SSD" ]]; then
|
|
warn "$ICON_DISK SSD fallback path missing: $TRANSCODE_SSD"
|
|
SSD_HEALTHY=false
|
|
if [[ "$TRANSCODE_MANAGER_MODE" == "ssd" ]]; then
|
|
error "Mode is 'ssd' but SSD path is missing — cannot continue"
|
|
notify "Transcode SSD path missing on $(hostname) — mode is 'ssd', manual intervention needed" "Transcode Manager" "warning"
|
|
exit 1
|
|
else
|
|
warn "SSD fallback disabled — will stay on ramdisk"
|
|
fi
|
|
else
|
|
SSD_FREE_GB=$(get_ssd_free_gb)
|
|
success "$ICON_DISK SSD available — ${SSD_FREE_GB}GB free"
|
|
fix_permissions "$TRANSCODE_SSD"
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# USAGE STATS
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
RAMDISK_USED_GB="0.00"
|
|
RAMDISK_AVAIL_GB="0.00"
|
|
|
|
if [[ "$RAMDISK_HEALTHY" == true ]]; then
|
|
RAMDISK_USED_GB=$(get_ramdisk_used_gb)
|
|
RAMDISK_AVAIL_GB=$(get_ramdisk_avail_gb)
|
|
RAMDISK_FILES=$(find "$RAMDISK_PATH" -type f 2>/dev/null | wc -l)
|
|
RAMDISK_SIZE_H=$(du -sh "$RAMDISK_PATH" 2>/dev/null | cut -f1)
|
|
fi
|
|
|
|
SSD_FILES=$(find "$TRANSCODE_SSD" -type f 2>/dev/null | wc -l)
|
|
SSD_SIZE_H=$(du -sh "$TRANSCODE_SSD" 2>/dev/null | cut -f1)
|
|
|
|
info "$ICON_RAM Ramdisk usage: ${RAMDISK_USED_GB}GB used / ${RAMDISK_AVAIL_GB}GB available"
|
|
info "$ICON_RAM Ramdisk files: ${RAMDISK_FILES:-0} ($RAMDISK_SIZE_H)"
|
|
info "$ICON_DISK SSD files: $SSD_FILES ($SSD_SIZE_H)"
|
|
|
|
FLIP_COUNT=$(get_flip_count)
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# ━━━ $ICON_EMBY Active Emby Sessions ━━━
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
echo "━━━ $ICON_EMBY Active Emby Sessions ━━━"
|
|
|
|
if [[ -z "$EMBY_API_KEY" ]]; then
|
|
info "EMBY_API_KEY not set in Master.conf — skipping session display"
|
|
elif [[ "$EMBY_RUNNING" == false ]]; then
|
|
info "Emby not running — no sessions"
|
|
else
|
|
SESSION_DATA=$(emby_api "Sessions" 2>/dev/null)
|
|
|
|
if [[ -z "$SESSION_DATA" ]] || ! command -v jq >/dev/null 2>&1; then
|
|
warn "Could not retrieve session data — check EMBY_URL and EMBY_API_KEY"
|
|
else
|
|
# Count sessions
|
|
TOTAL_SESSIONS=$(echo "$SESSION_DATA" | \
|
|
jq '[.[] | select(.NowPlayingItem != null)] | length' 2>/dev/null || echo 0)
|
|
LIVE_TV=$(echo "$SESSION_DATA" | \
|
|
jq '[.[] | select(.NowPlayingItem != null) | select(.NowPlayingItem.Type == "LiveTv" or .NowPlayingItem.Type == "TvChannel")] | length' 2>/dev/null || echo 0)
|
|
TRANSCODING=$(echo "$SESSION_DATA" | \
|
|
jq '[.[] | select(.NowPlayingItem != null) | select(.TranscodingInfo != null)] | length' 2>/dev/null || echo 0)
|
|
DIRECT=$(echo "$SESSION_DATA" | \
|
|
jq '[.[] | select(.NowPlayingItem != null) | select(.TranscodingInfo == null)] | length' 2>/dev/null || echo 0)
|
|
|
|
echo " $ICON_EMBY Total: $TOTAL_SESSIONS | $ICON_RAM Live TV: $LIVE_TV | $ICON_SYNC Transcoding: $TRANSCODING | $ICON_DONE Direct: $DIRECT"
|
|
echo ""
|
|
|
|
if [[ "$TOTAL_SESSIONS" -gt 0 ]]; then
|
|
# Parse each active session
|
|
while IFS= read -r session; do
|
|
USER=$(echo "$session" | jq -r '.UserName // "Unknown"' 2>/dev/null)
|
|
TITLE=$(echo "$session" | jq -r '.NowPlayingItem.Name // "Unknown"' 2>/dev/null)
|
|
MEDIA_TYPE=$(echo "$session" | jq -r '.NowPlayingItem.Type // "Unknown"' 2>/dev/null)
|
|
SESSION_ID=$(echo "$session" | jq -r '.Id // ""' 2>/dev/null)
|
|
|
|
# Play method
|
|
PLAY_METHOD=$(echo "$session" | \
|
|
jq -r '.PlayState.PlayMethod // "Unknown"' 2>/dev/null)
|
|
# Check TranscodingInfo for more accurate method
|
|
HAS_TRANSCODE=$(echo "$session" | \
|
|
jq '.TranscodingInfo != null' 2>/dev/null || echo "false")
|
|
[[ "$HAS_TRANSCODE" == "true" ]] && PLAY_METHOD="Transcode"
|
|
|
|
MEDIA_LABEL=$(get_media_type_label "$MEDIA_TYPE")
|
|
METHOD_LABEL=$(get_play_method_label "$PLAY_METHOD")
|
|
|
|
# Determine storage location for transcoding sessions
|
|
if [[ "$HAS_TRANSCODE" == "true" && -n "$SESSION_ID" ]]; then
|
|
LOCATION=$(get_session_location "$SESSION_ID")
|
|
case "$LOCATION" in
|
|
ramdisk) LOC_ICON="$ICON_RAM" LOC_LABEL="ramdisk" ;;
|
|
ssd) LOC_ICON="$ICON_DISK" LOC_LABEL="SSD" ;;
|
|
*) LOC_ICON="$ICON_INFO" LOC_LABEL="unknown" ;;
|
|
esac
|
|
echo " $ICON_EMBY $(printf '%-12s' "$USER") — $(printf '%-30s' "$TITLE") — $(printf '%-10s' "$MEDIA_LABEL") — $(printf '%-13s' "$METHOD_LABEL") — $LOC_ICON $LOC_LABEL"
|
|
else
|
|
echo " $ICON_EMBY $(printf '%-12s' "$USER") — $(printf '%-30s' "$TITLE") — $(printf '%-10s' "$MEDIA_LABEL") — $METHOD_LABEL"
|
|
fi
|
|
|
|
done < <(echo "$SESSION_DATA" | \
|
|
jq -c '.[] | select(.NowPlayingItem != null)' 2>/dev/null)
|
|
else
|
|
info "No active streams"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# MODE LOGIC
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
|
|
case "$TRANSCODE_MANAGER_MODE" in
|
|
|
|
ramdisk)
|
|
info "$ICON_RAM Mode: RAMDISK — forcing symlink to ramdisk"
|
|
if [[ "$RAMDISK_HEALTHY" == false ]]; then
|
|
error "Ramdisk mode selected but ramdisk is not available"
|
|
notify "Transcode ramdisk mode failed on $(hostname) — ramdisk not mounted" "Transcode Manager" "warning"
|
|
else
|
|
if [[ "$CURRENT_TARGET" != "$RAMDISK_PATH" ]]; then
|
|
flip_symlink "$RAMDISK_PATH" "ramdisk mode"
|
|
increment_flip_count > /dev/null
|
|
else
|
|
log "Symlink already points to ramdisk — no change"
|
|
fi
|
|
if (( $(awk "BEGIN {print ($RAMDISK_USED_GB >= $RAMDISK_WARN_GB) ? 1 : 0}") )); then
|
|
warn "$ICON_RAM Ramdisk usage ${RAMDISK_USED_GB}GB above threshold ${RAMDISK_WARN_GB}GB — consider switching to smart mode"
|
|
notify "Ramdisk usage high on $(hostname) — ${RAMDISK_USED_GB}GB used in ramdisk-only mode" "Transcode Manager" "warning"
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
ssd)
|
|
info "$ICON_DISK Mode: SSD — forcing symlink to SSD"
|
|
if [[ "$SSD_HEALTHY" == false ]]; then
|
|
error "SSD mode selected but SSD path is not available"
|
|
else
|
|
if [[ "$CURRENT_TARGET" != "$TRANSCODE_SSD" ]]; then
|
|
flip_symlink "$TRANSCODE_SSD" "ssd mode"
|
|
increment_flip_count > /dev/null
|
|
else
|
|
log "Symlink already points to SSD — no change"
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
smart)
|
|
info "$ICON_GEAR Mode: SMART — auto threshold management"
|
|
|
|
if [[ "$EMBY_RUNNING" == false ]]; then
|
|
info "Emby not running — skipping threshold checks"
|
|
|
|
elif [[ "$RAMDISK_HEALTHY" == false ]]; then
|
|
info "Ramdisk unavailable — staying on SSD until ramdisk recovers"
|
|
|
|
elif [[ "$CURRENT_TARGET" == "$RAMDISK_PATH" ]]; then
|
|
if (( $(awk "BEGIN {print ($RAMDISK_USED_GB >= $RAMDISK_WARN_GB) ? 1 : 0}") )); then
|
|
if [[ "$SSD_HEALTHY" == false ]]; then
|
|
error "Ramdisk above threshold but SSD unavailable — cannot flip"
|
|
notify "Transcode ramdisk full on $(hostname) and SSD unavailable — intervention needed" "Transcode Manager" "warning"
|
|
else
|
|
SSD_FREE_GB=$(get_ssd_free_gb)
|
|
if (( $(awk "BEGIN {print ($SSD_FREE_GB < $RAMDISK_SSD_MIN_GB) ? 1 : 0}") )); then
|
|
warn "$ICON_DISK SSD only ${SSD_FREE_GB}GB free — below ${RAMDISK_SSD_MIN_GB}GB minimum, not flipping"
|
|
else
|
|
warn "$ICON_RAM Ramdisk ${RAMDISK_USED_GB}GB — above ${RAMDISK_WARN_GB}GB, flipping to SSD"
|
|
flip_symlink "$TRANSCODE_SSD" "threshold exceeded"
|
|
CURRENT_TARGET="$TRANSCODE_SSD"
|
|
NEW_COUNT=$(increment_flip_count)
|
|
FLIP_COUNT=$NEW_COUNT
|
|
if [[ "$NEW_COUNT" -ge "$TRANSCODE_FLIP_WARN" ]]; then
|
|
notify "Transcode flipped to SSD on $(hostname) — ${NEW_COUNT} flips this hour. Consider increasing RAMDISK_SIZE." "Transcode Manager" "warning"
|
|
fi
|
|
fi
|
|
fi
|
|
else
|
|
log "Symlink already points to ramdisk — no change"
|
|
fi
|
|
|
|
elif [[ "$CURRENT_TARGET" == "$TRANSCODE_SSD" ]]; then
|
|
if (( $(awk "BEGIN {print ($RAMDISK_USED_GB <= $RAMDISK_LOW_GB) ? 1 : 0}") )); then
|
|
info "$ICON_RAM Ramdisk ${RAMDISK_USED_GB}GB — below ${RAMDISK_LOW_GB}GB, flipping back to ramdisk"
|
|
flip_symlink "$RAMDISK_PATH" "usage recovered"
|
|
CURRENT_TARGET="$RAMDISK_PATH"
|
|
NEW_COUNT=$(increment_flip_count)
|
|
FLIP_COUNT=$NEW_COUNT
|
|
else
|
|
log "On SSD — ramdisk ${RAMDISK_USED_GB}GB still above low threshold ${RAMDISK_LOW_GB}GB"
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
END=$(date +%s)
|
|
CURRENT_TARGET=$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "unknown")
|
|
|
|
# -----------------------------------------------------------------------------------------------
|
|
# ━━━ $ICON_SUMMARY Summary ━━━
|
|
# -----------------------------------------------------------------------------------------------
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY TRANSCODE MANAGER SUMMARY ━━━━━"
|
|
echo "$ICON_GEAR Mode: $TRANSCODE_MANAGER_MODE"
|
|
echo "$ICON_RAM Ramdisk: ${RAMDISK_USED_GB}GB used / ${RAMDISK_AVAIL_GB}GB available"
|
|
echo "$ICON_LINK Symlink: $TRANSCODE_LINK → $CURRENT_TARGET"
|
|
echo "$ICON_LINK Flips: $FLIP_COUNT this hour (warn at $TRANSCODE_FLIP_WARN)"
|
|
echo "$ICON_EMBY Streams: ${TOTAL_SESSIONS:-0} total | Live TV: ${LIVE_TV:-0} | Transcoding: ${TRANSCODING:-0} | Direct: ${DIRECT:-0}"
|
|
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
|
|
echo "$ICON_DONE Status: $ICON_SUCCESS DONE"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" |