#!/usr/bin/env bash set -euo pipefail # Headless GL: start a virtual framebuffer unless a real DISPLAY is provided # (a GPU node may pass through an X socket). CasparCG's mixer needs a GL context. if [ -z "${DISPLAY:-}" ]; then echo "[entrypoint] starting Xvfb on :99" Xvfb :99 -screen 0 1920x1080x24 -nolisten tcp & export DISPLAY=:99 # Give Xvfb a moment to create the socket. for i in $(seq 1 20); do [ -e /tmp/.X11-unix/X99 ] && break sleep 0.25 done fi # Ensure the HLS preview directory exists before CasparCG attaches its second # FFMPEG consumer (mam-api serves /live//* from the shared volume). if [ -n "${CHANNEL_ID:-}" ]; then mkdir -p "/media/live/${CHANNEL_ID}" fi # casparcg.config writes log/ + data/ under /media (the install dir is a symlink # into a possibly read-only apt location), so make sure those exist + writable. mkdir -p /media/casparcg/log /media/casparcg/data /media/templates # Launch CasparCG Server. The 2.5 deb installs the binary on PATH (symlinked to # /usr/local/bin/casparcg at build) and takes the config file as its first arg. # Run from /opt/casparcg (symlink to the install dir) so any relative lookups in # the server resolve against the install tree. cd /opt/casparcg CASPAR_BIN="casparcg" command -v "$CASPAR_BIN" >/dev/null || CASPAR_BIN=/usr/bin/casparcg-server-2.5 CASPAR_CFG=/opt/casparcg/casparcg.config echo "[entrypoint] launching CasparCG: $CASPAR_BIN $CASPAR_CFG" "$CASPAR_BIN" "$CASPAR_CFG" & CASPAR_PID=$! # Forward termination to CasparCG so the channel closes cleanly. term() { echo "[entrypoint] terminating CasparCG ($CASPAR_PID)" kill -TERM "$CASPAR_PID" 2>/dev/null || true wait "$CASPAR_PID" 2>/dev/null || true exit 0 } trap term SIGTERM SIGINT # Launch the Node control shim (foreground). If it exits, stop the container. cd /app node src/index.js & NODE_PID=$! wait -n "$CASPAR_PID" "$NODE_PID" term