mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-04 20:03:11 +02:00
Consolidate the two stream-check tools into a single master script with three subcommands: - check <url> verify one stream (or --live dashboard) - playlist <path|url> batch an .m3u list -> aggregate JSON (+ per-stream files) - graph <inputs...> render the JSON as SVG charts Shared helpers (HTTP, slugify, m3u/JSON handling) are now defined once. The old --playlist flag becomes the `playlist` subcommand and the bare-URL form becomes `check <url>`; the streamtest harness is updated accordingly. Also fix the per-stream SVG rendering black in viewers that do not support 8-digit #rrggbbaa hex: the bitrate area fill and not-PLAYING bands now use 6-digit hex plus a separate fill-opacity attribute. Docs (English + tools READMEs + STREAMTEST) updated to the new invocation. Removes stream_queue_check.py and stream_graph.py.
141 lines
5.6 KiB
Bash
141 lines
5.6 KiB
Bash
#!/bin/bash
|
|
# streamtest_in_container.sh — runs INSIDE the xcvm test container.
|
|
#
|
|
# Assumes the panel has ALREADY been configured (manually, once) and that
|
|
# configuration has been captured with `test_release.sh streamtest-backup` and
|
|
# restored by the host wrapper before this script runs. See STREAMTEST.md.
|
|
#
|
|
# This script does NOT touch the database. It only:
|
|
# 1. rebuilds the file caches the streaming path reads (cron:cache, cron:cache_engine)
|
|
# 2. launches tools/test-stream-generator as the upstream source (bundled ffmpeg)
|
|
# 3. fetches the panel's OUTPUT m3u for the configured test line
|
|
# 4. runs stream_check.py playlist against the panel output
|
|
#
|
|
# JSON report → STDOUT (captured by the host wrapper); progress → STDERR.
|
|
# Exit code = the checker's (0 = all healthy, 2 = a stream failed, 3 = setup error).
|
|
set -uo pipefail
|
|
|
|
log() { echo "[streamtest] $*" >&2; }
|
|
|
|
TOOLS=/opt/xcvm-tools
|
|
XC=/home/xc_vm
|
|
PHP="$XC/bin/php/bin/php"
|
|
CONSOLE="$XC/console.php"
|
|
USER_NAME="${STREAMTEST_USER:-xcvmtest}"
|
|
USER_PASS="${STREAMTEST_PASS:-xcvmtest}"
|
|
DURATION="${STREAMTEST_DURATION:-120}"
|
|
TOLERANCE="${STREAMTEST_TOLERANCE:-0}"
|
|
STALL_TIMEOUT="${STREAMTEST_STALL_TIMEOUT:-15}"
|
|
GEN_PORT="${STREAMTEST_GEN_PORT:-8088}"
|
|
OUT_DIR="${STREAMTEST_OUT_DIR:-/tmp/xcvm-streamlogs}" # per-stream JSON logs (copied out by the host)
|
|
GEN="http://127.0.0.1:$GEN_PORT"
|
|
|
|
# ── HTTP GET via python3 (always present; avoids a curl/wget dependency) ──
|
|
http_get() { # http_get <url> <out-file> → 0 on success
|
|
python3 - "$1" "$2" <<'PY'
|
|
import sys, urllib.request
|
|
url, out = sys.argv[1], sys.argv[2]
|
|
try:
|
|
data = urllib.request.urlopen(url, timeout=10).read()
|
|
except Exception as e:
|
|
sys.stderr.write("fetch fail: %s\n" % e)
|
|
sys.exit(1)
|
|
with open(out, "wb") as fh:
|
|
fh.write(data)
|
|
PY
|
|
}
|
|
|
|
as_xcvm() { # run a command as the xc_vm service user when possible
|
|
if command -v sudo >/dev/null 2>&1 && id xc_vm >/dev/null 2>&1; then
|
|
sudo -u xc_vm "$@"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
# ── locate bundled ffmpeg (only present after the panel is installed) ──
|
|
FFMPEG=""
|
|
for v in 8.0 7.1 4.0; do
|
|
if [[ -x "$XC/bin/ffmpeg_bin/$v/ffmpeg" ]]; then
|
|
FFMPEG="$XC/bin/ffmpeg_bin/$v/ffmpeg"
|
|
break
|
|
fi
|
|
done
|
|
[[ -n "$FFMPEG" ]] || { log "ERROR: no bundled ffmpeg under $XC/bin/ffmpeg_bin (is the panel installed?)"; exit 3; }
|
|
log "ffmpeg: $FFMPEG"
|
|
|
|
SAMPLE="$TOOLS/test-stream-generator/sample.mp4"
|
|
[[ -f "$SAMPLE" ]] || { log "ERROR: sample.mp4 not found at $SAMPLE (is tools/ mounted?)"; exit 3; }
|
|
|
|
# ── rebuild the file caches from the (restored) DB ──
|
|
log "cache: cron:cache"
|
|
as_xcvm "$PHP" "$CONSOLE" cron:cache >&2 2>&1 || log "warn: cron:cache rc=$?"
|
|
log "cache: cron:cache_engine"
|
|
as_xcvm "$PHP" "$CONSOLE" cron:cache_engine >&2 2>&1 || log "warn: cron:cache_engine rc=$?"
|
|
|
|
# ── launch the upstream generator (background; killed on exit) ──
|
|
# Kill any prior generator and WAIT for the port to actually free before binding
|
|
# (a just-terminated process can hold the socket briefly → "Address already in use").
|
|
pkill -f 'test-stream-generator/stream_server.py' 2>/dev/null || true
|
|
for _ in $(seq 1 12); do
|
|
python3 -c "import socket,sys
|
|
s=socket.socket()
|
|
try:
|
|
s.bind(('0.0.0.0', $GEN_PORT)); s.close()
|
|
except OSError:
|
|
sys.exit(1)" 2>/dev/null && break
|
|
sleep 0.5
|
|
done
|
|
log "generator: starting on :$GEN_PORT (advertise 127.0.0.1)"
|
|
nohup python3 "$TOOLS/test-stream-generator/stream_server.py" \
|
|
-i "$SAMPLE" --port "$GEN_PORT" --advertise-host 127.0.0.1 --ffmpeg "$FFMPEG" \
|
|
>/tmp/xcvm-streamgen.log 2>&1 &
|
|
GEN_PID=$!
|
|
trap 'kill "$GEN_PID" 2>/dev/null; pkill -f stream_server.py 2>/dev/null' EXIT
|
|
|
|
ready=0
|
|
for _ in $(seq 1 15); do
|
|
if http_get "$GEN/playlist.m3u" /tmp/xcvm-gen-check.m3u 2>/dev/null; then ready=1; break; fi
|
|
sleep 1
|
|
done
|
|
[[ $ready -eq 1 ]] || { log "ERROR: generator not ready"; sed 's/^/[gen] /' /tmp/xcvm-streamgen.log >&2; exit 3; }
|
|
log "generator: ready"
|
|
|
|
# ── give on-demand/monitored streams a moment to warm (cron:streams starts them) ──
|
|
sleep "${STREAMTEST_WARMUP:-8}"
|
|
|
|
# ── fetch the panel's OUTPUT m3u for the configured test line ──
|
|
PL=/tmp/xcvm-panel.m3u
|
|
fetched=0
|
|
for url in \
|
|
"http://127.0.0.1/get.php?username=$USER_NAME&password=$USER_PASS&type=m3u_plus&output=ts" \
|
|
"http://127.0.0.1/api/playlist?username=$USER_NAME&password=$USER_PASS&type=m3u_plus&output=ts" \
|
|
"http://127.0.0.1/playlist/$USER_NAME/$USER_PASS/m3u_plus"; do
|
|
if http_get "$url" "$PL" 2>/dev/null && head -1 "$PL" 2>/dev/null | grep -q '#EXTM3U'; then
|
|
log "playlist: fetched via ${url%%\?*}"
|
|
fetched=1
|
|
break
|
|
fi
|
|
done
|
|
if [[ $fetched -ne 1 ]]; then
|
|
log "ERROR: could not fetch a valid panel playlist for line '$USER_NAME'."
|
|
log "The restored backup must contain a line with THESE credentials and a non-empty bouquet."
|
|
log "If your line uses a different name, pass it explicitly, e.g.:"
|
|
log " STREAMTEST_USER=<line> STREAMTEST_PASS=<pass> ./test_release.sh streamtest"
|
|
log "See STREAMTEST.md."
|
|
log "last response head:"; head -5 "$PL" 2>/dev/null | sed 's/^/[body] /' >&2
|
|
exit 3
|
|
fi
|
|
log "panel playlist:"; sed 's/^/[streamtest] /' "$PL" >&2
|
|
|
|
# ── run the checker against the panel output (aggregate JSON → stdout,
|
|
# per-stream JSON logs → $OUT_DIR, which the host copies out) ──
|
|
rm -rf "$OUT_DIR" 2>/dev/null; mkdir -p "$OUT_DIR"
|
|
log "checker: testing each stream for ${DURATION}s (tolerance=${TOLERANCE}, stall-timeout=${STALL_TIMEOUT}s); per-stream logs → $OUT_DIR"
|
|
python3 "$TOOLS/stream-check/stream_check.py" playlist "$PL" \
|
|
--duration "$DURATION" --tolerance "$TOLERANCE" \
|
|
--stall-timeout "$STALL_TIMEOUT" --out-dir "$OUT_DIR"
|
|
rc=$?
|
|
log "checker: exit $rc"
|
|
exit $rc
|