ErsatzTV Guard-Script: Transcoding-Ordner vor dem Zumüllen schützen

IM EINSATZ?

Dann schau dir UNSEREN LOXKURS an und profitiere von unserem Wissen!

Wenn du ErsatzTV mit MPEG-TS und kontinuierlichem Stream betreibst so wie ich, kennst du das Problem vermutlich: Im Hintergrund laufende Streams erzeugen große temporäre Dateien, die die Platte nach und nach zumüllen können. Ein manueller Neustart ist mühsam und ohne Gegenmaßnahme stürzt der ErsatzTV-Service einfach irgendwann ab.

Deshalb stelle ich in nachfolgendem Blogpost mein Guard-Script vor, welches bei zu großem Speicherverbrauch automatisch Transcodingsessions stoppt und damit einen stabilen Betrieb ohne Abstürze ermöglicht.

Nachdem mein ErsatzTV-Service nach einer längeren Streaming-Session auf der VU+ (gestrige Anleitung hier) heute Abend abgestürzt ist und ich den Fehler nach etwas Recherche identifizieren konnte, habe ich kurzerhand ein Script gebaut (ok, ChatGPT hat mir sehr dabei geholfen), welches das Problem recht schick löst.

Aber was macht das Script am Ende überhaupt?

Guard-Script 4 the win!

  • Läuft periodisch per systemd-Timer (z. B. jede Minute).
  • Prüft die Root-Dateisystem-Auslastung (Fallback).
  • Misst die Größe pro Kanal-Transcoding-Ordner (z. B. etv-transcode/500).
  • Überschreitet ein Ordner den Schwellwert (z. B. größer 5 GB), wird die Session über die ErsatzTV-API gestoppt.
  • Loggt alle Aktionen in /var/log/etv-guard.log.

So bleibt das System sauber, ohne dass Streams unkontrolliert weiter wachsen.

Warum das sinnvoll ist (insb. MPEG-TS & Hintergrund-Streams)

  • Bei MPEG-TS gibt es keine Segment-Rotation wie bei HLS; die temporären Dateien wachsen kontinuierlich, solange der Stream läuft.
  • Besonders Hintergrund-Streams (nicht aktiv auf der Vu+ getuned) laufen oft unbemerkt weiter und fressen Platz; aktive Foreground-Streams verursachen deutlich weniger Temp-Last.
  • Das Script wirkt wie ein Garbage-Collector: erkennt Überlauf → stoppt gezielt die betroffene Session → Temp-Ordner wird freigegeben.
  • Fallback stoppt alle Sessions, wenn das Root-FS kritisch wird, damit die Box nicht hart wegstirbt.

Vorbereitung

  • ErsatzTV-API erreichbar (Standard: http://localhost:8409).
  • Transcoding-Basisordner vorhanden (üblich: /root/.local/share/etv-transcode).
  • Nutzer darf ins Log schreiben und die API aufrufen.

Schritt-für-Schritt-Anleitung

1) Script anlegen

Datei /usr/local/bin/etv-guard.sh erstellen:

nano /usr/local/bin/etv-guard.sh

und mit dem Inhalt füttern:

#!/bin/bash
set -euo pipefail

# --- Einstellungen ---
TRANSCODE_DIR="/root/.local/share/etv-transcode"   # Basisordner: <TRANSCODE_DIR>/<channel>
API_BASE="http://localhost:8409"                   # ErsatzTV-API
THRESHOLD_GB=5                                     # pro Channel-Ordner: ab dieser Größe stoppen
GLOBAL_FS_PCT=92                                   # Fallback: wenn Root-FS-Belegung >= %, greife hart ein
MIN_FREE_GB=2                                      # Alternativ-Fallback: wenn freier Platz < GB
COOLDOWN_SEC=60                                    # Cooldown zwischen Stop-Befehlen pro Channel
LOGFILE="/var/log/etv-guard.log"

mkdir -p "$(dirname "$LOGFILE")"

now() { date '+%Y-%m-%d %H:%M:%S'; }
log() { echo "[$(now)] $*" | tee -a "$LOGFILE"; }

bytes_to_gb() { awk -v b="$1" 'BEGIN{printf "%.2f", b/1024/1024/1024}'; }

# --- Fallback: Root-FS prüfen ---
USED_PCT=$(df --output=pcent / | tail -1 | tr -dc '0-9')
FREE_GB=$(df --output=avail -B1 / | tail -1)
FREE_GB=$(( FREE_GB / 1024 / 1024 / 1024 ))

log "Check start: Root used=${USED_PCT}% free=${FREE_GB}GB"

if [[ "$USED_PCT" -ge "$GLOBAL_FS_PCT" || "$FREE_GB" -lt "$MIN_FREE_GB" ]]; then
  log "WARN Root-FS kritisch (used=${USED_PCT}% free=${FREE_GB}GB) → stoppe ALLE Sessions (Safety Cutoff)."
  if [[ -d "$TRANSCODE_DIR" ]]; then
    for d in "$TRANSCODE_DIR"/*/; do
      [[ -d "$d" ]] || continue
      ch=$(basename "$d")
      [[ "$ch" =~ ^[0-9]+$ ]] || continue
      curl -sS -X DELETE "$API_BASE/api/session/$ch" || true
      log "STOP (global) channel=$ch"
      sleep 1
    done
  fi
  log "Check end (global cutoff)."
  exit 0
fi

# --- Pro Channel prüfen ---
[[ -d "$TRANSCODE_DIR" ]] || { log "Transcode-Verzeichnis fehlt ($TRANSCODE_DIR)"; exit 0; }

for d in "$TRANSCODE_DIR"/*/; do
  [[ -d "$d" ]] || continue
  ch=$(basename "$d")
  [[ "$ch" =~ ^[0-9]+$ ]] || continue

  size_bytes=$(du -sb "$d" | awk '{print $1}')
  size_gb=$(bytes_to_gb "$size_bytes")

  log "Channel $ch size=${size_gb}GB"

  if awk "BEGIN{exit !($size_gb >= $THRESHOLD_GB)}"; then
    log "WARN channel=$ch dir_size=${size_gb}GB ≥ ${THRESHOLD_GB}GB → stoppe Session über API."
    if curl -sS -X DELETE "$API_BASE/api/session/$ch" >/dev/null 2>&1; then
      log "OK   STOP channel=$ch"
      sleep "$COOLDOWN_SEC"
    else
      log "ERR  STOP channel=$ch fehlgeschlagen"
    fi
  fi
done

log "Check end."
exit 0

Rechte setzen:

chmod +x /usr/local/bin/etv-guard.sh

2) systemd-Service & Timer

Service /etc/systemd/system/etv-guard.service erstellen:

nano /etc/systemd/system/etv-guard.service

und mit dem Inhalt füllen:

[Unit]
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/etv-guard.sh
Nice=10

Timer erstellen in /etc/systemd/system/etv-guard.timer:

nano /etc/systemd/system/etv-guard.timer

Und mit dem Inhalt füllen:

[Unit]
Description=Run etv-guard every minute

[Timer]
OnBootSec=1min
OnUnitActiveSec=1min
AccuracySec=10s
Unit=etv-guard.service

[Install]
WantedBy=timers.target

Das Script „aktivieren“:

systemctl daemon-reload
systemctl enable --now etv-guard.timer
systemctl list-timers | grep etv-guard

Test & Monitoring

  • Manuell starten:
  systemctl start etv-guard.service
  • Log prüfen:
  tail -n 50 /var/log/etv-guard.log
  • Timer prüfen:
  systemctl list-timers | grep etv-guard

Mit dem Befehl

tail -f /var/log/etv-guard.log

sollten nun regelmäßig neue Einträge zu sehen sein. Sofern das Script einen Stream automatisch neugestartet hat (hier mal im Test auf max. 0,65GB gestellt), sollte es so aussehen:

root@ersatztv:~# tail -f /var/log/etv-guard.log
[2025-09-24 01:39:27] Check start: Root used=5% free=57GB
[2025-09-24 01:39:27] Channel 500 size=0.05GB
[2025-09-24 01:39:27] Channel 501 size=0.64GB
[2025-09-24 01:39:27] Check end.
[2025-09-24 01:40:37] Check start: Root used=5% free=57GB
[2025-09-24 01:40:37] Channel 500 size=0.04GB
[2025-09-24 01:40:37] Channel 501 size=0.68GB
[2025-09-24 01:40:37] WARN channel=501 dir_size=0.68GB ≥ 0.65GB → stoppe Session über API.
[2025-09-24 01:40:37] OK   STOP channel=501
[2025-09-24 01:41:37] Check end.
[2025-09-24 01:41:37] Check start: Root used=4% free=57GB
[2025-09-24 01:41:37] Channel 500 size=0.05GB
[2025-09-24 01:41:37] Channel 501 size=0.00GB
[2025-09-24 01:41:37] Check end.

Optional: Automatischer Neustart von Kanälen

Theoretisch ließe sich das Script auch noch so erweitern, dass ein gestoppter Kanal direkt wieder per ErsatzTV-API gestartet wird. Dann bleibt der Kanal ohne Umschalt-Verzögerung verfügbar.

In der Praxis beginnt damit aber sofort wieder die Transcoding-Last und die Temp-Dateien wachsen von vorn. Für Vu+-Setups ist ein Neustart meist nicht notwendig, weil die Box beim Umschalten ohnehin einen frischen Stream anfordert. In diesem Fall dauert es einfach einige Sekunden, bis das Bild „kommt“ – aber so what…

Aus meinem täglichen Leben

Mit dem Guard-Script bekommt man am Ende eine robuste „Sicherheitsleine“, die ErsatzTV gerade bei MPEG-TS deutlich stabiler macht. Hintergrund-Streams ballern nicht mehr unkontrolliert die Platte voll, und bei kritischem Füllstand greift der Fallback, bevor es hässlich wird.

Evtl. kommt diese Funktion ja irgendwann in einem ErsatzTV-Update, wobei ich glaube, dass der hier beschriebene Anwendungsfall mit MPEG-TS in Kombination mit der VU+ schon sehr speziell ist.

Vielleicht teste ich auch mal einen anderen Mediaplayer auf der VU+, der feste Segmente wie HLS unterstützt. Denn damit sollte das Problem eigentlich gar nicht erst auftreten. Aber das soll dann laut Forenberichten wiederum nicht so stabil laufen wie MPEG-TS.

Wer mehr Ahnung davon hat, ist gerne eingeladen, sein Wissen per Kommentarfunktion zu teilen. Denn evtl. gibt es ja ein noch besser funktionierendes Setup…

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Das könnte dir auch gefallen