Browse Source

fix: block playout build loop (#2957)

pull/2958/head
Jason Dove 6 days ago committed by GitHub
parent
commit
a2f54ef1bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 14
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs
  3. 23
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs

3
CHANGELOG.md

@ -29,11 +29,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Content that can never play was still counted as waiting to play, so that never happened and the order stayed the same forever - Content that can never play was still counted as waiting to play, so that never happened and the order stayed the same forever
- This was caused by specials (season 0), which are skipped by `Season, Episode` order; marathons grouped by show or season use that order unless `Marathon Shuffle Items` is enabled - This was caused by specials (season 0), which are skipped by `Season, Episode` order; marathons grouped by show or season use that order unless `Marathon Shuffle Items` is enabled
- Affected playouts also became slower to build the longer they ran - Affected playouts also became slower to build the longer they ran
- Fix shuffled playlists and marathon groups changing order when a playout is rebuilt - Fix shuffled playlists and marathon groups changing order when a playout is rebuilt (continued or refreshed)
- Existing playlists and marathons will change order once after updating - Existing playlists and marathons will change order once after updating
- Fix playlists used as filler skipping content - Fix playlists used as filler skipping content
- When something doesn't fit, ETV puts the filler back where it was and tries again, but playlists were only partly put back - When something doesn't fit, ETV puts the filler back where it was and tries again, but playlists were only partly put back
- This could skip playlist content, and could cause the next playout build to resume in the wrong place - This could skip playlist content, and could cause the next playout build to resume in the wrong place
- Fix case where block playouts would occasionally get stuck building forever
- Fix green line sometimes seen with NVIDIA and AMD/VAAPI encoding - Fix green line sometimes seen with NVIDIA and AMD/VAAPI encoding
- Both bugs were in ffmpeg, and ETV's patched ffmpeg 8.1.2 is required for the fixes - Both bugs were in ffmpeg, and ETV's patched ffmpeg 8.1.2 is required for the fixes

14
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs

@ -217,8 +217,13 @@ public class BlockPlayoutBuilder(
if (effectiveBlock.Block.StopScheduling is BlockStopScheduling.BeforeDurationEnd && if (effectiveBlock.Block.StopScheduling is BlockStopScheduling.BeforeDurationEnd &&
itemDuration > blockDuration) itemDuration > blockDuration)
{ {
foreach (TimeSpan minimumDuration in enumerator.MinimumDuration) // enumerator.MinimumDuration is none for images and remote streams, which
{ // would skip forever, so use the same duration source as the check above
TimeSpan minimumDuration = collectionMediaItems[CollectionKey.ForBlockItem(blockItem)]
.Map(i => i.GetDurationForPlayout())
.OrderBy(identity)
.Head();
if (minimumDuration > blockDuration) if (minimumDuration > blockDuration)
{ {
Logger.LogError( Logger.LogError(
@ -227,11 +232,6 @@ public class BlockPlayoutBuilder(
blockDuration); blockDuration);
done = true; done = true;
}
}
if (done)
{
break; break;
} }

23
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs

@ -34,13 +34,19 @@ public class BlockPlayoutFillerBuilder(
var allItems = result.AddedItems.ToList(); var allItems = result.AddedItems.ToList();
var removeBefore = await result.RemoveBefore.IfNoneAsync(DateTimeOffset.MaxValue); var removeBefore = await result.RemoveBefore.IfNoneAsync(DateTimeOffset.MaxValue);
// existing items that finish before this are deleted from the database by this build,
// so they must not be treated as existing items here
DateTimeOffset deletedBefore = await result.RemoveBefore
.Map(rb => rb - referenceData.MaxPlayoutOffset)
.IfNoneAsync(DateTimeOffset.MinValue);
if (mode is PlayoutBuildMode.Reset) if (mode is PlayoutBuildMode.Reset)
{ {
// remove all playout items with type filler // remove all playout items with type filler
// except block items that are hidden from the guide (guide mode) // except block items that are hidden from the guide (guide mode)
foreach (PlayoutItem item in filteredExistingItems) foreach (PlayoutItem item in filteredExistingItems)
{ {
if (item.Finish < removeBefore) if (item.FinishOffset < removeBefore)
{ {
continue; continue;
} }
@ -90,6 +96,7 @@ public class BlockPlayoutFillerBuilder(
allItems = referenceData.ExistingItems allItems = referenceData.ExistingItems
.Where(i => !result.ItemsToRemove.Contains(i.Id)) .Where(i => !result.ItemsToRemove.Contains(i.Id))
.Where(i => i.FinishOffset >= deletedBefore)
.ToList(); .ToList();
allItems.AddRange(result.AddedItems); allItems.AddRange(result.AddedItems);
@ -348,12 +355,24 @@ public class BlockPlayoutFillerBuilder(
DateTimeOffset current = start; DateTimeOffset current = start;
var pastTime = false; var pastTime = false;
while (current < finish) while (current < finish && !cancellationToken.IsCancellationRequested)
{ {
foreach (MediaItem mediaItem in enumerator.Current) foreach (MediaItem mediaItem in enumerator.Current)
{ {
TimeSpan itemDuration = mediaItem.GetDurationForPlayout(); TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
// without a duration this loop can never reach the end of the gap
if (itemDuration <= TimeSpan.Zero)
{
logger.LogWarning(
"Default filler item {Item} has no duration; will stop filling gap at {Time}",
mediaItem.Id,
current);
pastTime = true;
break;
}
// add filler from deco to unscheduled period // add filler from deco to unscheduled period
var filler = new PlayoutItem var filler = new PlayoutItem
{ {

Loading…
Cancel
Save