From 7e38297d6ed240f784d388696e687f781e550a71 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Thu, 23 Apr 2026 11:44:19 -0500 Subject: [PATCH] fix: schedule all required fallback filler in classic schedules (#2867) --- .../PlayoutModeSchedulerBaseTests.cs | 172 ++++++++++++++++++ .../PlayoutModeSchedulerDurationTests.cs | 16 +- .../PlayoutModeSchedulerFloodTests.cs | 24 ++- .../PlayoutModeSchedulerMultipleTests.cs | 24 ++- .../PlayoutModeSchedulerOneTests.cs | 21 ++- .../Scheduling/PlayoutModeSchedulerBase.cs | 62 +++++-- 6 files changed, 290 insertions(+), 29 deletions(-) diff --git a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerBaseTests.cs b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerBaseTests.cs index 528b4d303..eb9a64c35 100644 --- a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerBaseTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerBaseTests.cs @@ -731,6 +731,163 @@ public class PlayoutModeSchedulerBaseTests : SchedulerTestBase playoutItems[4].MediaItemId.ShouldBe(6); playoutItems[4].StartOffset.ShouldBe(startState.CurrentTime + TimeSpan.FromMinutes(55)); } + + [Test] + public void Should_Schedule_Multiple_Fallback_Items_For_Pad_Gap() + { + // content 45 min, post roll pad to 60 with an empty collection, remaining 15 min filled by 2 min fallback items + // fallback has 2 min items, so 8 items will be scheduled. + + Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromMinutes(45)); + Collection collectionTwo = new Collection { Id = 2, MediaItems = new List() }; + Collection collectionThree = TwoItemCollection(5, 6, TimeSpan.FromMinutes(2)); + + var scheduleItem = new ProgramScheduleItemOne + { + Id = 1, + Index = 1, + Collection = collectionOne, + CollectionId = collectionOne.Id, + StartTime = null, + PlaybackOrder = PlaybackOrder.Chronological, + TailFiller = null, + FallbackFiller = new FillerPreset + { + FillerKind = FillerKind.Fallback, + CollectionId = 3, + Collection = collectionThree + }, + PostRollFiller = new FillerPreset + { + FillerKind = FillerKind.PostRoll, + FillerMode = FillerMode.Pad, + PadToNearestMinute = 60, + CollectionId = 2, + Collection = collectionTwo + } + }; + + var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator( + new List { scheduleItem }, + new CollectionEnumeratorState()); + + var enumerator = new ChronologicalMediaCollectionEnumerator( + collectionOne.MediaItems, + new CollectionEnumeratorState()); + + var postRollFillerEnumerator = new ChronologicalMediaCollectionEnumerator( + collectionTwo.MediaItems, + new CollectionEnumeratorState()); + + var fallbackFillerEnumerator = new ChronologicalMediaCollectionEnumerator( + collectionThree.MediaItems, + new CollectionEnumeratorState()); + + PlayoutBuilderState startState = StartState(scheduleItemsEnumerator); + + Dictionary enumerators = CollectionEnumerators( + scheduleItem, + enumerator); + + enumerators.Add(CollectionKey.ForFillerPreset(scheduleItem.PostRollFiller), postRollFillerEnumerator); + enumerators.Add(CollectionKey.ForFillerPreset(scheduleItem.FallbackFiller), fallbackFillerEnumerator); + + List playoutItems = _scheduler + .AddFiller( + startState, + enumerators, + scheduleItem, + new PlayoutItem + { + MediaItemId = 1, + Start = startState.CurrentTime.UtcDateTime, + Finish = startState.CurrentTime.AddMinutes(45).UtcDateTime + }, + new List(), + new PlayoutBuildWarnings(), + _cancellationToken); + + playoutItems.Count.ShouldBe(9); // 1 primary + 8 fallback + + // primary + playoutItems[0].MediaItemId.ShouldBe(1); + playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); + + // fallback 1 + playoutItems[1].MediaItemId.ShouldBe(5); + playoutItems[1].StartOffset.ShouldBe(startState.CurrentTime + TimeSpan.FromMinutes(45)); + + // fallback 8 (cut short) + playoutItems[8].MediaItemId.ShouldBe(6); + playoutItems[8].StartOffset.ShouldBe(startState.CurrentTime + TimeSpan.FromMinutes(59)); + (playoutItems[8].Finish - playoutItems[8].Start).ShouldBe(TimeSpan.FromMinutes(1)); + playoutItems[8].OutPoint.ShouldBe(TimeSpan.FromMinutes(1)); + } + } + + [TestFixture] + public class AddFallback : PlayoutModeSchedulerBaseTests + { + [Test] + public void Should_Schedule_Multiple_Fallback_Items_For_Large_Gap() + { + Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromMinutes(2)); + + var scheduleItem = new ProgramScheduleItemOne + { + Id = 1, + Index = 1, + FallbackFiller = new FillerPreset + { + FillerKind = FillerKind.Fallback, + CollectionId = 1, + Collection = collectionOne + } + }; + + var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator( + new List { scheduleItem }, + new CollectionEnumeratorState()); + + var enumerator = new ChronologicalMediaCollectionEnumerator( + collectionOne.MediaItems, + new CollectionEnumeratorState()); + + PlayoutBuilderState startState = StartState(scheduleItemsEnumerator); + + Dictionary enumerators = CollectionEnumerators( + scheduleItem, + enumerator); + + enumerators.Add(CollectionKey.ForFillerPreset(scheduleItem.FallbackFiller), enumerator); + + DateTimeOffset nextItemStart = startState.CurrentTime.AddMinutes(5); + + Tuple> result = ((TestScheduler)_scheduler).TestAddFallbackFiller( + startState, + enumerators, + scheduleItem, + new List(), + nextItemStart, + _cancellationToken); + + List playoutItems = result.Item2; + + playoutItems.Count.ShouldBe(3); + + playoutItems[0].MediaItemId.ShouldBe(1); + playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); + (playoutItems[0].Finish - playoutItems[0].Start).ShouldBe(TimeSpan.FromMinutes(2)); + + playoutItems[1].MediaItemId.ShouldBe(2); + playoutItems[1].StartOffset.ShouldBe(startState.CurrentTime.AddMinutes(2)); + (playoutItems[1].Finish - playoutItems[1].Start).ShouldBe(TimeSpan.FromMinutes(2)); + + playoutItems[2].MediaItemId.ShouldBe(1); + playoutItems[2].StartOffset.ShouldBe(startState.CurrentTime.AddMinutes(4)); + (playoutItems[2].Finish - playoutItems[2].Start).ShouldBe(TimeSpan.FromMinutes(1)); + playoutItems[2].OutPoint.ShouldBe(TimeSpan.FromMinutes(1)); + } } [TestFixture] @@ -800,6 +957,21 @@ public class PlayoutModeSchedulerBaseTests : SchedulerTestBase CancellationToken cancellationToken) => throw new NotSupportedException(); + public Tuple> TestAddFallbackFiller( + PlayoutBuilderState playoutBuilderState, + Dictionary collectionEnumerators, + ProgramScheduleItem scheduleItem, + List playoutItems, + DateTimeOffset nextItemStart, + CancellationToken cancellationToken) => + AddFallbackFiller( + playoutBuilderState, + collectionEnumerators, + scheduleItem, + playoutItems, + nextItemStart, + cancellationToken); + protected override string SchedulingContextName => "Test"; } } diff --git a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerDurationTests.cs b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerDurationTests.cs index 1520748b2..6b29d727d 100644 --- a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerDurationTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerDurationTests.cs @@ -388,7 +388,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase enumerator1.State.Index.ShouldBe(1); enumerator2.State.Index.ShouldBe(1); - playoutItems.Count.ShouldBe(4); + playoutItems.Count.ShouldBe(18); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -413,6 +413,12 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase playoutItems[3].GuideGroup.ShouldBe(3); playoutItems[3].FillerKind.ShouldBe(FillerKind.Fallback); playoutItems[3].GuideFinish.HasValue.ShouldBeFalse(); + + playoutItems[17].MediaItemId.ShouldBe(3); + playoutItems[17].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 59, 0))); + playoutItems[17].GuideGroup.ShouldBe(3); + playoutItems[17].FillerKind.ShouldBe(FillerKind.Fallback); + playoutItems[17].GuideFinish.HasValue.ShouldBeFalse(); } [Test] @@ -694,7 +700,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase enumerator2.State.Index.ShouldBe(1); enumerator3.State.Index.ShouldBe(1); - playoutItems.Count.ShouldBe(7); + playoutItems.Count.ShouldBe(9); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -737,6 +743,12 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase playoutItems[6].GuideGroup.ShouldBe(3); playoutItems[6].FillerKind.ShouldBe(FillerKind.Fallback); playoutItems[6].GuideFinish.HasValue.ShouldBeFalse(); + + playoutItems[8].MediaItemId.ShouldBe(5); + playoutItems[8].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 59, 0))); + playoutItems[8].GuideGroup.ShouldBe(3); + playoutItems[8].FillerKind.ShouldBe(FillerKind.Fallback); + playoutItems[8].GuideFinish.HasValue.ShouldBeFalse(); } [Test] diff --git a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs index be7bbb74e..b01918181 100644 --- a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs @@ -609,7 +609,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase enumerator1.State.Index.ShouldBe(1); enumerator2.State.Index.ShouldBe(1); - playoutItems.Count.ShouldBe(4); + playoutItems.Count.ShouldBe(6); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -630,6 +630,16 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase playoutItems[3].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 45, 0))); playoutItems[3].GuideGroup.ShouldBe(3); playoutItems[3].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[4].MediaItemId.ShouldBe(4); + playoutItems[4].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 50, 0))); + playoutItems[4].GuideGroup.ShouldBe(3); + playoutItems[4].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[5].MediaItemId.ShouldBe(3); + playoutItems[5].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 55, 0))); + playoutItems[5].GuideGroup.ShouldBe(3); + playoutItems[5].FillerKind.ShouldBe(FillerKind.Fallback); } [Test] @@ -814,7 +824,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase enumerator2.State.Index.ShouldBe(1); enumerator3.State.Index.ShouldBe(1); - playoutItems.Count.ShouldBe(7); + playoutItems.Count.ShouldBe(9); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -850,6 +860,16 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase playoutItems[6].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 57, 0))); playoutItems[6].GuideGroup.ShouldBe(3); playoutItems[6].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[7].MediaItemId.ShouldBe(6); + playoutItems[7].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 58, 0))); + playoutItems[7].GuideGroup.ShouldBe(3); + playoutItems[7].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[8].MediaItemId.ShouldBe(5); + playoutItems[8].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 59, 0))); + playoutItems[8].GuideGroup.ShouldBe(3); + playoutItems[8].FillerKind.ShouldBe(FillerKind.Fallback); } [Test] diff --git a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerMultipleTests.cs b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerMultipleTests.cs index a34becbe4..137820f75 100644 --- a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerMultipleTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerMultipleTests.cs @@ -515,7 +515,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase enumerator1.State.Index.ShouldBe(1); enumerator2.State.Index.ShouldBe(1); - playoutItems.Count.ShouldBe(4); + playoutItems.Count.ShouldBe(6); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -536,6 +536,16 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase playoutItems[3].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 45, 0))); playoutItems[3].GuideGroup.ShouldBe(3); playoutItems[3].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[4].MediaItemId.ShouldBe(4); + playoutItems[4].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 50, 0))); + playoutItems[4].GuideGroup.ShouldBe(3); + playoutItems[4].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[5].MediaItemId.ShouldBe(3); + playoutItems[5].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 55, 0))); + playoutItems[5].GuideGroup.ShouldBe(3); + playoutItems[5].FillerKind.ShouldBe(FillerKind.Fallback); } [Test] @@ -723,7 +733,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase enumerator2.State.Index.ShouldBe(1); enumerator3.State.Index.ShouldBe(1); - playoutItems.Count.ShouldBe(7); + playoutItems.Count.ShouldBe(9); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -759,6 +769,16 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase playoutItems[6].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 57, 0))); playoutItems[6].GuideGroup.ShouldBe(3); playoutItems[6].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[7].MediaItemId.ShouldBe(6); + playoutItems[7].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 58, 0))); + playoutItems[7].GuideGroup.ShouldBe(3); + playoutItems[7].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[8].MediaItemId.ShouldBe(5); + playoutItems[8].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 59, 0))); + playoutItems[8].GuideGroup.ShouldBe(3); + playoutItems[8].FillerKind.ShouldBe(FillerKind.Fallback); } [Test] diff --git a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerOneTests.cs b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerOneTests.cs index 7b77f268d..0d1853a61 100644 --- a/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerOneTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerOneTests.cs @@ -306,9 +306,9 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase playoutBuilderState.ScheduleItemsEnumerator.State.Index.ShouldBe(0); enumerator1.State.Index.ShouldBe(1); - enumerator2.State.Index.ShouldBe(1); + enumerator2.State.Index.ShouldBe(0); - playoutItems.Count.ShouldBe(2); + playoutItems.Count.ShouldBe(25); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -319,6 +319,11 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase playoutItems[1].StartOffset.ShouldBe(startState.CurrentTime.AddHours(1)); playoutItems[1].GuideGroup.ShouldBe(1); playoutItems[1].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[24].MediaItemId.ShouldBe(4); + playoutItems[24].StartOffset.ShouldBe(startState.CurrentTime.AddHours(3).Subtract(TimeSpan.FromMinutes(5))); + playoutItems[24].GuideGroup.ShouldBe(1); + playoutItems[24].FillerKind.ShouldBe(FillerKind.Fallback); } [Test] @@ -481,7 +486,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase enumerator2.State.Index.ShouldBe(1); enumerator3.State.Index.ShouldBe(1); - playoutItems.Count.ShouldBe(5); + playoutItems.Count.ShouldBe(7); playoutItems[0].MediaItemId.ShouldBe(1); playoutItems[0].StartOffset.ShouldBe(startState.CurrentTime); @@ -507,6 +512,16 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase playoutItems[4].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 57, 0))); playoutItems[4].GuideGroup.ShouldBe(1); playoutItems[4].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[5].MediaItemId.ShouldBe(6); + playoutItems[5].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 58, 0))); + playoutItems[5].GuideGroup.ShouldBe(1); + playoutItems[5].FillerKind.ShouldBe(FillerKind.Fallback); + + playoutItems[6].MediaItemId.ShouldBe(5); + playoutItems[6].StartOffset.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 59, 0))); + playoutItems[6].GuideGroup.ShouldBe(1); + playoutItems[6].FillerKind.ShouldBe(FillerKind.Fallback); } [Test] diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs index 09c1d3316..c4e41e748 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs @@ -210,19 +210,27 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode IMediaCollectionEnumerator enumerator = collectionEnumerators[CollectionKey.ForFillerPreset(scheduleItem.FallbackFiller)]; - foreach (MediaItem mediaItem in enumerator.Current) + while (enumerator.Current.IsSome && nextState.CurrentTime < nextItemStart) { + MediaItem mediaItem = enumerator.Current.ValueUnsafe(); + + TimeSpan itemDuration = mediaItem.GetDurationForPlayout(); + TimeSpan gap = nextItemStart - nextState.CurrentTime; + TimeSpan duration = itemDuration < gap ? itemDuration : gap; + TimeSpan inPoint = InPointForMediaItem(mediaItem); + var playoutItem = new PlayoutItem { PlayoutId = playoutBuilderState.PlayoutId, - MediaItemId = mediaItem.Id, + MediaItemId = IdForMediaItem(mediaItem), Start = nextState.CurrentTime.UtcDateTime, - Finish = nextItemStart.UtcDateTime, - InPoint = TimeSpan.Zero, - OutPoint = TimeSpan.Zero, + Finish = nextState.CurrentTime.Add(duration).UtcDateTime, + InPoint = inPoint, + OutPoint = inPoint + duration, GuideGroup = nextState.NextGuideGroup, FillerKind = FillerKind.Fallback, DisableWatermarks = !scheduleItem.FallbackFiller.AllowWatermarks, + ChapterTitle = ChapterTitleForMediaItem(mediaItem), SchedulingContext = GetSchedulingContext(scheduleItem, scheduleItem.FallbackFillerId, enumerator) }; @@ -230,7 +238,7 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode nextState = nextState with { - CurrentTime = nextItemStart.UtcDateTime + CurrentTime = nextState.CurrentTime.Add(duration) }; enumerator.MoveNext(playoutItem.StartOffset); @@ -704,14 +712,14 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode ? leftOverall : leftInThisBreak; - Option maybeFallback = FallbackFillerForPad( + List fallbackItems = FallbackFillerForPad( playoutBuilderState, enumerators, scheduleItem, i < filteredChapters.Count - 1 ? maxThisBreak : leftOverall, cancellationToken); - foreach (PlayoutItem fallback in maybeFallback) + foreach (PlayoutItem fallback in fallbackItems) { current += fallback.Finish - fallback.Start; filled += fallback.Finish - fallback.Start; @@ -864,41 +872,55 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode return result; } - private static Option FallbackFillerForPad( + private static List FallbackFillerForPad( PlayoutBuilderState playoutBuilderState, Dictionary enumerators, ProgramScheduleItem scheduleItem, TimeSpan duration, CancellationToken cancellationToken) { + var result = new List(); + if (scheduleItem.FallbackFiller != null) { IMediaCollectionEnumerator enumerator = enumerators[CollectionKey.ForFillerPreset(scheduleItem.FallbackFiller)]; - foreach (MediaItem mediaItem in enumerator.Current) + TimeSpan remainingToFill = duration; + DateTimeOffset currentTime = new DateTimeOffset(2020, 2, 1, 0, 0, 0, TimeSpan.Zero); + + while (enumerator.Current.IsSome && remainingToFill > TimeSpan.Zero) { - var result = new PlayoutItem + MediaItem mediaItem = enumerator.Current.ValueUnsafe(); + TimeSpan itemDuration = mediaItem.GetDurationForPlayout(); + TimeSpan currentDuration = itemDuration < remainingToFill ? itemDuration : remainingToFill; + TimeSpan inPoint = InPointForMediaItem(mediaItem); + + var playoutItem = new PlayoutItem { PlayoutId = playoutBuilderState.PlayoutId, - 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) + duration, - InPoint = TimeSpan.Zero, - OutPoint = TimeSpan.Zero, + MediaItemId = IdForMediaItem(mediaItem), + Start = currentTime.UtcDateTime, + Finish = currentTime.Add(currentDuration).UtcDateTime, + InPoint = inPoint, + OutPoint = inPoint + currentDuration, GuideGroup = playoutBuilderState.NextGuideGroup, FillerKind = FillerKind.Fallback, - DisableWatermarks = !scheduleItem.FallbackFiller.AllowWatermarks + DisableWatermarks = !scheduleItem.FallbackFiller.AllowWatermarks, + ChapterTitle = ChapterTitleForMediaItem(mediaItem) }; + result.Add(playoutItem); + + remainingToFill -= currentDuration; + currentTime += currentDuration; + // TODO: this won't work with reruns enumerator.MoveNext(Option.None); - - return result; } } - return None; + return result; } private List AddRandomCountFiller(