Mantiene la copertina YouTube se il telefono cade e avvia ffmpeg sul nodo, non via SSH.

Il relay HLS→RTMPS resta sul worker/agent del nodo assegnato, così tre dirette contemporanee restano in onda sul sito e sul canale della società senza schermo nero.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-16 20:41:46 +02:00
co-authored by Cursor
parent bfb3115a64
commit baa6283a15
36 changed files with 1444 additions and 141 deletions
+40 -13
View File
@@ -1,5 +1,5 @@
#cloud-config
# Nodo stream Hetzner: MediaMTX + ffmpeg.
# Nodo stream Hetzner: MediaMTX + ffmpeg + relay-agent (YouTube sul nodo).
# HCLOUD_USER_DATA_FILE=.../infra/stream-node/cloud-init.yaml
package_update: true
@@ -8,14 +8,14 @@ packages:
- ffmpeg
- curl
- apparmor
- python3
- fonts-dejavu-core
write_files:
- path: /opt/stream-node/mediamtx.yml
permissions: "0644"
content: |
logLevel: info
# Allinea a infra/mediamtx.yml: API raggiungibile anche da IP non-localhost
# (in prod restringere via WireGuard / firewall Hetzner).
authInternalUsers:
- user: any
pass:
@@ -34,34 +34,61 @@ write_files:
hlsAddress: :8888
paths:
all_others:
- path: /opt/stream-node/relay-agent.service
permissions: "0644"
content: |
[Unit]
Description=Match Live TV YouTube relay agent
After=network-online.target docker.service
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=-/opt/stream-node/agent.env
ExecStart=/usr/bin/python3 /opt/stream-node/relay-agent.py
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
- path: /opt/stream-node/agent.env
permissions: "0600"
content: |
STREAM_NODE_AGENT_SECRET=mediamtx_webhook_dev_secret
STREAM_NODE_AGENT_LISTEN=0.0.0.0:9100
STREAM_NODE_LOCAL_HLS_URL=http://127.0.0.1:8888
STREAM_NODE_RELAY_LOG_DIR=/var/log
- path: /opt/stream-node/bootstrap.sh
permissions: "0755"
content: |
#!/bin/bash
set -euo pipefail
mkdir -p /recordings /slates
mkdir -p /recordings /slates /var/log
systemctl enable --now docker
# aspetta il socket docker
for i in $(seq 1 60); do
if docker info >/dev/null 2>&1; then break; fi
sleep 2
done
docker pull bluenviron/mediamtx:latest
docker rm -f mediamtx 2>/dev/null || true
# Slate offline (alwaysAvailable) — richiesta da Mediamtx::Client#create_path.
# MediaMTX 1.20+: AAC del publisher RTMP deve matchare la slate.
# Allineato ad app Android (BroadcastConfig: 48 kHz mono) e infra/scripts/generate_slate.sh.
mkdir -p /slates
ffmpeg -y -f lavfi -i color=c=black:s=1280x720:d=2 -f lavfi -i anullsrc=r=48000:cl=mono \
-c:v libx264 -t 2 -pix_fmt yuv420p -c:a aac -ac 1 -ar 48000 -shortest /slates/offline.mp4
# Debian cloud image: docker.io senza apparmor_parser → serve unconfined o pkg apparmor
FONT=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf
# Copertina alwaysAvailable (NON nera): resta in HLS/YouTube se l'app cade.
ffmpeg -y -f lavfi -i color=c=0x0a0a0e:s=1280x720:r=30:d=30 \
-f lavfi -i anullsrc=r=48000:cl=mono \
-vf "drawtext=fontfile=${FONT}:text='Match Live TV':fontsize=64:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2-48,drawtext=fontfile=${FONT}:text='Trasmissione in pausa':fontsize=36:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2+36" \
-c:v libx264 -pix_fmt yuv420p -profile:v baseline -level 3.1 \
-x264-params "keyint=30:min-keyint=30:scenecut=0:bframes=0" \
-g 30 -keyint_min 30 -preset fast \
-c:a aac -b:a 128k -ac 1 -ar 48000 -shortest /slates/offline.mp4
docker run -d --name mediamtx --restart unless-stopped --network host \
--security-opt apparmor=unconfined \
-v /opt/stream-node/mediamtx.yml:/mediamtx.yml:ro \
-v /recordings:/recordings \
-v /slates:/slates:ro \
bluenviron/mediamtx:latest /mediamtx.yml
# smoke locale
if [[ -f /opt/stream-node/relay-agent.py ]]; then
cp /opt/stream-node/relay-agent.service /etc/systemd/system/mltv-relay-agent.service
systemctl daemon-reload
systemctl enable --now mltv-relay-agent.service
fi
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:9997/v3/paths/list >/dev/null; then
echo "mediamtx ready" | tee /opt/stream-node/ready