From db7e30aa8538f7df1bf248be36c814f2eefed0b2 Mon Sep 17 00:00:00 2001 From: N73311 <61464942+N73311@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:58:10 -0400 Subject: [PATCH] fix: don't write an empty hls playlist on long sessions --- .../FFmpeg/HlsPlaylistFilterTests.cs | 40 +++++++++++++++++++ ErsatzTV.Core/FFmpeg/HlsPlaylistFilter.cs | 8 ++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/ErsatzTV.Core.Tests/FFmpeg/HlsPlaylistFilterTests.cs b/ErsatzTV.Core.Tests/FFmpeg/HlsPlaylistFilterTests.cs index 882c4fbc2..ad6777624 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/HlsPlaylistFilterTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/HlsPlaylistFilterTests.cs @@ -996,6 +996,46 @@ live_1760874038_000056.m4s result.Playlist.ShouldBe(expectedPlaylist); } + [Test] + public void HlsPlaylistFilter_ShouldKeepSegments_WhenNoneAreNewerThanFilterBefore() + { + // long session: the transcode has fallen behind and every segment is now older + // than filterBefore. trimming used to remove all of them and emit a header-only + // playlist with no segments, which leaves the channel unplayable. + var start = new DateTimeOffset(2021, 10, 9, 8, 0, 0, TimeSpan.FromHours(-5)); + string[] input = NormalizeLineEndings( + @"#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:4 +#EXT-X-MEDIA-SEQUENCE:1137 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-DISCONTINUITY +#EXT-X-MAP:URI=""1760874038_init.mp4"" +#EXTINF:4.000000, +#EXT-X-PROGRAM-DATE-TIME:2021-10-08T08:34:49.320-0500 +live_1760874038_001137.m4s +#EXTINF:4.000000, +#EXT-X-PROGRAM-DATE-TIME:2021-10-08T08:34:53.320-0500 +live_1760874038_001138.m4s +#EXTINF:4.000000, +#EXT-X-PROGRAM-DATE-TIME:2021-10-08T08:34:57.320-0500 +live_1760874038_001139.m4s").Split(Environment.NewLine); + + TrimPlaylistResult result = _hlsPlaylistFilter.TrimPlaylistWithDiscontinuity( + new Dictionary + { + [1760874038] = 1 + }, + OutputFormatKind.HlsMp4, + start, + start.AddSeconds(60), + new FakeHlsInitSegmentCache(), + input); + + result.SegmentCount.ShouldBeGreaterThan(0); + result.Playlist.ShouldContain("#EXTINF"); + } + private static string NormalizeLineEndings(string str) => str .Replace("\r\n", "\n") diff --git a/ErsatzTV.Core/FFmpeg/HlsPlaylistFilter.cs b/ErsatzTV.Core/FFmpeg/HlsPlaylistFilter.cs index 263fb8f3e..740ce1c5b 100644 --- a/ErsatzTV.Core/FFmpeg/HlsPlaylistFilter.cs +++ b/ErsatzTV.Core/FFmpeg/HlsPlaylistFilter.cs @@ -148,8 +148,10 @@ public class HlsPlaylistFilter(ITempFilePool tempFilePool, ILogger().ToList(); - // still need to filter old content - if (maybeMaxSegments.IsNone) + // still need to filter old content, but never down to an empty playlist: + // if nothing is newer than filterBefore, keep the existing segments + // (the maxSegments branch below keeps a non-empty tail the same way) + if (maybeMaxSegments.IsNone && allSegments.Any(s => s.StartTime >= filterBefore)) { allSegments.RemoveAll(s => s.StartTime < filterBefore); } @@ -268,7 +270,7 @@ public class HlsPlaylistFilter(ITempFilePool tempFilePool, ILogger().Min(s => s.GeneratedAt), + items.OfType().Select(s => s.GeneratedAt).DefaultIfEmpty(generatedAt).Min(), allSegments.Count); }