diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs index 2221367a..fb4a01be 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs @@ -704,7 +704,7 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe return result; } - + private List AddDurationFiller( PlayoutBuilderState playoutBuilderState, IMediaCollectionEnumerator enumerator, @@ -715,17 +715,50 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe CancellationToken cancellationToken) { var result = new List(); + var candidatePlayoutItems = new List(); - TimeSpan remainingToFill = duration; - while (enumerator.Current.IsSome && remainingToFill > TimeSpan.Zero && - remainingToFill >= enumerator.MinimumDuration) + // First pass: Collect all possible playout items from available media + var processedMediaItemIds = new System.Collections.Generic.HashSet(); + + while (enumerator.Current.IsSome) { + bool foundNewItem = false; + foreach (MediaItem mediaItem in enumerator.Current) { + // Skip if we've already processed this media item + if (processedMediaItemIds.Contains(mediaItem.Id)) + continue; + + processedMediaItemIds.Add(mediaItem.Id); + foundNewItem = true; + TimeSpan itemDuration = DurationForMediaItem(mediaItem); + List itemChapters = ChaptersForMediaItem(mediaItem); - if (remainingToFill - itemDuration >= TimeSpan.Zero) + if (itemChapters.Count > 1) + { + // Add each chapter as a separate candidate + foreach (MediaChapter chapter in itemChapters) + { + TimeSpan chapterDuration = chapter.EndTime - chapter.StartTime; + var playoutItem = new PlayoutItem + { + MediaItemId = mediaItem.Id, + Start = new DateTime(2020, 2, 1, 0, 0, 0, DateTimeKind.Utc), + Finish = new DateTime(2020, 2, 1, 0, 0, 0, DateTimeKind.Utc) + chapterDuration, + InPoint = chapter.StartTime, + OutPoint = chapter.EndTime, + GuideGroup = playoutBuilderState.NextGuideGroup, + FillerKind = fillerKind, + DisableWatermarks = !allowWatermarks + }; + candidatePlayoutItems.Add(playoutItem); + } + } + else { + // Add the full media item as a candidate var playoutItem = new PlayoutItem { MediaItemId = mediaItem.Id, @@ -737,22 +770,43 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe FillerKind = fillerKind, DisableWatermarks = !allowWatermarks }; - - remainingToFill -= itemDuration; - result.Add(playoutItem); - enumerator.MoveNext(); + candidatePlayoutItems.Add(playoutItem); } - else - { - if (log) - { - Logger.LogDebug( - "Filler item is too long {FillerDuration:g} to fill {GapDuration:g}; skipping to next filler item", - itemDuration, - remainingToFill); - } + } + + // If we didn't find any new items, we've seen all items - break the loop + if (!foundNewItem) + break; + + enumerator.MoveNext(); + } + + // Shuffle all candidate playout items + var random = new Random(); + candidatePlayoutItems = candidatePlayoutItems.OrderBy(x => random.Next()).ToList(); + + // Second pass: Add shuffled items until duration is filled + TimeSpan remainingToFill = duration; + foreach (var playoutItem in candidatePlayoutItems) + { + if (remainingToFill <= TimeSpan.Zero) + break; - enumerator.MoveNext(); + TimeSpan itemDuration = playoutItem.Finish - playoutItem.Start; + + if (remainingToFill >= itemDuration) + { + remainingToFill -= itemDuration; + result.Add(playoutItem); + } + else + { + if (log) + { + Logger.LogDebug( + "Filler item is too long {FillerDuration:g} to fill {GapDuration:g}; skipping to next filler item", + itemDuration, + remainingToFill); } } }