public override Tuple <PlayoutBuilderState, List <PlayoutItem> > Schedule(
     PlayoutBuilderState playoutBuilderState,
     Dictionary <CollectionKey, IMediaCollectionEnumerator> collectionEnumerators,
     ProgramScheduleItem scheduleItem,
     ProgramScheduleItem nextScheduleItem,
     DateTimeOffset hardStop) =>
 throw new NotSupportedException();
예제 #2
0
    public void Should_Have_Gap_With_No_Tail_No_Fallback()
    {
        Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));

        var scheduleItem = new ProgramScheduleItemOne
        {
            Id             = 1,
            Index          = 1,
            Collection     = collectionOne,
            CollectionId   = collectionOne.Id,
            StartTime      = null,
            PlaybackOrder  = PlaybackOrder.Chronological,
            TailFiller     = null,
            FallbackFiller = null
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            new List <ProgramScheduleItem> {
            scheduleItem
        },
            new CollectionEnumeratorState());

        var enumerator = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerOne(new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(scheduleItem, enumerator),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(1));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(2);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);

        enumerator.State.Index.Should().Be(1);

        playoutItems.Count.Should().Be(1);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);
    }
        public void Should_Not_Crash_Mid_Roll_One_Chapter()
        {
            Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));

            var scheduleItem = new ProgramScheduleItemOne
            {
                Id             = 1,
                Index          = 1,
                Collection     = collectionOne,
                CollectionId   = collectionOne.Id,
                StartTime      = null,
                PlaybackOrder  = PlaybackOrder.Chronological,
                TailFiller     = null,
                FallbackFiller = null,
                MidRollFiller  = new FillerPreset
                {
                    FillerKind         = FillerKind.MidRoll,
                    FillerMode         = FillerMode.Pad,
                    PadToNearestMinute = 15
                }
            };

            var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
                new List <ProgramScheduleItem> {
                scheduleItem
            },
                new CollectionEnumeratorState());

            var enumerator = new ChronologicalMediaCollectionEnumerator(
                collectionOne.MediaItems,
                new CollectionEnumeratorState());

            PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

            Dictionary <CollectionKey, IMediaCollectionEnumerator> enumerators = CollectionEnumerators(
                scheduleItem,
                enumerator);

            // too lazy to make another enumerator for the filler that we don't want
            enumerators.Add(CollectionKey.ForFillerPreset(scheduleItem.MidRollFiller), enumerator);

            List <PlayoutItem> playoutItems = Scheduler()
                                              .AddFiller(
                startState,
                enumerators,
                scheduleItem,
                new PlayoutItem(),
                new List <MediaChapter> {
                new()
            });
        public void Should_Compare_Time_As_Local_Time()
        {
            var enumerator = new Mock <IScheduleItemsEnumerator>();

            var state = new PlayoutBuilderState(
                enumerator.Object,
                None,
                None,
                false,
                false,
                0,
                DateTime.Today.AddHours(6).ToUniversalTime());

            var scheduleItem = new ProgramScheduleItemOne
            {
                StartTime = TimeSpan.FromHours(6)
            };

            DateTimeOffset result =
                PlayoutModeSchedulerBase <ProgramScheduleItem> .GetStartTimeAfter(state, scheduleItem);

            result.Should().Be(DateTime.Today.AddHours(6));
        }
        public void Should_Not_Crash_Mid_Roll_Zero_Chapters()
        {
            Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));

            var scheduleItem = new ProgramScheduleItemOne
            {
                MidRollFiller = new FillerPreset
                {
                    FillerKind         = FillerKind.MidRoll,
                    FillerMode         = FillerMode.Pad,
                    PadToNearestMinute = 15
                }
            };

            var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
                new List <ProgramScheduleItem> {
                scheduleItem
            },
                new CollectionEnumeratorState());

            var enumerator = new ChronologicalMediaCollectionEnumerator(
                collectionOne.MediaItems,
                new CollectionEnumeratorState());

            PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

            List <PlayoutItem> playoutItems = Scheduler()
                                              .AddFiller(
                startState,
                CollectionEnumerators(scheduleItem, enumerator),
                scheduleItem,
                new PlayoutItem(),
                new List <MediaChapter>());

            playoutItems.Count.Should().Be(1);
        }
예제 #6
0
    public void Should_Have_No_Gap_With_Exact_Post_Roll_Pad_With_Chapters()
    {
        Collection collectionOne   = TwoItemCollection(1, 2, new TimeSpan(2, 45, 0), 2);
        Collection collectionTwo   = TwoItemCollection(3, 4, TimeSpan.FromMinutes(5));
        Collection collectionThree = TwoItemCollection(5, 6, TimeSpan.FromMinutes(1));

        var scheduleItem = new ProgramScheduleItemOne
        {
            Id             = 1,
            Index          = 1,
            Collection     = collectionOne,
            CollectionId   = collectionOne.Id,
            StartTime      = null,
            PlaybackOrder  = PlaybackOrder.Chronological,
            PostRollFiller = new FillerPreset
            {
                FillerKind         = FillerKind.PostRoll,
                FillerMode         = FillerMode.Pad,
                PadToNearestMinute = 30,
                Collection         = collectionTwo,
                CollectionId       = collectionTwo.Id
            },
            FallbackFiller = new FillerPreset
            {
                FillerKind   = FillerKind.Fallback,
                Collection   = collectionThree,
                CollectionId = collectionThree.Id
            }
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            new List <ProgramScheduleItem> {
            scheduleItem
        },
            new CollectionEnumeratorState());

        var enumerator1 = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        var enumerator2 = new ChronologicalMediaCollectionEnumerator(
            collectionTwo.MediaItems,
            new CollectionEnumeratorState());

        var enumerator3 = new ChronologicalMediaCollectionEnumerator(
            collectionThree.MediaItems,
            new CollectionEnumeratorState());

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerOne(new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(
                scheduleItem,
                enumerator1,
                scheduleItem.PostRollFiller,
                enumerator2,
                scheduleItem.FallbackFiller,
                enumerator3),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(2);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);

        enumerator1.State.Index.Should().Be(1);
        enumerator2.State.Index.Should().Be(1);
        enumerator3.State.Index.Should().Be(0);

        playoutItems.Count.Should().Be(4);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);

        playoutItems[1].MediaItemId.Should().Be(3);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 45, 0)));
        playoutItems[1].GuideGroup.Should().Be(1);
        playoutItems[1].FillerKind.Should().Be(FillerKind.PostRoll);

        playoutItems[2].MediaItemId.Should().Be(4);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 50, 0)));
        playoutItems[2].GuideGroup.Should().Be(1);
        playoutItems[2].FillerKind.Should().Be(FillerKind.PostRoll);

        playoutItems[3].MediaItemId.Should().Be(3);
        playoutItems[3].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 55, 0)));
        playoutItems[3].GuideGroup.Should().Be(1);
        playoutItems[3].FillerKind.Should().Be(FillerKind.PostRoll);
    }
예제 #7
0
    public override Tuple <PlayoutBuilderState, List <PlayoutItem> > Schedule(
        PlayoutBuilderState playoutBuilderState,
        Dictionary <CollectionKey, IMediaCollectionEnumerator> collectionEnumerators,
        ProgramScheduleItemOne scheduleItem,
        ProgramScheduleItem nextScheduleItem,
        DateTimeOffset hardStop)
    {
        IMediaCollectionEnumerator contentEnumerator =
            collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)];

        foreach (MediaItem mediaItem in contentEnumerator.Current)
        {
            // find when we should start this item, based on the current time
            DateTimeOffset itemStartTime = GetStartTimeAfter(
                playoutBuilderState,
                scheduleItem);

            TimeSpan            itemDuration = DurationForMediaItem(mediaItem);
            List <MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem);

            var playoutItem = new PlayoutItem
            {
                MediaItemId = mediaItem.Id,
                Start       = itemStartTime.UtcDateTime,
                Finish      = itemStartTime.UtcDateTime + itemDuration,
                InPoint     = TimeSpan.Zero,
                OutPoint    = itemDuration,
                GuideGroup  = playoutBuilderState.NextGuideGroup,
                FillerKind  = scheduleItem.GuideMode == GuideMode.Filler
                    ? FillerKind.Tail
                    : FillerKind.None,
                WatermarkId = scheduleItem.WatermarkId,
                PreferredAudioLanguageCode    = scheduleItem.PreferredAudioLanguageCode,
                PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
                SubtitleMode = scheduleItem.SubtitleMode
            };

            DateTimeOffset itemEndTimeWithFiller = CalculateEndTimeWithFiller(
                collectionEnumerators,
                scheduleItem,
                itemStartTime,
                itemDuration,
                itemChapters);

            List <PlayoutItem> playoutItems = AddFiller(
                playoutBuilderState,
                collectionEnumerators,
                scheduleItem,
                playoutItem,
                itemChapters);

            PlayoutBuilderState nextState = playoutBuilderState with
            {
                CurrentTime = itemEndTimeWithFiller
            };

            nextState.ScheduleItemsEnumerator.MoveNext();
            contentEnumerator.MoveNext();

            // LogScheduledItem(scheduleItem, mediaItem, itemStartTime);

            // only play one item from collection, so always advance to the next item
            // _logger.LogDebug(
            //     "Advancing to next schedule item after playout mode {PlayoutMode}",
            //     "One");

            DateTimeOffset nextItemStart = GetStartTimeAfter(nextState, nextScheduleItem);

            if (scheduleItem.TailFiller != null)
            {
                (nextState, playoutItems) = AddTailFiller(
                    nextState,
                    collectionEnumerators,
                    scheduleItem,
                    playoutItems,
                    nextItemStart);
            }

            if (scheduleItem.FallbackFiller != null)
            {
                (nextState, playoutItems) = AddFallbackFiller(
                    nextState,
                    collectionEnumerators,
                    scheduleItem,
                    playoutItems,
                    nextItemStart);
            }

            nextState = nextState with {
                NextGuideGroup = nextState.IncrementGuideGroup
            };

            return(Tuple(nextState, playoutItems));
        }

        return(Tuple(playoutBuilderState, new List <PlayoutItem>()));
    }
}
    public override Tuple <PlayoutBuilderState, List <PlayoutItem> > Schedule(
        PlayoutBuilderState playoutBuilderState,
        Dictionary <CollectionKey, IMediaCollectionEnumerator> collectionEnumerators,
        ProgramScheduleItemFlood scheduleItem,
        ProgramScheduleItem nextScheduleItem,
        DateTimeOffset hardStop)
    {
        var playoutItems = new List <PlayoutItem>();

        PlayoutBuilderState nextState = playoutBuilderState;
        var willFinishInTime          = true;

        IMediaCollectionEnumerator contentEnumerator =
            collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)];

        ProgramScheduleItem peekScheduleItem = nextScheduleItem;

        while (contentEnumerator.Current.IsSome && nextState.CurrentTime < hardStop && willFinishInTime)
        {
            MediaItem mediaItem = contentEnumerator.Current.ValueUnsafe();

            // find when we should start this item, based on the current time
            DateTimeOffset      itemStartTime = GetStartTimeAfter(nextState, scheduleItem);
            TimeSpan            itemDuration  = DurationForMediaItem(mediaItem);
            List <MediaChapter> itemChapters  = ChaptersForMediaItem(mediaItem);

            var playoutItem = new PlayoutItem
            {
                MediaItemId = mediaItem.Id,
                Start       = itemStartTime.UtcDateTime,
                Finish      = itemStartTime.UtcDateTime + itemDuration,
                InPoint     = TimeSpan.Zero,
                OutPoint    = itemDuration,
                GuideGroup  = nextState.NextGuideGroup,
                FillerKind  = scheduleItem.GuideMode == GuideMode.Filler
                    ? FillerKind.Tail
                    : FillerKind.None,
                WatermarkId = scheduleItem.WatermarkId,
                PreferredAudioLanguageCode    = scheduleItem.PreferredAudioLanguageCode,
                PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
                SubtitleMode = scheduleItem.SubtitleMode
            };

            DateTimeOffset peekScheduleItemStart =
                peekScheduleItem.StartType == StartType.Fixed
                    ? GetStartTimeAfter(nextState with {
                InFlood = false
            }, peekScheduleItem)
                    : DateTimeOffset.MaxValue;

            DateTimeOffset itemEndTimeWithFiller = CalculateEndTimeWithFiller(
                collectionEnumerators,
                scheduleItem,
                itemStartTime,
                itemDuration,
                itemChapters);

            // if the next schedule item is supposed to start during this item,
            // don't schedule this item and just move on
            willFinishInTime = peekScheduleItemStart < itemStartTime ||
                               peekScheduleItemStart >= itemEndTimeWithFiller;

            if (willFinishInTime)
            {
                playoutItems.AddRange(
                    AddFiller(nextState, collectionEnumerators, scheduleItem, playoutItem, itemChapters));
                // LogScheduledItem(scheduleItem, mediaItem, itemStartTime);

                DateTimeOffset actualEndTime = playoutItems.Max(p => p.FinishOffset);
                if (Math.Abs((itemEndTimeWithFiller - actualEndTime).TotalSeconds) > 1)
                {
                    _logger.LogWarning(
                        "Filler prediction failure: predicted {PredictedDuration} doesn't match actual {ActualDuration}",
                        itemEndTimeWithFiller,
                        actualEndTime);

                    // _logger.LogWarning("Playout items: {@PlayoutItems}", playoutItems);
                }

                nextState = nextState with
                {
                    CurrentTime    = itemEndTimeWithFiller,
                    InFlood        = true,
                    NextGuideGroup = nextState.IncrementGuideGroup
                };

                contentEnumerator.MoveNext();
            }
        }

        // _logger.LogDebug(
        //     "Advancing to next schedule item after playout mode {PlayoutMode}",
        //     "Flood");

        nextState = nextState with
        {
            InFlood        = nextState.CurrentTime >= hardStop,
            NextGuideGroup = nextState.DecrementGuideGroup
        };

        nextState.ScheduleItemsEnumerator.MoveNext();

        ProgramScheduleItem peekItem      = nextScheduleItem;
        DateTimeOffset      peekItemStart = GetStartTimeAfter(nextState, peekItem);

        if (scheduleItem.TailFiller != null)
        {
            (nextState, playoutItems) = AddTailFiller(
                nextState,
                collectionEnumerators,
                scheduleItem,
                playoutItems,
                peekItemStart);
        }

        if (scheduleItem.FallbackFiller != null)
        {
            (nextState, playoutItems) = AddFallbackFiller(
                nextState,
                collectionEnumerators,
                scheduleItem,
                playoutItems,
                peekItemStart);
        }

        nextState = nextState with {
            NextGuideGroup = nextState.IncrementGuideGroup
        };

        return(Tuple(nextState, playoutItems));
    }
}
예제 #9
0
    public override Tuple <PlayoutBuilderState, List <PlayoutItem> > Schedule(
        PlayoutBuilderState playoutBuilderState,
        Dictionary <CollectionKey, IMediaCollectionEnumerator> collectionEnumerators,
        ProgramScheduleItemDuration scheduleItem,
        ProgramScheduleItem nextScheduleItem,
        DateTimeOffset hardStop)
    {
        var playoutItems = new List <PlayoutItem>();

        PlayoutBuilderState nextState = playoutBuilderState;

        var willFinishInTime = true;
        Option <DateTimeOffset> durationUntil = None;

        IMediaCollectionEnumerator contentEnumerator =
            collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)];

        while (contentEnumerator.Current.IsSome && nextState.CurrentTime < hardStop && willFinishInTime)
        {
            MediaItem mediaItem = contentEnumerator.Current.ValueUnsafe();

            // find when we should start this item, based on the current time
            DateTimeOffset itemStartTime = GetStartTimeAfter(nextState, scheduleItem);

            // remember when we need to finish this duration item
            if (nextState.DurationFinish.IsNone)
            {
                nextState = nextState with
                {
                    DurationFinish = itemStartTime + scheduleItem.PlayoutDuration
                };

                durationUntil = nextState.DurationFinish;
            }

            TimeSpan            itemDuration = DurationForMediaItem(mediaItem);
            List <MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem);

            if (itemDuration > scheduleItem.PlayoutDuration)
            {
                _logger.LogWarning(
                    "Skipping playout item {Title} with duration {Duration} that is longer than schedule item duration {PlayoutDuration}",
                    PlayoutBuilder.DisplayTitle(mediaItem),
                    itemDuration,
                    scheduleItem.PlayoutDuration);

                contentEnumerator.MoveNext();
                continue;
            }

            var playoutItem = new PlayoutItem
            {
                MediaItemId = mediaItem.Id,
                Start       = itemStartTime.UtcDateTime,
                Finish      = itemStartTime.UtcDateTime + itemDuration,
                InPoint     = TimeSpan.Zero,
                OutPoint    = itemDuration,
                GuideGroup  = nextState.NextGuideGroup,
                FillerKind  = scheduleItem.GuideMode == GuideMode.Filler
                    ? FillerKind.Tail
                    : FillerKind.None,
                CustomTitle = scheduleItem.CustomTitle,
                WatermarkId = scheduleItem.WatermarkId,
                PreferredAudioLanguageCode    = scheduleItem.PreferredAudioLanguageCode,
                PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
                SubtitleMode = scheduleItem.SubtitleMode
            };

            durationUntil.Do(du => playoutItem.GuideFinish = du.UtcDateTime);

            DateTimeOffset durationFinish        = nextState.DurationFinish.IfNone(SystemTime.MaxValueUtc);
            DateTimeOffset itemEndTimeWithFiller = CalculateEndTimeWithFiller(
                collectionEnumerators,
                scheduleItem,
                itemStartTime,
                itemDuration,
                itemChapters);
            willFinishInTime = itemStartTime > durationFinish ||
                               itemEndTimeWithFiller <= durationFinish;
            if (willFinishInTime)
            {
                // LogScheduledItem(scheduleItem, mediaItem, itemStartTime);
                playoutItems.AddRange(
                    AddFiller(nextState, collectionEnumerators, scheduleItem, playoutItem, itemChapters));

                nextState = nextState with
                {
                    CurrentTime = itemEndTimeWithFiller,

                    // only bump guide group if we don't have a custom title
                    NextGuideGroup = string.IsNullOrWhiteSpace(scheduleItem.CustomTitle)
                        ? nextState.IncrementGuideGroup
                        : nextState.NextGuideGroup
                };

                contentEnumerator.MoveNext();
            }
            else
            {
                TimeSpan durationBlock = itemEndTimeWithFiller - itemStartTime;
                if (itemEndTimeWithFiller - itemStartTime > scheduleItem.PlayoutDuration)
                {
                    _logger.LogWarning(
                        "Unable to schedule duration block of {DurationBlock} which is longer than the configured playout duration {PlayoutDuration}",
                        durationBlock,
                        scheduleItem.PlayoutDuration);
                }

                nextState = nextState with
                {
                    DurationFinish = None
                };

                nextState.ScheduleItemsEnumerator.MoveNext();
            }
        }

        // this is needed when the duration finish exactly matches the hard stop
        if (nextState.DurationFinish.IsSome && nextState.CurrentTime == nextState.DurationFinish)
        {
            nextState = nextState with
            {
                DurationFinish = None
            };

            nextState.ScheduleItemsEnumerator.MoveNext();
        }

        if (playoutItems.Select(pi => pi.GuideGroup).Distinct().Count() != 1)
        {
            nextState = nextState with {
                NextGuideGroup = nextState.DecrementGuideGroup
            };
        }

        foreach (DateTimeOffset nextItemStart in durationUntil)
        {
            switch (scheduleItem.TailMode)
            {
            case TailMode.Filler:
                if (scheduleItem.TailFiller != null)
                {
                    (nextState, playoutItems) = AddTailFiller(
                        nextState,
                        collectionEnumerators,
                        scheduleItem,
                        playoutItems,
                        nextItemStart);
                }

                if (scheduleItem.FallbackFiller != null)
                {
                    (nextState, playoutItems) = AddFallbackFiller(
                        nextState,
                        collectionEnumerators,
                        scheduleItem,
                        playoutItems,
                        nextItemStart);
                }

                nextState = nextState with {
                    CurrentTime = nextItemStart
                };
                break;

            case TailMode.Offline:
                if (scheduleItem.FallbackFiller != null)
                {
                    (nextState, playoutItems) = AddFallbackFiller(
                        nextState,
                        collectionEnumerators,
                        scheduleItem,
                        playoutItems,
                        nextItemStart);
                }

                nextState = nextState with {
                    CurrentTime = nextItemStart
                };
                break;
            }
        }

        // clear guide finish on all but the last item
        var         all  = playoutItems.Filter(pi => pi.FillerKind == FillerKind.None).ToList();
        PlayoutItem last = all.OrderBy(pi => pi.FinishOffset).LastOrDefault();

        foreach (PlayoutItem item in all.Filter(pi => pi != last))
        {
            item.GuideFinish = null;
        }

        nextState = nextState with {
            NextGuideGroup = nextState.IncrementGuideGroup
        };

        return(Tuple(nextState, playoutItems));
    }
}
예제 #10
0
    public void Should_Not_Have_Gap_Duration_Tail_Mode_Filler_With_Fallback()
    {
        Collection collectionOne   = TwoItemCollection(1, 2, TimeSpan.FromMinutes(55));
        Collection collectionTwo   = TwoItemCollection(3, 4, TimeSpan.FromMinutes(4));
        Collection collectionThree = TwoItemCollection(5, 6, TimeSpan.FromMinutes(1));

        var scheduleItem = new ProgramScheduleItemDuration
        {
            Id              = 1,
            Index           = 1,
            Collection      = collectionOne,
            CollectionId    = collectionOne.Id,
            StartTime       = null,
            PlayoutDuration = TimeSpan.FromHours(3),
            TailMode        = TailMode.Filler,
            PlaybackOrder   = PlaybackOrder.Chronological,
            TailFiller      = new FillerPreset
            {
                FillerKind   = FillerKind.Tail,
                Collection   = collectionTwo,
                CollectionId = collectionTwo.Id
            },
            FallbackFiller = new FillerPreset
            {
                FillerKind   = FillerKind.Fallback,
                Collection   = collectionThree,
                CollectionId = collectionThree.Id
            }
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            new List <ProgramScheduleItem> {
            scheduleItem
        },
            new CollectionEnumeratorState());

        var enumerator1 = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        var enumerator2 = new ChronologicalMediaCollectionEnumerator(
            collectionTwo.MediaItems,
            new CollectionEnumeratorState());

        var enumerator3 = new ChronologicalMediaCollectionEnumerator(
            collectionThree.MediaItems,
            new CollectionEnumeratorState());

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerDuration(new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(
                scheduleItem,
                enumerator1,
                scheduleItem.TailFiller,
                enumerator2,
                scheduleItem.FallbackFiller,
                enumerator3),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(4);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);

        enumerator1.State.Index.Should().Be(1);
        enumerator2.State.Index.Should().Be(1);
        enumerator3.State.Index.Should().Be(1);

        playoutItems.Count.Should().Be(7);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);
        playoutItems[0].GuideFinish.HasValue.Should().BeFalse();

        playoutItems[1].MediaItemId.Should().Be(2);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddMinutes(55));
        playoutItems[1].GuideGroup.Should().Be(2);
        playoutItems[1].FillerKind.Should().Be(FillerKind.None);
        playoutItems[1].GuideFinish.HasValue.Should().BeFalse();

        playoutItems[2].MediaItemId.Should().Be(1);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(1, 50, 0)));
        playoutItems[2].GuideGroup.Should().Be(3);
        playoutItems[2].FillerKind.Should().Be(FillerKind.None);
        playoutItems[2].GuideFinish.HasValue.Should().BeTrue();

        playoutItems[3].MediaItemId.Should().Be(3);
        playoutItems[3].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 45, 0)));
        playoutItems[3].GuideGroup.Should().Be(3);
        playoutItems[3].FillerKind.Should().Be(FillerKind.Tail);
        playoutItems[3].GuideFinish.HasValue.Should().BeFalse();

        playoutItems[4].MediaItemId.Should().Be(4);
        playoutItems[4].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 49, 0)));
        playoutItems[4].GuideGroup.Should().Be(3);
        playoutItems[4].FillerKind.Should().Be(FillerKind.Tail);
        playoutItems[4].GuideFinish.HasValue.Should().BeFalse();

        playoutItems[5].MediaItemId.Should().Be(3);
        playoutItems[5].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 53, 0)));
        playoutItems[5].GuideGroup.Should().Be(3);
        playoutItems[5].FillerKind.Should().Be(FillerKind.Tail);
        playoutItems[5].GuideFinish.HasValue.Should().BeFalse();

        playoutItems[6].MediaItemId.Should().Be(5);
        playoutItems[6].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 57, 0)));
        playoutItems[6].GuideGroup.Should().Be(3);
        playoutItems[6].FillerKind.Should().Be(FillerKind.Fallback);
        playoutItems[6].GuideFinish.HasValue.Should().BeFalse();
    }
예제 #11
0
    public void Should_Fill_Exact_Duration_CustomTitle()
    {
        Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));

        var scheduleItem = new ProgramScheduleItemDuration
        {
            Id              = 1,
            Index           = 1,
            Collection      = collectionOne,
            CollectionId    = collectionOne.Id,
            StartTime       = null,
            PlayoutDuration = TimeSpan.FromHours(3),
            TailMode        = TailMode.None,
            PlaybackOrder   = PlaybackOrder.Chronological,
            CustomTitle     = "Custom Title"
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            new List <ProgramScheduleItem> {
            scheduleItem
        },
            new CollectionEnumeratorState());

        var enumerator = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerDuration(new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(scheduleItem, enumerator),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(2);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);

        enumerator.State.Index.Should().Be(1);

        playoutItems.Count.Should().Be(3);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);
        playoutItems[0].GuideFinish.HasValue.Should().BeFalse();
        playoutItems[0].CustomTitle.Should().Be("Custom Title");

        playoutItems[1].MediaItemId.Should().Be(2);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddHours(1));
        playoutItems[1].GuideGroup.Should().Be(1);
        playoutItems[1].FillerKind.Should().Be(FillerKind.None);
        playoutItems[1].GuideFinish.HasValue.Should().BeFalse();
        playoutItems[1].CustomTitle.Should().Be("Custom Title");

        playoutItems[2].MediaItemId.Should().Be(1);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.AddHours(2));
        playoutItems[2].GuideGroup.Should().Be(1);
        playoutItems[2].FillerKind.Should().Be(FillerKind.None);
        playoutItems[2].GuideFinish.HasValue.Should().BeTrue();
        playoutItems[2].CustomTitle.Should().Be("Custom Title");
    }
예제 #12
0
    public void Should_Have_Gap_Duration_Tail_Mode_Offline_No_Fallback()
    {
        Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromMinutes(55));

        var scheduleItem = new ProgramScheduleItemDuration
        {
            Id              = 1,
            Index           = 1,
            Collection      = collectionOne,
            CollectionId    = collectionOne.Id,
            StartTime       = null,
            PlayoutDuration = TimeSpan.FromHours(3),
            TailMode        = TailMode.Offline,
            PlaybackOrder   = PlaybackOrder.Chronological
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            new List <ProgramScheduleItem> {
            scheduleItem
        },
            new CollectionEnumeratorState());

        var enumerator = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerDuration(new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(scheduleItem, enumerator),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        // duration block should end after exact duration, with gap
        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 45, 0)));

        playoutBuilderState.NextGuideGroup.Should().Be(4);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);

        enumerator.State.Index.Should().Be(1);

        playoutItems.Count.Should().Be(3);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);
        playoutItems[0].GuideFinish.HasValue.Should().BeFalse();

        playoutItems[1].MediaItemId.Should().Be(2);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddMinutes(55));
        playoutItems[1].GuideGroup.Should().Be(2);
        playoutItems[1].FillerKind.Should().Be(FillerKind.None);
        playoutItems[1].GuideFinish.HasValue.Should().BeFalse();

        playoutItems[2].MediaItemId.Should().Be(1);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(1, 50, 0)));
        playoutItems[2].GuideGroup.Should().Be(3);
        playoutItems[2].FillerKind.Should().Be(FillerKind.None);
        playoutItems[2].GuideFinish.HasValue.Should().BeTrue();
    }
    public void Should_Not_Have_Gap_With_Unused_Tail_And_Unused_Fallback()
    {
        Collection collectionOne   = TwoItemCollection(1, 2, TimeSpan.FromHours(1));
        Collection collectionTwo   = TwoItemCollection(3, 4, TimeSpan.FromMinutes(4));
        Collection collectionThree = TwoItemCollection(5, 6, TimeSpan.FromMinutes(1));

        var scheduleItem = new ProgramScheduleItemMultiple
        {
            Id            = 1,
            Index         = 1,
            Collection    = collectionOne,
            CollectionId  = collectionOne.Id,
            StartTime     = null,
            PlaybackOrder = PlaybackOrder.Chronological,
            TailFiller    = new FillerPreset
            {
                FillerKind   = FillerKind.Tail,
                Collection   = collectionTwo,
                CollectionId = collectionTwo.Id
            },
            FallbackFiller = new FillerPreset
            {
                FillerKind   = FillerKind.Fallback,
                Collection   = collectionThree,
                CollectionId = collectionThree.Id
            },
            Count = 3
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            new List <ProgramScheduleItem> {
            scheduleItem
        },
            new CollectionEnumeratorState());

        var enumerator1 = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        var enumerator2 = new ChronologicalMediaCollectionEnumerator(
            collectionTwo.MediaItems,
            new CollectionEnumeratorState());

        var enumerator3 = new ChronologicalMediaCollectionEnumerator(
            collectionThree.MediaItems,
            new CollectionEnumeratorState());

        var collectionMediaItems = new Dictionary <CollectionKey, List <MediaItem> >
        {
            { CollectionKey.ForScheduleItem(scheduleItem), collectionOne.MediaItems },
            { CollectionKey.ForFillerPreset(scheduleItem.TailFiller), collectionTwo.MediaItems },
            { CollectionKey.ForFillerPreset(scheduleItem.FallbackFiller), collectionThree.MediaItems }
        }.ToMap();

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerMultiple(collectionMediaItems, new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(
                scheduleItem,
                enumerator1,
                scheduleItem.TailFiller,
                enumerator2,
                scheduleItem.FallbackFiller,
                enumerator3),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(4);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);

        enumerator1.State.Index.Should().Be(1);
        enumerator2.State.Index.Should().Be(0);
        enumerator3.State.Index.Should().Be(0);

        playoutItems.Count.Should().Be(3);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);

        playoutItems[1].MediaItemId.Should().Be(2);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddHours(1));
        playoutItems[1].GuideGroup.Should().Be(2);
        playoutItems[1].FillerKind.Should().Be(FillerKind.None);

        playoutItems[2].MediaItemId.Should().Be(1);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.AddHours(2));
        playoutItems[2].GuideGroup.Should().Be(3);
        playoutItems[2].FillerKind.Should().Be(FillerKind.None);
    }
    public void Should_Fill_Exactly_To_Next_Schedule_Item()
    {
        Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));

        var scheduleItem = new ProgramScheduleItemMultiple
        {
            Id             = 1,
            Index          = 1,
            CollectionType = ProgramScheduleItemCollectionType.Collection,
            Collection     = collectionOne,
            CollectionId   = collectionOne.Id,
            StartTime      = null,
            PlaybackOrder  = PlaybackOrder.Chronological,
            TailFiller     = null,
            FallbackFiller = null,
            Count          = 3
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            new List <ProgramScheduleItem> {
            scheduleItem
        },
            new CollectionEnumeratorState());

        var enumerator = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        var collectionMediaItems = new Dictionary <CollectionKey, List <MediaItem> >
        {
            { CollectionKey.ForScheduleItem(scheduleItem), collectionOne.MediaItems }
        }.ToMap();

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerMultiple(collectionMediaItems, new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(scheduleItem, enumerator),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(4);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);

        enumerator.State.Index.Should().Be(1);

        playoutItems.Count.Should().Be(3);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);

        playoutItems[1].MediaItemId.Should().Be(2);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddHours(1));
        playoutItems[1].GuideGroup.Should().Be(2);
        playoutItems[1].FillerKind.Should().Be(FillerKind.None);

        playoutItems[2].MediaItemId.Should().Be(1);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.AddHours(2));
        playoutItems[2].GuideGroup.Should().Be(3);
        playoutItems[2].FillerKind.Should().Be(FillerKind.None);
    }
    public void Should_Fill_Exactly_To_Next_Schedule_Item_Flood()
    {
        Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));

        var scheduleItem = new ProgramScheduleItemFlood
        {
            Id             = 1,
            Index          = 1,
            Collection     = collectionOne,
            CollectionId   = collectionOne.Id,
            StartTime      = null,
            PlaybackOrder  = PlaybackOrder.Chronological,
            TailFiller     = null,
            FallbackFiller = null
        };

        var enumerator = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        var sortedScheduleItems = new List <ProgramScheduleItem>
        {
            scheduleItem,
            // this caused trouble with the peek logic and the IsFlood flag
            new ProgramScheduleItemFlood
            {
                StartTime = TimeSpan.FromHours(3)
            }
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            sortedScheduleItems,
            new CollectionEnumeratorState());

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerFlood(new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(scheduleItem, enumerator),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(4);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(1);

        enumerator.State.Index.Should().Be(1);

        playoutItems.Count.Should().Be(3);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);

        playoutItems[1].MediaItemId.Should().Be(2);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddHours(1));
        playoutItems[1].GuideGroup.Should().Be(2);
        playoutItems[1].FillerKind.Should().Be(FillerKind.None);

        playoutItems[2].MediaItemId.Should().Be(1);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.AddHours(2));
        playoutItems[2].GuideGroup.Should().Be(3);
        playoutItems[2].FillerKind.Should().Be(FillerKind.None);
    }
    public void Should_Fill_Exactly_To_Next_Schedule_Item_With_Post_Roll_Multiple_One()
    {
        Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromMinutes(55));
        Collection collectionTwo = TwoItemCollection(3, 4, TimeSpan.FromMinutes(5));

        var scheduleItem = new ProgramScheduleItemFlood
        {
            Id             = 1,
            Index          = 1,
            Collection     = collectionOne,
            CollectionId   = collectionOne.Id,
            StartTime      = null,
            PlaybackOrder  = PlaybackOrder.Chronological,
            PostRollFiller = new FillerPreset
            {
                FillerKind   = FillerKind.PostRoll,
                FillerMode   = FillerMode.Count,
                Count        = 1,
                Collection   = collectionTwo,
                CollectionId = collectionTwo.Id
            },
            TailFiller     = null,
            FallbackFiller = null
        };

        var enumerator1 = new ChronologicalMediaCollectionEnumerator(
            collectionOne.MediaItems,
            new CollectionEnumeratorState());

        var enumerator2 = new ChronologicalMediaCollectionEnumerator(
            collectionTwo.MediaItems,
            new CollectionEnumeratorState());

        var sortedScheduleItems = new List <ProgramScheduleItem>
        {
            scheduleItem,
            NextScheduleItem
        };

        var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
            sortedScheduleItems,
            new CollectionEnumeratorState());

        PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);

        var scheduler = new PlayoutModeSchedulerFlood(new Mock <ILogger>().Object);

        (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule(
            startState,
            CollectionEnumerators(scheduleItem, enumerator1, scheduleItem.PostRollFiller, enumerator2),
            scheduleItem,
            NextScheduleItem,
            HardStop(scheduleItemsEnumerator));

        playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3));
        playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);

        playoutBuilderState.NextGuideGroup.Should().Be(4);
        playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
        playoutBuilderState.InFlood.Should().BeFalse();
        playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
        playoutBuilderState.InDurationFiller.Should().BeFalse();
        playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(1);

        enumerator1.State.Index.Should().Be(1);
        enumerator2.State.Index.Should().Be(1);

        playoutItems.Count.Should().Be(6);

        playoutItems[0].MediaItemId.Should().Be(1);
        playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
        playoutItems[0].GuideGroup.Should().Be(1);
        playoutItems[0].FillerKind.Should().Be(FillerKind.None);

        playoutItems[1].MediaItemId.Should().Be(3);
        playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddMinutes(55));
        playoutItems[1].GuideGroup.Should().Be(1);
        playoutItems[1].FillerKind.Should().Be(FillerKind.PostRoll);

        playoutItems[2].MediaItemId.Should().Be(2);
        playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.AddHours(1));
        playoutItems[2].GuideGroup.Should().Be(2);
        playoutItems[2].FillerKind.Should().Be(FillerKind.None);

        playoutItems[3].MediaItemId.Should().Be(4);
        playoutItems[3].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(1, 55, 0)));
        playoutItems[3].GuideGroup.Should().Be(2);
        playoutItems[3].FillerKind.Should().Be(FillerKind.PostRoll);

        playoutItems[4].MediaItemId.Should().Be(1);
        playoutItems[4].StartOffset.Should().Be(startState.CurrentTime.AddHours(2));
        playoutItems[4].GuideGroup.Should().Be(3);
        playoutItems[4].FillerKind.Should().Be(FillerKind.None);

        playoutItems[5].MediaItemId.Should().Be(3);
        playoutItems[5].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 55, 0)));
        playoutItems[5].GuideGroup.Should().Be(3);
        playoutItems[5].FillerKind.Should().Be(FillerKind.PostRoll);
    }