Skip to content

simonchrz/tvheadend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

365 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tvheadend HLS-Gateway

Self-hosted TV-Streaming-Stack: DVB-Aufnahme + HLS-Live + Watch-Player für mobile Browser. Sitzt auf tvheadend als Tuner/EPG/DVR-Backend und liefert eine eigene Flask-basierte Player-UI.

Stack

  • tvheadend — Tuner-Driver (DVB-C/T/S oder SAT>IP), EPG-Grabber, DVR, Autorec. Container-Image z. B. linuxserver/tvheadend.
  • hls-gateway (Flask + waitress, Python 3.13, ffmpeg static) — on-demand HLS-Transcoding pro Kanal, EPG-Archiv, Web-UI, Watch- Player mit Scrub/Pause/Von-Anfang/Mediathek-Fallback, Recording- Player mit Werbe-Ausblendung via tv-detect, virtuelle Mediathek-Aufnahmen für ARD/ZDF.
  • Caddy (oder beliebiger Reverse-Proxy) — TLS-Terminator. Player funktioniert nur über HTTPS, damit Service-Worker / fullscreen laufen.
  • Optional Mac-side daemon (mac-daemon/tv-thumbs-daemon.py) — übernimmt HLS-Remux, Thumbnail-Generierung und Ad-Detection vom Streaming-Host via HTTP-API. Lokaler .ts-Cache, Letterbox-aware cropdetect pro Detect, Whisper-Post-Processor für Boundary-Refinement. Wenn vorhanden: Streaming-Host-CPU für Recording-Verarbeitung dropt um ~98 %, Re-Detects nach Head-Bumps lesen aus Cache statt LAN (~30× schneller).

Container laufen mit network_mode: host damit DVB-Multicast + SAT>IP-Discovery direkt funktionieren.

Funktionen

Live-TV

  • 2 h DVR-Fenster (timeshift), Swipe zum Kanalwechsel
  • Mediathek-Live als Primärquelle für ARD/ZDF — spart Tuner. Fällt automatisch auf DVB zurück, wenn Mediathek-HLS nicht antwortet (8 s Timeout oder hls.js-Error)
  • Live-Werbeerkennung (Privatsender, tv-detect rolling, ~12 min Takt; skippt wenn keine neuen Segmente)
  • Mux-Dots auf der Kanalübersicht zeigen welche Kanäle sich einen Tuner teilen

Aufnahmen

  • tvheadend-DVR: nach Fertigstellung automatisch zu HLS-VOD umgepackt, Werbeblöcke per tv-detect (MLP2-Head 1290→32→1 mit Channel-One-Hot + Logo + Wall-Clock-Prior; aktuell Block-IoU 0.92 / Test-Acc 98.5 % bei n=35 Test-Recordings über 9 Sender), Serien- Autorec über Titel-Regex
  • Eager-Remux + Eager-Thumbs + Eager-Detect: prewarm-loop (alle 120 s) erkennt fertige Aufnahmen ohne HLS/Thumbs/Cutlist, schreibt .requested-Marker in den jeweiligen rec-dir. Optional Mac-Daemon pollt /api/internal/{thumbs,hls,detect}-pending alle 5 s, holt .ts via HTTP, läuft ffmpeg/tv-detect lokal, postet Resultate via PUT zurück. User-Klick auf eine fertige Aufnahme = instant playback
  • Virtuelle Mediathek-Aufnahmen (ARD + ZDF): Long-press auf EPG- Event → Modal bietet „Aus Mediathek speichern", „Jetzt abspielen" oder „DVR (Episode/Serie)". Funktioniert auch für vergangene Sendungen
  • Pre-Expiry-Rip: Mediathek-Aufnahmen werden 48 h vor Ablauf lokal als MP4 gerippt (-c copy, kein Re-encode), überleben so die Mediathek-Verfügbarkeit
  • Series-Autorec + Mediathek-Kombi: beim Setzen einer Serien- Aufnahme werden upcoming Episoden automatisch mit Mediathek-Matches ergänzt, damit Tuner frei bleiben
  • Whisper-Post-Processor (WHISPER_ENABLE=1, daemon-side): nach jeder Detect-Run klassifiziert whisper.cpp deutsche Werbe-Sprache und refined die Block-Boundaries

Bibliothek

  • Netflix-style Show-Tile-Grid (/bibliothek) als Watch-fokussierte Persona-Split von /recordings. Pro Show genau ein Tile mit Latest- Episode-Preview, Folgenzahl und „vor 3 h"-Zeit
  • Cross-Channel-Merge nach normalisiertem Titel — selbe Show auf verschiedenen Sendern = ein Tile, Channel-Chip-Filter behält die Info
  • Filter + Sortierung: Sender-Chips, Filme/Serien/Alle, Datum/A-Z. Bucketing-Priorität: User-Group → Autorec → (Title, Channel) Orphan
  • Movie-vs-Series Klassifikation: User-Group + 🎬 Badge → Film; Autorec → Serie; sonst TMDB kind + ≥80 min Heuristik
  • Multi-Source Poster-Pipeline: TMDB w342 für Filme (Portrait- Plakat), fernsehserien.de hr2-Banner für deutsche Tagesshows, manuelle URL-Pin-Map für TV-Events ohne Eintrag, Slug-Aliases für ambiguous Cases
  • Uniform 2:3 Portrait-Tiles via AppleTV-style blur-bg-Trick: Landscape-Banner kriegen blurred-zoomed Hintergrund + sharp foreground in Native-Aspect, Portraits laufen object-fit:cover
  • Detail-Page mit Hero-Backdrop (gleicher Trick, 21:9 desktop / 16:9 mobile), Episodenliste darunter mit direktem Click-zum-Player
  • Tag-/Nacht-Modus via prefers-color-scheme (folgt System-Theme)

Warm-Tuner + Pin-System

  • Konfigurierbare Pins (📌 auf Startseite), zusätzlich LRU-Cache für zuletzt geschaute
  • Pin-Cap dynamisch nach Tuner-Belegung; Mux-Sharing wird angerechnet (mehrere Kanäle auf gleichem Mux = 1 Tuner)
  • Pin-Idle-Timeout: 6 h ohne Viewer → Pin geht schlafen (💤), nächster Tap weckt
  • Badge pro Kanal zeigt Puffergröße (grün=LRU, blau=pinned); Klick auf grün stoppt den Tuner
  • Header zeigt live 📡 n/N Tuner

EPG

  • Logo-only-Kanal-Spalte, kompakte Zeitachse, Kurzsendungen mit 2- Zeilen-Wrap
  • Long-press öffnet kontextuelles Modal (Episode/Serie/Mediathek je nach Kanal + Vergangenheit)
  • Tap auf past event → gleiches Modal (nicht Live-Stream)
  • Serien-Gruppierung im Aufnahmen-View mit Episoden-Zähler

Player-Infrastruktur

  • Geteilter CSS/JS-Scaffold, Doppel-Tap links/rechts seek ±10 s
  • Hot-Reload ohne Puffer-Verlust: scp service.py reicht — File- Watcher triggert os.execv, ffmpeg-Kinder werden per PID-Datei adoptiert. Compile-Check schützt vor kaputtem scp
  • iOS-Safari-Workarounds: aria-label statt title (Tap-Tooltip- Bug), forceMseHlsOnAppleDevices für hls.js, Mute-autoplay + Unmute-Overlay

Storage-Layout

Alles unter dem konfigurierten HLS-Root (z. B. /mnt/tv/hls/):

Pfad Zweck
<slug>/ Live-HLS-Segmente pro Kanal
<slug>/.ffmpeg.pid Kind-PID + Startzeit für Re-Adoption nach Hot-Reload
<slug>/.adskip/ Live-Werbeerkennung-Artefakte (tv-detect rolling)
_rec_<uuid>/ tvheadend-Aufnahmen umgepackt (HLS-VOD + Cutlist + Thumbs)
_rec_<uuid>/.{hls,detect}-requested, thumbs/.requested Marker-Files für Eager-Pipeline
_rec_<uuid>/{ads_user,ads,pseudo_labels,bumpers}.json User-Edits, Auto-Cutlist, Self-Training-Pseudo-Labels, Bumper-Detector-Output
_<mt_uuid>/file.mp4 gerippte Mediathek-MP4s
.tvd-models/ tv-detect NN-Head (165 KB MLP1 v1: 1290→32→1 ReLU+Sigmoid mit Channel-One-Hot) + MobileNetV2-Backbone + Sidecars (calibration, channel-map, test-set, history, uncertain)
.tvd-logos/<slug>.logo.txt per-Channel Logo-Templates
.tvd-bumpers/<slug>/{start,end}/*.png per-Channel Ad-Bumper-Templates für Boundary-Snap
.minute_prior_by_channel.json Wall-Clock-Prior P(ad | minute, channel)
.channel-config.json, .detection_learning.json, .block_length_prior*.json Per-Channel Tuning-State + Boundary-Drift-Learning + Block-Length-Prior
.always_warm.json Pin-State (persistent)
.mediathek_recordings.json Virtuelle Mediathek-Aufnahmen (Metadaten + HLS-URL + rip-Pfad)
.live_ads.json Erkannte Werbeblöcke pro Kanal (überlebt Hot-Reload)
.epg_archive.jsonl EPG-Archiv mit Auto-Compaction
.epg_meta.json TMDB/fernsehserien Metadaten-Cache für Bibliothek-Tiles
.usage_stats.json Start-Count + Watch-Hours pro Kanal
.codec_cache.json ffprobe-Ergebnisse (H.264 vs MPEG-2)

Entwicklung

Nach Änderung an service.py reicht ein scp auf den Streaming-Host. Ein File-Watcher im Container erkennt die Änderung und ruft os.execv auf, um sich an Ort und Stelle durch die neue Version zu ersetzen. Die laufenden ffmpeg-Prozesse (mit start_new_session=True gestartet und per PID-Datei getrackt) werden vom neuen Prozess wieder adoptiert — der 2-h-DVR-Puffer bleibt erhalten. Ein docker restart zerstört dagegen das cgroup und killt damit auch die ffmpegs.

scp service.py <host>:/path/to/hls-gateway/service.py
# optional explizit: docker kill -s HUP hls-gateway

Beim Bauen des Image (z. B. ffmpeg-Update) — Puffer wird dabei resettet:

ssh <host> 'cd /path/to/hls-gateway && docker compose build && docker compose up -d'

Ports

Service Port Zweck
Caddy 8443 HTTPS + HTTP/2
tvheadend 9981 Web-UI + API
tvheadend 9982 HTSP
hls-gateway 8080 Flask (intern)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors