fix mid-roll filler scheduling bug (#675)

This commit is contained in:
Jason Dove
2022-03-04 21:40:08 -06:00
committed by GitHub
parent a3260b2316
commit 6c06fbe621
3 changed files with 262 additions and 163 deletions
+2
View File
@@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix playback of interlaced mpeg2video content with NVIDIA acceleration - Fix playback of interlaced mpeg2video content with NVIDIA acceleration
- Fix playback of all interlaced content with QSV acceleration - Fix playback of all interlaced content with QSV acceleration
- Fix adding songs to collections from search results page - Fix adding songs to collections from search results page
- Fix bug scheduling mid-roll filler with content that contains one chapter
- No mid-roll filler will be inserted for content with zero or one chapters
### Added ### Added
- Add automated error reporting via Bugsnag - Add automated error reporting via Bugsnag
@@ -3,15 +3,20 @@ using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.Scheduling; using ErsatzTV.Core.Interfaces.Scheduling;
using ErsatzTV.Core.Scheduling; using ErsatzTV.Core.Scheduling;
using FluentAssertions; using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework; using NUnit.Framework;
namespace ErsatzTV.Core.Tests.Scheduling; namespace ErsatzTV.Core.Tests.Scheduling;
[TestFixture] [TestFixture]
public class PlayoutModeSchedulerBaseTests public class PlayoutModeSchedulerBaseTests : SchedulerTestBase
{ {
[TestFixture]
public class CalculateEndTimeWithFiller
{
[Test] [Test]
public void CalculateEndTimeWithFiller_Should_Not_Touch_Enumerator() public void Should_Not_Touch_Enumerator()
{ {
var collection = new Collection var collection = new Collection
{ {
@@ -58,7 +63,7 @@ public class PlayoutModeSchedulerBaseTests
} }
[Test] [Test]
public void CalculateEndTimeWithFiller_Should_Pad_To_15_Minutes_15() public void Should_Pad_To_15_Minutes_15()
{ {
DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem> DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem>
.CalculateEndTimeWithFiller( .CalculateEndTimeWithFiller(
@@ -80,7 +85,7 @@ public class PlayoutModeSchedulerBaseTests
} }
[Test] [Test]
public void CalculateEndTimeWithFiller_Should_Pad_To_15_Minutes_30() public void Should_Pad_To_15_Minutes_30()
{ {
DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem> DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem>
.CalculateEndTimeWithFiller( .CalculateEndTimeWithFiller(
@@ -102,7 +107,7 @@ public class PlayoutModeSchedulerBaseTests
} }
[Test] [Test]
public void CalculateEndTimeWithFiller_Should_Pad_To_15_Minutes_45() public void Should_Pad_To_15_Minutes_45()
{ {
DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem> DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem>
.CalculateEndTimeWithFiller( .CalculateEndTimeWithFiller(
@@ -124,7 +129,7 @@ public class PlayoutModeSchedulerBaseTests
} }
[Test] [Test]
public void CalculateEndTimeWithFiller_Should_Pad_To_15_Minutes_00() public void Should_Pad_To_15_Minutes_00()
{ {
DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem> DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem>
.CalculateEndTimeWithFiller( .CalculateEndTimeWithFiller(
@@ -146,7 +151,7 @@ public class PlayoutModeSchedulerBaseTests
} }
[Test] [Test]
public void CalculateEndTimeWithFiller_Should_Pad_To_30_Minutes_30() public void Should_Pad_To_30_Minutes_30()
{ {
DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem> DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem>
.CalculateEndTimeWithFiller( .CalculateEndTimeWithFiller(
@@ -168,7 +173,7 @@ public class PlayoutModeSchedulerBaseTests
} }
[Test] [Test]
public void CalculateEndTimeWithFiller_Should_Pad_To_30_Minutes_00() public void Should_Pad_To_30_Minutes_00()
{ {
DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem> DateTimeOffset result = PlayoutModeSchedulerBase<ProgramScheduleItem>
.CalculateEndTimeWithFiller( .CalculateEndTimeWithFiller(
@@ -188,6 +193,98 @@ public class PlayoutModeSchedulerBaseTests
result.Should().Be(new DateTimeOffset(2020, 2, 1, 13, 0, 0, TimeSpan.FromHours(-5))); result.Should().Be(new DateTimeOffset(2020, 2, 1, 13, 0, 0, TimeSpan.FromHours(-5)));
} }
}
[TestFixture]
public class AddFiller
{
[Test]
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 = PlayoutModeSchedulerBase<ProgramScheduleItem>
.AddFiller(
startState,
CollectionEnumerators(scheduleItem, enumerator),
scheduleItem,
new PlayoutItem(),
new List<MediaChapter>());
playoutItems.Count.Should().Be(1);
}
[Test]
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 = PlayoutModeSchedulerBase<ProgramScheduleItem>
.AddFiller(
startState,
enumerators,
scheduleItem,
new PlayoutItem(),
new List<MediaChapter> { new() });
playoutItems.Count.Should().Be(1);
}
}
private static Movie TestMovie(int id, TimeSpan duration, DateTime aired) => private static Movie TestMovie(int id, TimeSpan duration, DateTime aired) =>
new() new()
@@ -310,7 +310,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
return itemStartTime + totalDuration; return itemStartTime + totalDuration;
} }
protected List<PlayoutItem> AddFiller( internal static List<PlayoutItem> AddFiller(
PlayoutBuilderState playoutBuilderState, PlayoutBuilderState playoutBuilderState,
Dictionary<CollectionKey, IMediaCollectionEnumerator> enumerators, Dictionary<CollectionKey, IMediaCollectionEnumerator> enumerators,
ProgramScheduleItem scheduleItem, ProgramScheduleItem scheduleItem,
@@ -333,7 +333,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
} }
List<MediaChapter> effectiveChapters = chapters; List<MediaChapter> effectiveChapters = chapters;
if (allFiller.All(fp => fp.FillerKind != FillerKind.MidRoll)) if (allFiller.All(fp => fp.FillerKind != FillerKind.MidRoll) || effectiveChapters.Count <= 1)
{ {
effectiveChapters = new List<MediaChapter>(); effectiveChapters = new List<MediaChapter>();
} }
@@ -356,7 +356,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
} }
} }
if (!effectiveChapters.Any()) if (effectiveChapters.Count <= 1)
{ {
result.Add(playoutItem); result.Add(playoutItem);
} }
@@ -493,7 +493,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
? remainingToFill ? remainingToFill
: remainingToFill / (effectiveChapters.Count - 1); : remainingToFill / (effectiveChapters.Count - 1);
TimeSpan filled = TimeSpan.Zero; TimeSpan filled = TimeSpan.Zero;
for (var i = 0; i < chapters.Count; i++) for (var i = 0; i < effectiveChapters.Count; i++)
{ {
result.Add(playoutItem.ForChapter(effectiveChapters[i])); result.Add(playoutItem.ForChapter(effectiveChapters[i]));
if (i < effectiveChapters.Count - 1) if (i < effectiveChapters.Count - 1)
@@ -650,7 +650,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
return result; return result;
} }
private Option<PlayoutItem> FallbackFillerForPad( private static Option<PlayoutItem> FallbackFillerForPad(
PlayoutBuilderState playoutBuilderState, PlayoutBuilderState playoutBuilderState,
Dictionary<CollectionKey, IMediaCollectionEnumerator> enumerators, Dictionary<CollectionKey, IMediaCollectionEnumerator> enumerators,
ProgramScheduleItem scheduleItem, ProgramScheduleItem scheduleItem,