fix(streaming): bounded ffmpeg auto-restart + tmpdir gc + probe/stderr safety

Reliability hardening pass for the HLS daemon. None of these change the
public API, all reduce the chances of an end-user seeing a broken
session in production.

- engine/hls.go waitFFmpeg now supervises ffmpeg: on a non-graceful
  exit while the session is still in use, restart from the last good
  segment up to 3 times within a 60 s window. Beyond that we give up
  and log the file as broken — better than a perpetually black player
  with no error.
- engine/hls.go CleanupHLSOrphanDirs() removes tmpdirs older than 1 h
  at startup; cmd/daemon.go calls it before streamSrv.Listen so a
  daemon crash + restart doesn't leak gigabytes of segment files.
- engine/hls.go StartHLSSession wraps ffprobe in a 15 s timeout. A
  hung probe on a slow remote fs would otherwise block the goroutine
  forever and the player would stay on "Preparando sesion".
- engine/hls.go hlsStderrCapture buffer is capped at 64 KiB; a
  misbehaving ffmpeg that emits megabytes without newlines used to
  grow daemon memory unbounded.
This commit is contained in:
Deivid Soto 2026-05-08 08:51:19 +02:00
parent eb2548f9a6
commit 40e7977cf5
2 changed files with 108 additions and 3 deletions

View file

@ -226,6 +226,12 @@ func runDaemonStart() error {
// Create persistent stream server
streamSrv := engine.NewStreamServer(cfg.Download.StreamPort)
// Reap HLS tmpdirs left over from a previous daemon run before we start
// accepting new sessions. The in-memory registry doesn't survive a
// restart, so without this disk usage grows unbounded across restarts.
if err := engine.CleanupHLSOrphanDirs(); err != nil {
log.Printf("[hls] orphan tmpdir cleanup: %v", err)
}
if err := streamSrv.Listen(ctx); err != nil {
return fmt.Errorf("start stream server: %w", err)
}