1359bb613526f48f4c88f065330c7d76f1eb9318
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 11s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 16s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 15s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 1m19s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 1m20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m52s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 15m14s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 19m12s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
`-readrate 1.05` paces input reading at wall clock so a channel behaves like live TV, but it applies from the very first read. With 4s HLS segments and the segmenter waiting for the first one, the playlist could not appear sooner than ~4/1.05 = 3.8s, so every tune-in that did not win a work-ahead slot paid a multi-second wait. Add `-readrate_initial_burst` (FFmpeg >= 6.1) next to `-readrate` on the normal playback path, gated on runtime capability detection via the existing `FFmpegKnownOption`/`HasOption` machinery, whose option list had simply been empty. Measured on real prod media: time-to-first-playlist 5369/5344ms -> 648/649ms. Root cause detail: the cold-start bimodality earlier rounds could not explain was never about the media. `HlsSessionWorker` grants an unthrottled start only while `_workAheadCount < work_ahead_limit` (prod: 1), so concurrent tune-ins fall back to the throttled path. Confirmed on prod with three concurrent tunes: firstGop 866ms for the slot winner vs 3845ms and 6357ms. This also falsifies the issue's ranked #1 driver — accurate-seek decode-discard measures 30-100ms on real media, and probe caps 20-50ms; neither can account for seconds. Still images are excluded: their video input is paced by the realtime filter and takes no readrate, so a burst would only run a song's separate audio input ahead of the video. Concat/WrapSegmenter keep the unburst single-arg constructor. fixes #350
ci(406): disable swap on the capped mysql; fix peak-step placement and failure mode [decisions-edit]
Merge pull request 'fix(497): reconcile music-video metadata collections on Jellyfin rescan' (#499) from fix/497-musicvideo-metadata-update into main
Merge pull request 'chore(deps): update dependency humanizer.core to 3.0.10' (#328) from renovate/humanizer-monorepo into main
ErsatzTV
ErsatzTV lets you transform your media library into a personalized, live TV experience - complete with EPG, channel scheduling, and seamless streaming to all your devices. Rediscover your content, your way.
How It Works
- Install ErsatzTV: Download and set up the server on your system.
- Add Your Media: Connect your media libraries and collections.
- Create Channels: Design and schedule your own live channels.
- Stream Anywhere: Watch on any device with IPTV and EPG support.
Key Features
- Custom channels: Create and schedule your own live TV channels.
- IPTV & EPG: Stream with IPTV and Electronic Program Guide support.
- Hardware Transcoding: High-performance streaming with hardware acceleration (NVENC, QSV, VAAPI, AMF, VideoToolbox)
- Media Server Integration: Connect Plex, Jellyfin, Emby and more.
- Music & Subtitles: Mix music videos and enjoy subtitle support.
- Open Source: Free, open, and community-driven project.
Documentation
Documentation is available at ersatztv.org.
License
This project is inspired by pseudotv-plex and the dizquetv fork and is released under the zlib license.
