Aggiunge overlay server-side burn-in e stabilizza diretta live.

Tabellone, badge e brand sono ricodificati nel flusso _air via OverlayRelay;
sync punteggio HTTP, volume condiviso rails/sidekiq, qualità 720p migliorata
e badge CONNECTING in ripresa da pausa.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 23:55:17 +02:00
parent fd225fbadd
commit ab9cb02083
58 changed files with 2423 additions and 242 deletions

95
scripts/test/live_e2e_cycle.sh Executable file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env bash
# E2E ciclo diretta web: status, HLS, pausa, ripresa.
set -euo pipefail
SESSION_ID="${SESSION_ID:-fe09a29f-79db-44d5-864f-397942c0a2c5}"
API="${API:-https://www.matchlivetv.it}"
EMAIL="${TEST_EMAIL:-coach@matchlivetv.test}"
PASSWORD="${TEST_PASSWORD:-password123}"
fail() { echo "FAIL: $*"; exit 1; }
ok() { echo "OK: $*"; }
echo "== Login =="
TOKEN=$(curl -sf -X POST "$API/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
AUTH="Authorization: Bearer $TOKEN"
echo "== Status iniziale =="
INIT=$(curl -sf "$API/live/$SESSION_ID/status.json")
echo "$INIT" | python3 -m json.tool
python3 -c "
import json,sys
d=json.loads('''$INIT''')
assert not d.get('stream_closed'), d
print('status', d.get('status'), 'on_air', d.get('on_air'), 'publisher', d.get('publisher_online'))
"
echo "== HLS master =="
MASTER=$(curl -sf "$API/hls/live/match_${SESSION_ID}/index.m3u8")
echo "$MASTER" | head -8
echo "$MASTER" | grep -q main_stream || fail "master senza variant"
VARIANT_LINE=$(echo "$MASTER" | grep main_stream | head -1 | tr -d '\r')
VARIANT_URL="$API/hls/live/match_${SESSION_ID}/${VARIANT_LINE}"
MEDIA=$(curl -sf "$VARIANT_URL")
SEG=$(echo "$MEDIA" | grep '_main_seg' | tail -1 | tr -d '\r')
[[ -n "$SEG" ]] || fail "nessun segmento in playlist"
SEG_URL="$API/hls/live/match_${SESSION_ID}/${SEG}"
SZ=$(curl -sf -o /tmp/live_e2e_seg.ts -w '%{size_download}' "$SEG_URL")
[[ "${SZ:-0}" -gt 1000 ]] || fail "segmento troppo piccolo ($SZ bytes)"
ok "HLS segmento ${SZ} bytes"
if [[ "$(echo "$INIT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('paused'))")" == "True" ]]; then
echo "== Sessione in pausa: test resume =="
curl -sf -X PATCH -H "$AUTH" "$API/api/v1/sessions/$SESSION_ID/resume" >/dev/null
sleep 6
fi
echo "== Verifica live (publisher atteso) =="
for i in 1 2 3 4 5 6 7 8 9 10; do
ST=$(curl -sf "$API/live/$SESSION_ID/status.json")
PUB=$(echo "$ST" | python3 -c "import sys,json; print(json.load(sys.stdin).get('publisher_online'))")
PAUSED=$(echo "$ST" | python3 -c "import sys,json; print(json.load(sys.stdin).get('paused'))")
echo " poll $i paused=$PAUSED publisher_online=$PUB"
if [[ "$PAUSED" == "False" && "$PUB" == "True" ]]; then
ok "live attivo"
break
fi
sleep 2
if [[ "$i" == "10" ]]; then
echo "WARN: live non confermato entro 20s (continuo pausa test)"
fi
done
echo "== Pausa API =="
curl -sf -X PATCH -H "$AUTH" "$API/api/v1/sessions/$SESSION_ID/pause" >/dev/null
sleep 8
PAUSE_ST=$(curl -sf "$API/live/$SESSION_ID/status.json")
echo "$PAUSE_ST" | python3 -m json.tool | head -12
python3 -c "
import json,sys
d=json.loads('''$PAUSE_ST''')
assert d.get('paused') or d.get('status')=='paused', d
print('paused OK')
"
echo "== Ripresa API =="
curl -sf -X PATCH -H "$AUTH" "$API/api/v1/sessions/$SESSION_ID/resume" >/dev/null
sleep 8
RES=$(curl -sf "$API/live/$SESSION_ID/status.json")
echo "$RES" | python3 -m json.tool | head -12
python3 -c "
import json,sys
d=json.loads('''$RES''')
assert not d.get('paused'), d
assert d.get('status') in ('live','connecting'), d
print('resume OK')
"
echo "== Telemetry device =="
curl -sf -H "$AUTH" "$API/api/v1/sessions/$SESSION_ID" \
| python3 -c "import sys,json; d=json.load(sys.stdin); dev=d.get('devices') or []; print('devices', dev[:2])"
ok "Ciclo E2E API/HLS completato"