#!/bin/bash # ============================================================================================== # ============================= Transcode Management =========================================== # ============================================================================================== # Orchestrator — runs transcode_cleanup.sh then transcode_manager.sh in the correct order. # Replace individual transcode_manager and transcode_cleanup cron entries with this. # Schedule: */3 * * * * (every 3 minutes via User Scripts plugin) # # ── WHY CLEANUP BEFORE MANAGER ──────────────────────────────────────────────────────────────── # Cleanup runs first — removes stale segment files from ended sessions. # Manager runs after — threshold decisions based on real current usage post-cleanup. # Without this order, stale files inflate the ramdisk usage reading and trigger # unnecessary SSD flips even when active sessions would fit on the ramdisk. # # ── WHAT EACH SCRIPT DOES ───────────────────────────────────────────────────────────────────── # transcode_cleanup.sh — removes aged segment files not open by any process # uses lsof for O(1) per-file active check (never per-file lsof) # also triggers flip-back to ramdisk after cleanup if recovered ✅ # # transcode_manager.sh — checks ramdisk usage against thresholds # flips symlink between ramdisk and SSD as needed # writes one entry to TRANSCODE_DAILY_LOG after each run # shows active Emby sessions with play method # # ── DAILY LOG ───────────────────────────────────────────────────────────────────────────────── # transcode_manager.sh writes to TRANSCODE_DAILY_LOG after each run: # Format: DATE|RAMDISK_USED_GB|FLIP_COUNT|RAM_SESSIONS|SSD_SESSIONS # This orchestrator does NOT write its own log — manager handles it ✅ # Log trimmed to TRANSCODE_LOG_RETENTION days by manager on each write. # Read by sunday_morning_coffee_report.sh and weekly_health_digest.sh. # # ── HOST AWARENESS ──────────────────────────────────────────────────────────────────────────── # detect_hosts() sets MY_ID and aliases RAMDISK_PATH, TRANSCODE_SSD, RAMDISK_WARN_GB etc. # Each server manages its own transcode location independently. # # ── SAFEGUARDS ──────────────────────────────────────────────────────────────────────────────── # Root check — mount and docker operations require root # acquire_lock — prevents concurrent 3-minute cycles overlapping # detect_hosts() — correct paths per host # --dry-run — passed through to both child scripts # Exit code — worst exit code of both scripts returned # # ── CONFIGURATION (master.conf) ─────────────────────────────────────────────────────────────── # TRANSCODE_DAILY_LOG — daily stats log (written by transcode_manager.sh) # TRANSCODE_LOG_RETENTION — days to keep (trimmed by manager) # TRANSCODE_STATE_FILE — current state (ramdisk_used, flip_count etc.) # All TRANSCODE_* threshold vars — see master.conf Transcode Manager section # # ── USAGE ───────────────────────────────────────────────────────────────────────────────────── # transcode_management.sh — normal run (every 3 minutes via cron) # transcode_management.sh --dry-run — preview without changes (passed to children) # transcode_management.sh --status — show configuration and current state # transcode_management.sh --log — verbose output from both child scripts # ============================================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../load_config.sh" parse_args "$@" CLEANUP_SCRIPT="$SCRIPT_DIR/../Transcodes/transcode_cleanup.sh" MANAGER_SCRIPT="$SCRIPT_DIR/../Transcodes/transcode_manager.sh" # ============================================================================================== # ━━━ Setup ━━━ # ============================================================================================== if [[ "$EUID" -ne 0 ]]; then error "Must be run as root" exit 1 fi acquire_lock "wait" if ! command -v docker &>/dev/null; then error "Docker command not found" exit 1 fi # detect_hosts() sets MY_ID and aliases all HOST*_TRANSCODE_* vars detect_hosts [[ "$DRY_RUN" == true ]] && warn "DRY RUN — passing through to child scripts" # ============================================================================================== # ━━━ Status ━━━ # ============================================================================================== if [[ "$SHOW_STATUS" == true ]]; then echo "" echo "━━━━━ $ICON_SUMMARY TRANSCODE MANAGEMENT STATUS ━━━━━" echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)" echo "$ICON_RAM Ramdisk: $RAMDISK_PATH ($RAMDISK_SIZE)" echo "$ICON_DISK SSD fallback: $TRANSCODE_SSD" echo "$ICON_LINK Symlink: $TRANSCODE_LINK" echo "$ICON_TIME Schedule: every 3 minutes" echo "" echo "━━━ Child Scripts ━━━" [[ -f "$CLEANUP_SCRIPT" ]] && \ echo " $ICON_SUCCESS transcode_cleanup.sh — found" || \ echo " $ICON_ERROR transcode_cleanup.sh — NOT FOUND at $CLEANUP_SCRIPT" [[ -f "$MANAGER_SCRIPT" ]] && \ echo " $ICON_SUCCESS transcode_manager.sh — found" || \ echo " $ICON_ERROR transcode_manager.sh — NOT FOUND at $MANAGER_SCRIPT" echo "" echo "━━━ Daily Log ━━━" if [[ -f "${TRANSCODE_DAILY_LOG:-}" ]] && [[ -s "$TRANSCODE_DAILY_LOG" ]]; then ENTRY_COUNT=$(wc -l < "$TRANSCODE_DAILY_LOG") OLDEST=$(awk -F'|' 'NR==1{print $1}' "$TRANSCODE_DAILY_LOG") NEWEST=$(awk -F'|' 'END{print $1}' "$TRANSCODE_DAILY_LOG") echo " $ICON_SUCCESS $TRANSCODE_DAILY_LOG ($ENTRY_COUNT entries, $OLDEST → $NEWEST)" else echo " $ICON_SKIP $TRANSCODE_DAILY_LOG — no data yet" fi echo "" echo "━━━ Current State ━━━" if [[ -f "${TRANSCODE_STATE_FILE:-/tmp/transcode_state.db}" ]]; then while IFS='=' read -r key val; do [[ -n "$key" ]] && echo " $key = $val" done < "${TRANSCODE_STATE_FILE:-/tmp/transcode_state.db}" else echo " State file not found (ramdisk_setup.sh creates it at array start)" fi echo "━━━━━━━━━━━━━━━━━━━━━━━" exit 0 fi # ============================================================================================== # ━━━ Validate Child Scripts ━━━ # ============================================================================================== if [[ ! -f "$CLEANUP_SCRIPT" ]]; then error "transcode_cleanup.sh not found: $CLEANUP_SCRIPT" exit 1 fi if [[ ! -f "$MANAGER_SCRIPT" ]]; then error "transcode_manager.sh not found: $MANAGER_SCRIPT" exit 1 fi # ============================================================================================== # ━━━ Run Cleanup ━━━ # ============================================================================================== DRY_FLAG="" [[ "$DRY_RUN" == true ]] && DRY_FLAG="--dry-run" bash "$CLEANUP_SCRIPT" $DRY_FLAG CLEANUP_EXIT=$? # ============================================================================================== # ━━━ Run Manager ━━━ # ============================================================================================== # transcode_manager.sh writes to TRANSCODE_DAILY_LOG after each run # No --no-log flag here — manager owns the log write for this cycle ✅ bash "$MANAGER_SCRIPT" $DRY_FLAG MANAGER_EXIT=$? # ============================================================================================== # ━━━ Exit ━━━ # ============================================================================================== # Return worst exit code — caller knows if either script failed [[ "$CLEANUP_EXIT" -ne 0 || "$MANAGER_EXIT" -ne 0 ]] && exit 1 exit 0