Browse Source

refactor playout build errors (#2480)

* refactor classic playout builds

* refactor sequential playout builds

* refactor block playout building

* don't fail building an empty block schedule

* fix scripted playout build errors
pull/2483/head
Jason Dove 3 months ago committed by GitHub
parent
commit
3e8ac9914c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 102
      ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs
  2. 238
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  3. 72
      ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs
  4. 42
      ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutBuilderTests.cs
  5. 206
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs
  6. 690
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs
  7. 33
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs
  8. 18
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs
  9. 2
      ErsatzTV.Core/Interfaces/Scheduling/IBlockPlayoutBuilder.cs
  10. 2
      ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs
  11. 2
      ErsatzTV.Core/Interfaces/Scheduling/IScriptedPlayoutBuilder.cs
  12. 2
      ErsatzTV.Core/Interfaces/Scheduling/ISequentialPlayoutBuilder.cs
  13. 7
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs
  14. 11
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutPreviewBuilder.cs
  15. 8
      ErsatzTV.Core/Scheduling/PlayoutBuildException.cs
  16. 132
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  17. 10
      ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs
  18. 14
      ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs

102
ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using ErsatzTV.Application.Scheduling;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Scheduling;
using ErsatzTV.Core.Interfaces.Repositories;
@ -57,65 +58,70 @@ public class PreviewPlaylistPlayoutHandler( @@ -57,65 +58,70 @@ public class PreviewPlaylistPlayoutHandler(
// TODO: make an explicit method to preview, this is ugly
playoutBuilder.TrimStart = false;
playoutBuilder.DebugPlaylist = playout.ProgramSchedule.Items[0].Playlist;
PlayoutBuildResult result = await playoutBuilder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await playoutBuilder.Build(
DateTimeOffset.Now,
playout,
referenceData,
PlayoutBuildMode.Reset,
cancellationToken);
var maxItems = 0;
Dictionary<PlaylistItem, List<MediaItem>> map =
await mediaCollectionRepository.GetPlaylistItemMap(
playout.ProgramSchedule.Items[0].Playlist,
cancellationToken);
foreach (PlaylistItem item in playout.ProgramSchedule.Items[0].Playlist.Items)
{
if (item.PlayAll)
{
maxItems += map[item].Count;
}
else
return await buildResult.MatchAsync(
async result =>
{
maxItems += 1;
}
}
var maxItems = 0;
Dictionary<PlaylistItem, List<MediaItem>> map =
await mediaCollectionRepository.GetPlaylistItemMap(
playout.ProgramSchedule.Items[0].Playlist,
cancellationToken);
foreach (PlaylistItem item in playout.ProgramSchedule.Items[0].Playlist.Items)
{
if (item.PlayAll)
{
maxItems += map[item].Count;
}
else
{
maxItems += 1;
}
}
// limit preview to once through the playlist
var onceThrough = result.AddedItems.Take(maxItems).ToList();
// limit preview to once through the playlist
var onceThrough = result.AddedItems.Take(maxItems).ToList();
// load playout item details for title
foreach (PlayoutItem playoutItem in onceThrough)
{
Option<MediaItem> maybeMediaItem = await dbContext.MediaItems
.AsNoTracking()
.Include(mi => (mi as Movie).MovieMetadata)
.Include(mi => (mi as Movie).MediaVersions)
.Include(mi => (mi as MusicVideo).MusicVideoMetadata)
.Include(mi => (mi as MusicVideo).MediaVersions)
.Include(mi => (mi as MusicVideo).Artist)
.ThenInclude(mm => mm.ArtistMetadata)
.Include(mi => (mi as Episode).EpisodeMetadata)
.Include(mi => (mi as Episode).MediaVersions)
.Include(mi => (mi as Episode).Season)
.ThenInclude(s => s.SeasonMetadata)
.Include(mi => (mi as Episode).Season.Show)
.ThenInclude(s => s.ShowMetadata)
.Include(mi => (mi as OtherVideo).OtherVideoMetadata)
.Include(mi => (mi as OtherVideo).MediaVersions)
.Include(mi => (mi as Song).SongMetadata)
.Include(mi => (mi as Song).MediaVersions)
.Include(mi => (mi as Image).ImageMetadata)
.Include(mi => (mi as Image).MediaVersions)
.SelectOneAsync(mi => mi.Id, mi => mi.Id == playoutItem.MediaItemId, cancellationToken);
// load playout item details for title
foreach (PlayoutItem playoutItem in onceThrough)
{
Option<MediaItem> maybeMediaItem = await dbContext.MediaItems
.AsNoTracking()
.Include(mi => (mi as Movie).MovieMetadata)
.Include(mi => (mi as Movie).MediaVersions)
.Include(mi => (mi as MusicVideo).MusicVideoMetadata)
.Include(mi => (mi as MusicVideo).MediaVersions)
.Include(mi => (mi as MusicVideo).Artist)
.ThenInclude(mm => mm.ArtistMetadata)
.Include(mi => (mi as Episode).EpisodeMetadata)
.Include(mi => (mi as Episode).MediaVersions)
.Include(mi => (mi as Episode).Season)
.ThenInclude(s => s.SeasonMetadata)
.Include(mi => (mi as Episode).Season.Show)
.ThenInclude(s => s.ShowMetadata)
.Include(mi => (mi as OtherVideo).OtherVideoMetadata)
.Include(mi => (mi as OtherVideo).MediaVersions)
.Include(mi => (mi as Song).SongMetadata)
.Include(mi => (mi as Song).MediaVersions)
.Include(mi => (mi as Image).ImageMetadata)
.Include(mi => (mi as Image).MediaVersions)
.SelectOneAsync(mi => mi.Id, mi => mi.Id == playoutItem.MediaItemId, cancellationToken);
foreach (MediaItem mediaItem in maybeMediaItem)
{
playoutItem.MediaItem = mediaItem;
}
}
foreach (MediaItem mediaItem in maybeMediaItem)
{
playoutItem.MediaItem = mediaItem;
}
}
return onceThrough.OrderBy(i => i.StartOffset).Map(Scheduling.Mapper.ProjectToViewModel).ToList();
return onceThrough.OrderBy(i => i.StartOffset).Map(Scheduling.Mapper.ProjectToViewModel).ToList();
},
_ => []);
}
private static ProgramScheduleItemFlood MapToScheduleItem(PreviewPlaylistPlayout request) =>

238
ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs

@ -124,26 +124,31 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -124,26 +124,31 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
playout.ScheduleKind);
string channelNumber = referenceData.Channel.Number;
channelName = referenceData.Channel.Name;
PlayoutBuildResult result = PlayoutBuildResult.Empty;
Either<BaseError, PlayoutBuildResult> buildResult = BaseError.New("Unsupported schedule kind");
switch (playout.ScheduleKind)
{
case PlayoutScheduleKind.Block:
result = await _blockPlayoutBuilder.Build(
buildResult = await _blockPlayoutBuilder.Build(
request.Start,
playout,
referenceData,
request.Mode,
cancellationToken);
result = await _blockPlayoutFillerBuilder.Build(
playout,
referenceData,
result,
request.Mode,
cancellationToken);
foreach (var result in buildResult.RightToSeq())
{
buildResult = await _blockPlayoutFillerBuilder.Build(
playout,
referenceData,
result,
request.Mode,
cancellationToken);
}
break;
case PlayoutScheduleKind.Sequential:
result = await _sequentialPlayoutBuilder.Build(
buildResult = await _sequentialPlayoutBuilder.Build(
request.Start,
playout,
referenceData,
@ -151,7 +156,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -151,7 +156,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
cancellationToken);
break;
case PlayoutScheduleKind.Scripted:
result = await _scriptedPlayoutBuilder.Build(
buildResult = await _scriptedPlayoutBuilder.Build(
request.Start,
playout,
referenceData,
@ -164,7 +169,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -164,7 +169,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
case PlayoutScheduleKind.None:
case PlayoutScheduleKind.Classic:
default:
result = await _playoutBuilder.Build(
buildResult = await _playoutBuilder.Build(
request.Start,
playout,
referenceData,
@ -173,123 +178,134 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -173,123 +178,134 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
break;
}
var changeCount = 0;
return await buildResult.MatchAsync<Either<BaseError, PlayoutBuildResult>>(
async result =>
{
var changeCount = 0;
if (result.RerunHistoryToRemove.Count > 0)
{
changeCount += await dbContext.RerunHistory
.Where(rh => result.RerunHistoryToRemove.Contains(rh.Id))
.ExecuteDeleteAsync(cancellationToken);
}
if (result.RerunHistoryToRemove.Count > 0)
{
changeCount += await dbContext.RerunHistory
.Where(rh => result.RerunHistoryToRemove.Contains(rh.Id))
.ExecuteDeleteAsync(cancellationToken);
}
if (result.AddedRerunHistory.Count > 0)
{
changeCount += 1;
await dbContext.BulkInsertAsync(result.AddedRerunHistory, cancellationToken: cancellationToken);
}
if (result.AddedRerunHistory.Count > 0)
{
changeCount += 1;
await dbContext.BulkInsertAsync(result.AddedRerunHistory, cancellationToken: cancellationToken);
}
if (result.ClearItems)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => pi.PlayoutId == playout.Id)
.ExecuteDeleteAsync(cancellationToken);
}
if (result.ClearItems)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => pi.PlayoutId == playout.Id)
.ExecuteDeleteAsync(cancellationToken);
}
foreach (DateTimeOffset removeBefore in result.RemoveBefore)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => pi.PlayoutId == playout.Id)
.Where(pi => pi.Finish < removeBefore.UtcDateTime - referenceData.MaxPlayoutOffset)
.ExecuteDeleteAsync(cancellationToken);
}
foreach (DateTimeOffset removeBefore in result.RemoveBefore)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => pi.PlayoutId == playout.Id)
.Where(pi => pi.Finish < removeBefore.UtcDateTime - referenceData.MaxPlayoutOffset)
.ExecuteDeleteAsync(cancellationToken);
}
foreach (DateTimeOffset removeAfter in result.RemoveAfter)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => pi.PlayoutId == playout.Id)
.Where(pi => pi.Start >= removeAfter.UtcDateTime)
.ExecuteDeleteAsync(cancellationToken);
}
foreach (DateTimeOffset removeAfter in result.RemoveAfter)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => pi.PlayoutId == playout.Id)
.Where(pi => pi.Start >= removeAfter.UtcDateTime)
.ExecuteDeleteAsync(cancellationToken);
}
if (result.ItemsToRemove.Count > 0)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => result.ItemsToRemove.Contains(pi.Id))
.ExecuteDeleteAsync(cancellationToken);
}
if (result.ItemsToRemove.Count > 0)
{
changeCount += await dbContext.PlayoutItems
.Where(pi => result.ItemsToRemove.Contains(pi.Id))
.ExecuteDeleteAsync(cancellationToken);
}
if (result.AddedItems.Count > 0)
{
changeCount += 1;
bool anyWatermarks = result.AddedItems.Any(i =>
i.PlayoutItemWatermarks is not null && i.PlayoutItemWatermarks.Count > 0);
bool anyGraphicsElements = result.AddedItems.Any(i =>
i.PlayoutItemGraphicsElements is not null && i.PlayoutItemGraphicsElements.Count > 0);
if (anyWatermarks || anyGraphicsElements)
{
// need to use slow ef core to also insert watermarks and graphics elements properly
await dbContext.AddRangeAsync(result.AddedItems, cancellationToken);
}
else
{
// no watermarks or graphics, bulk insert is ok
await dbContext.BulkInsertAsync(result.AddedItems, cancellationToken: cancellationToken);
}
}
if (result.AddedItems.Count > 0)
{
changeCount += 1;
bool anyWatermarks = result.AddedItems.Any(i =>
i.PlayoutItemWatermarks is not null && i.PlayoutItemWatermarks.Count > 0);
bool anyGraphicsElements = result.AddedItems.Any(i =>
i.PlayoutItemGraphicsElements is not null && i.PlayoutItemGraphicsElements.Count > 0);
if (anyWatermarks || anyGraphicsElements)
{
// need to use slow ef core to also insert watermarks and graphics elements properly
await dbContext.AddRangeAsync(result.AddedItems, cancellationToken);
}
else
{
// no watermarks or graphics, bulk insert is ok
await dbContext.BulkInsertAsync(result.AddedItems, cancellationToken: cancellationToken);
}
}
if (result.HistoryToRemove.Count > 0)
{
changeCount += await dbContext.PlayoutHistory
.Where(ph => result.HistoryToRemove.Contains(ph.Id))
.ExecuteDeleteAsync(cancellationToken);
}
if (result.HistoryToRemove.Count > 0)
{
changeCount += await dbContext.PlayoutHistory
.Where(ph => result.HistoryToRemove.Contains(ph.Id))
.ExecuteDeleteAsync(cancellationToken);
}
if (result.AddedHistory.Count > 0)
{
changeCount += 1;
await dbContext.BulkInsertAsync(result.AddedHistory, cancellationToken: cancellationToken);
}
if (result.AddedHistory.Count > 0)
{
changeCount += 1;
await dbContext.BulkInsertAsync(result.AddedHistory, cancellationToken: cancellationToken);
}
// let any active segmenter processes know that the playout has been modified
// and therefore the segmenter may need to seek into the next item instead of
// starting at the beginning (if already working ahead)
changeCount += await dbContext.SaveChangesAsync(cancellationToken);
bool hasChanges = changeCount > 0;
// let any active segmenter processes know that the playout has been modified
// and therefore the segmenter may need to seek into the next item instead of
// starting at the beginning (if already working ahead)
changeCount += await dbContext.SaveChangesAsync(cancellationToken);
bool hasChanges = changeCount > 0;
if (request.Mode != PlayoutBuildMode.Continue && hasChanges)
{
_ffmpegSegmenterService.PlayoutUpdated(referenceData.Channel.Number);
}
if (request.Mode != PlayoutBuildMode.Continue && hasChanges)
{
_ffmpegSegmenterService.PlayoutUpdated(referenceData.Channel.Number);
}
await _workerChannel.WriteAsync(
new CheckForOverlappingPlayoutItems(request.PlayoutId),
cancellationToken);
await _workerChannel.WriteAsync(
new CheckForOverlappingPlayoutItems(request.PlayoutId),
cancellationToken);
await _workerChannel.WriteAsync(new InsertPlayoutGaps(request.PlayoutId), cancellationToken);
await _workerChannel.WriteAsync(new InsertPlayoutGaps(request.PlayoutId), cancellationToken);
string fileName = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channelNumber}.xml");
if (hasChanges || !File.Exists(fileName) ||
playout.ScheduleKind is PlayoutScheduleKind.ExternalJson)
{
await _workerChannel.WriteAsync(new RefreshChannelData(channelNumber), cancellationToken);
// refresh guide data for all mirror channels, too
List<string> maybeMirrors = await dbContext.Channels
.AsNoTracking()
.Filter(c => c.MirrorSourceChannelId == referenceData.Channel.Id)
.Map(c => c.Number)
.ToListAsync(cancellationToken);
foreach (string mirror in maybeMirrors)
{
await _workerChannel.WriteAsync(new RefreshChannelData(mirror), cancellationToken);
}
}
string fileName = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channelNumber}.xml");
if (hasChanges || !File.Exists(fileName) ||
playout.ScheduleKind is PlayoutScheduleKind.ExternalJson)
{
await _workerChannel.WriteAsync(new RefreshChannelData(channelNumber), cancellationToken);
// refresh guide data for all mirror channels, too
List<string> maybeMirrors = await dbContext.Channels
.AsNoTracking()
.Filter(c => c.MirrorSourceChannelId == referenceData.Channel.Id)
.Map(c => c.Number)
.ToListAsync(cancellationToken);
foreach (string mirror in maybeMirrors)
{
await _workerChannel.WriteAsync(new RefreshChannelData(mirror), cancellationToken);
}
}
await _workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id), cancellationToken);
await _workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id), cancellationToken);
newBuildStatus.Success = true;
newBuildStatus.Success = true;
return result;
},
error =>
{
newBuildStatus.Success = false;
newBuildStatus.Message = error.Value;
return result;
return error;
});
}
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{

72
ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Scheduling;
using ErsatzTV.Core.Interfaces.Scheduling;
@ -68,7 +69,7 @@ public class PreviewBlockPlayoutHandler( @@ -68,7 +69,7 @@ public class PreviewBlockPlayoutHandler(
playout.PlayoutHistory.ToList(),
TimeSpan.Zero);
PlayoutBuildResult result =
Either<BaseError, PlayoutBuildResult> buildResult =
await blockPlayoutBuilder.Build(
DateTimeOffset.Now,
playout,
@ -76,40 +77,45 @@ public class PreviewBlockPlayoutHandler( @@ -76,40 +77,45 @@ public class PreviewBlockPlayoutHandler(
PlayoutBuildMode.Reset,
cancellationToken);
// load playout item details for title
foreach (PlayoutItem playoutItem in result.AddedItems)
{
Option<MediaItem> maybeMediaItem = await dbContext.MediaItems
.AsNoTracking()
.Include(mi => (mi as Movie).MovieMetadata)
.Include(mi => (mi as Movie).MediaVersions)
.Include(mi => (mi as MusicVideo).MusicVideoMetadata)
.Include(mi => (mi as MusicVideo).MediaVersions)
.Include(mi => (mi as MusicVideo).Artist)
.ThenInclude(mm => mm.ArtistMetadata)
.Include(mi => (mi as Episode).EpisodeMetadata)
.Include(mi => (mi as Episode).MediaVersions)
.Include(mi => (mi as Episode).Season)
.ThenInclude(s => s.SeasonMetadata)
.Include(mi => (mi as Episode).Season.Show)
.ThenInclude(s => s.ShowMetadata)
.Include(mi => (mi as OtherVideo).OtherVideoMetadata)
.Include(mi => (mi as OtherVideo).MediaVersions)
.Include(mi => (mi as Song).SongMetadata)
.Include(mi => (mi as Song).MediaVersions)
.Include(mi => (mi as Image).ImageMetadata)
.Include(mi => (mi as Image).MediaVersions)
.Include(mi => (mi as RemoteStream).RemoteStreamMetadata)
.Include(mi => (mi as RemoteStream).MediaVersions)
.SelectOneAsync(mi => mi.Id, mi => mi.Id == playoutItem.MediaItemId, cancellationToken);
foreach (MediaItem mediaItem in maybeMediaItem)
return await buildResult.MatchAsync(
async result =>
{
playoutItem.MediaItem = mediaItem;
}
}
// load playout item details for title
foreach (PlayoutItem playoutItem in result.AddedItems)
{
Option<MediaItem> maybeMediaItem = await dbContext.MediaItems
.AsNoTracking()
.Include(mi => (mi as Movie).MovieMetadata)
.Include(mi => (mi as Movie).MediaVersions)
.Include(mi => (mi as MusicVideo).MusicVideoMetadata)
.Include(mi => (mi as MusicVideo).MediaVersions)
.Include(mi => (mi as MusicVideo).Artist)
.ThenInclude(mm => mm.ArtistMetadata)
.Include(mi => (mi as Episode).EpisodeMetadata)
.Include(mi => (mi as Episode).MediaVersions)
.Include(mi => (mi as Episode).Season)
.ThenInclude(s => s.SeasonMetadata)
.Include(mi => (mi as Episode).Season.Show)
.ThenInclude(s => s.ShowMetadata)
.Include(mi => (mi as OtherVideo).OtherVideoMetadata)
.Include(mi => (mi as OtherVideo).MediaVersions)
.Include(mi => (mi as Song).SongMetadata)
.Include(mi => (mi as Song).MediaVersions)
.Include(mi => (mi as Image).ImageMetadata)
.Include(mi => (mi as Image).MediaVersions)
.Include(mi => (mi as RemoteStream).RemoteStreamMetadata)
.Include(mi => (mi as RemoteStream).MediaVersions)
.SelectOneAsync(mi => mi.Id, mi => mi.Id == playoutItem.MediaItemId, cancellationToken);
return result.AddedItems.Map(Mapper.ProjectToViewModel).ToList();
foreach (MediaItem mediaItem in maybeMediaItem)
{
playoutItem.MediaItem = mediaItem;
}
}
return result.AddedItems.Map(Mapper.ProjectToViewModel).ToList();
},
_ => []);
}
private static Block MapToBlock(ReplaceBlockItems request) =>

42
ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutBuilderTests.cs

@ -150,18 +150,22 @@ public class BlockPlayoutBuilderTests @@ -150,18 +150,22 @@ public class BlockPlayoutBuilderTests
[],
TimeSpan.Zero);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
now,
playout,
referenceData,
PlayoutBuildMode.Reset,
cancellationToken);
// this test only cares about "today"
result.AddedItems.RemoveAll(i => i.StartOffset.Date > now.Date);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
// this test only cares about "today"
result.AddedItems.RemoveAll(i => i.StartOffset.Date > now.Date);
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(9));
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(9));
}
}
[Test]
@ -293,18 +297,22 @@ public class BlockPlayoutBuilderTests @@ -293,18 +297,22 @@ public class BlockPlayoutBuilderTests
[],
TimeSpan.Zero);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
now,
playout,
referenceData,
PlayoutBuildMode.Reset,
cancellationToken);
// this test only cares about "today"
result.AddedItems.RemoveAll(i => i.StartOffset.Date > now.Date);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
// this test only cares about "today"
result.AddedItems.RemoveAll(i => i.StartOffset.Date > now.Date);
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(9));
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(9));
}
}
[Test]
@ -459,18 +467,22 @@ public class BlockPlayoutBuilderTests @@ -459,18 +467,22 @@ public class BlockPlayoutBuilderTests
[],
TimeSpan.Zero);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
now,
playout,
referenceData,
PlayoutBuildMode.Reset,
cancellationToken);
// this test only cares about "today"
result.AddedItems.RemoveAll(i => i.StartOffset.Date > now.Date);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
// this test only cares about "today"
result.AddedItems.RemoveAll(i => i.StartOffset.Date > now.Date);
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(9));
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(9));
}
}
}

206
ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs

@ -28,7 +28,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -28,7 +28,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(6);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -37,8 +37,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -37,8 +37,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(1);
result.AddedItems.Head().MediaItemId.ShouldBe(1);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(1);
result.AddedItems.Head().MediaItemId.ShouldBe(1);
}
playout.Anchor.NextStartOffset.ShouldBe(finish);
@ -48,7 +52,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -48,7 +52,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = HoursAfterMidnight(1);
DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6);
PlayoutBuildResult result2 = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult2 = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -57,9 +61,13 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -57,9 +61,13 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish2,
CancellationToken);
result2.AddedItems.Count.ShouldBe(1);
result2.AddedItems[0].StartOffset.ShouldBe(finish);
result2.AddedItems[0].MediaItemId.ShouldBe(2);
buildResult2.IsRight.ShouldBeTrue();
foreach (var result2 in buildResult2.RightToSeq())
{
result2.AddedItems.Count.ShouldBe(1);
result2.AddedItems[0].StartOffset.ShouldBe(finish);
result2.AddedItems[0].MediaItemId.ShouldBe(2);
}
playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(12));
playout.ProgramScheduleAnchors.Count.ShouldBe(1);
@ -80,7 +88,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -80,7 +88,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(6);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -89,8 +97,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -89,8 +97,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(1);
result.AddedItems.Head().MediaItemId.ShouldBe(1);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(1);
result.AddedItems.Head().MediaItemId.ShouldBe(1);
}
playout.Anchor.NextStartOffset.ShouldBe(finish);
playout.ProgramScheduleAnchors.Count.ShouldBe(1);
@ -99,7 +111,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -99,7 +111,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = HoursAfterMidnight(1);
DateTimeOffset finish2 = start2 + TimeSpan.FromHours(12);
PlayoutBuildResult result2 = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult2 = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -108,11 +120,15 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -108,11 +120,15 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish2,
CancellationToken);
result2.AddedItems.Count.ShouldBe(2);
result2.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(6));
result2.AddedItems[0].MediaItemId.ShouldBe(2);
result2.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(12));
result2.AddedItems[1].MediaItemId.ShouldBe(1);
buildResult2.IsRight.ShouldBeTrue();
foreach (var result2 in buildResult2.RightToSeq())
{
result2.AddedItems.Count.ShouldBe(2);
result2.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(6));
result2.AddedItems[0].MediaItemId.ShouldBe(2);
result2.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(12));
result2.AddedItems[1].MediaItemId.ShouldBe(1);
}
playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(18));
playout.ProgramScheduleAnchors.Count.ShouldBe(1);
@ -133,7 +149,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -133,7 +149,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromDays(1);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -142,8 +158,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -142,8 +158,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(4);
result.AddedItems.Map(i => i.MediaItemId).ToList().ShouldBe([1, 2, 1, 2]);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(4);
result.AddedItems.Map(i => i.MediaItemId).ToList().ShouldBe([1, 2, 1, 2]);
}
playout.Anchor.NextStartOffset.ShouldBe(finish);
playout.ProgramScheduleAnchors.Count.ShouldBe(1);
@ -177,7 +197,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -177,7 +197,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = HoursAfterMidnight(1);
DateTimeOffset finish2 = start2 + TimeSpan.FromDays(1);
result = await builder.Build(
buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -186,9 +206,13 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -186,9 +206,13 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish2,
CancellationToken);
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.ShouldBe(finish);
result.AddedItems[0].MediaItemId.ShouldBe(1);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(1);
result.AddedItems[0].StartOffset.ShouldBe(finish);
result.AddedItems[0].MediaItemId.ShouldBe(1);
}
playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(30));
@ -199,7 +223,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -199,7 +223,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start3 = HoursAfterMidnight(2);
DateTimeOffset finish3 = start3 + TimeSpan.FromDays(1);
result = await builder.Build(
buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -208,7 +232,11 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -208,7 +232,11 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish3,
CancellationToken);
result.AddedItems.Count.ShouldBe(0);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(0);
}
playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(30));
@ -231,7 +259,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -231,7 +259,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(6);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -240,7 +268,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -240,7 +268,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(6);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(6);
}
playout.ProgramScheduleAnchors.Count.ShouldBe(1);
playout.ProgramScheduleAnchors.Head().EnumeratorState.Seed.ShouldBeGreaterThan(0);
@ -251,7 +284,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -251,7 +284,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = HoursAfterMidnight(0);
DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6);
PlayoutBuildResult result2 = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult2 = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -281,7 +314,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -281,7 +314,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0).AddSeconds(5);
DateTimeOffset finish = start + TimeSpan.FromDays(2);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -290,7 +323,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -290,7 +323,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(53);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(53);
}
playout.ProgramScheduleAnchors.Count.ShouldBe(2);
playout.ProgramScheduleAnchors.All(x => x.AnchorDate is not null).ShouldBeTrue();
@ -318,7 +356,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -318,7 +356,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = start.AddHours(1);
DateTimeOffset finish2 = start2 + TimeSpan.FromDays(2);
PlayoutBuildResult result2 = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult2 = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -350,7 +388,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -350,7 +388,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(6);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -359,7 +397,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -359,7 +397,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(6);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(6);
}
playout.ProgramScheduleAnchors.Count.ShouldBe(2);
PlayoutProgramScheduleAnchor primaryAnchor =
playout.ProgramScheduleAnchors.First(a => a.SmartCollectionId == 1);
@ -371,7 +414,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -371,7 +414,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = HoursAfterMidnight(0);
DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6);
PlayoutBuildResult result2 = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult2 = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -402,7 +445,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -402,7 +445,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0).AddSeconds(5);
DateTimeOffset finish = start + TimeSpan.FromDays(2);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -411,7 +454,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -411,7 +454,12 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(53);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(53);
}
playout.ProgramScheduleAnchors.Count.ShouldBe(4);
playout.ProgramScheduleAnchors.All(x => x.AnchorDate is not null).ShouldBeTrue();
@ -429,7 +477,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -429,7 +477,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = start.AddHours(i);
DateTimeOffset finish2 = start2 + TimeSpan.FromDays(2);
PlayoutBuildResult result2 = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult2 = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -555,7 +603,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -555,7 +603,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(32);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -564,20 +612,24 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -564,20 +612,24 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(5);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(5);
result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(9));
result.AddedItems[0].MediaItemId.ShouldBe(1);
result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(10));
result.AddedItems[1].MediaItemId.ShouldBe(2);
result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(11));
result.AddedItems[2].MediaItemId.ShouldBe(1);
result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(9));
result.AddedItems[0].MediaItemId.ShouldBe(1);
result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(10));
result.AddedItems[1].MediaItemId.ShouldBe(2);
result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(11));
result.AddedItems[2].MediaItemId.ShouldBe(1);
result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(12));
result.AddedItems[3].MediaItemId.ShouldBe(3);
result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(12));
result.AddedItems[3].MediaItemId.ShouldBe(3);
result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(31));
result.AddedItems[4].MediaItemId.ShouldBe(2);
result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(31));
result.AddedItems[4].MediaItemId.ShouldBe(2);
}
playout.Anchor.InFlood.ShouldBeTrue();
@ -677,7 +729,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -677,7 +729,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(5);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -686,17 +738,21 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -686,17 +738,21 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(4);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(4);
result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(1));
result.AddedItems[0].MediaItemId.ShouldBe(1);
result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(2));
result.AddedItems[1].MediaItemId.ShouldBe(1);
result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(1));
result.AddedItems[0].MediaItemId.ShouldBe(1);
result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(2));
result.AddedItems[1].MediaItemId.ShouldBe(1);
result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(3));
result.AddedItems[2].MediaItemId.ShouldBe(2);
result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(4));
result.AddedItems[3].MediaItemId.ShouldBe(2);
result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(3));
result.AddedItems[2].MediaItemId.ShouldBe(2);
result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(4));
result.AddedItems[3].MediaItemId.ShouldBe(2);
}
playout.Anchor.ScheduleItemsEnumeratorState.Index.ShouldBe(1);
playout.Anchor.MultipleRemaining.ShouldBe(1);
@ -798,7 +854,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -798,7 +854,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
rerunHelper,
Logger);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -807,19 +863,23 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -807,19 +863,23 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(5);
result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(1));
result.AddedItems[0].MediaItemId.ShouldBe(1);
result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(2));
result.AddedItems[1].MediaItemId.ShouldBe(1);
result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(3));
result.AddedItems[2].MediaItemId.ShouldBe(2);
result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(4));
result.AddedItems[3].MediaItemId.ShouldBe(2);
result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5));
result.AddedItems[4].MediaItemId.ShouldBe(2);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(5);
result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(1));
result.AddedItems[0].MediaItemId.ShouldBe(1);
result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(2));
result.AddedItems[1].MediaItemId.ShouldBe(1);
result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(3));
result.AddedItems[2].MediaItemId.ShouldBe(2);
result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(4));
result.AddedItems[3].MediaItemId.ShouldBe(2);
result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5));
result.AddedItems[4].MediaItemId.ShouldBe(2);
}
playout.Anchor.ScheduleItemsEnumeratorState.Index.ShouldBe(0);
playout.Anchor.DurationFinish.ShouldBeNull();

690
ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs

File diff suppressed because it is too large Load Diff

33
ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs

@ -116,7 +116,7 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase @@ -116,7 +116,7 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(24);
DateTimeOffset finish = start + TimeSpan.FromDays(1);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -125,19 +125,24 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase @@ -125,19 +125,24 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(4);
result.AddedItems[0].MediaItemId.ShouldBe(2);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.Zero);
result.AddedItems[0].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(6));
result.AddedItems[1].MediaItemId.ShouldBe(3);
result.AddedItems[1].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(6));
result.AddedItems[1].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(12));
result.AddedItems[2].MediaItemId.ShouldBe(1);
result.AddedItems[2].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(12));
result.AddedItems[2].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(18));
result.AddedItems[3].MediaItemId.ShouldBe(2);
result.AddedItems[3].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(18));
result.AddedItems[3].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.Zero);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(4);
result.AddedItems[0].MediaItemId.ShouldBe(2);
result.AddedItems[0].StartOffset.TimeOfDay.ShouldBe(TimeSpan.Zero);
result.AddedItems[0].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(6));
result.AddedItems[1].MediaItemId.ShouldBe(3);
result.AddedItems[1].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(6));
result.AddedItems[1].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(12));
result.AddedItems[2].MediaItemId.ShouldBe(1);
result.AddedItems[2].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(12));
result.AddedItems[2].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(18));
result.AddedItems[3].MediaItemId.ShouldBe(2);
result.AddedItems[3].StartOffset.TimeOfDay.ShouldBe(TimeSpan.FromHours(18));
result.AddedItems[3].FinishOffset.TimeOfDay.ShouldBe(TimeSpan.Zero);
}
playout.Anchor.NextStartOffset.ShouldBe(HoursAfterMidnight(48));
}

18
ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs

@ -26,7 +26,7 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase @@ -26,7 +26,7 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(6);
PlayoutBuildResult result = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -35,7 +35,12 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase @@ -35,7 +35,12 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase
finish,
CancellationToken);
result.AddedItems.Count.ShouldBe(6);
buildResult.IsRight.ShouldBeTrue();
foreach (var result in buildResult.RightToSeq())
{
result.AddedItems.Count.ShouldBe(6);
}
playout.Anchor.NextStartOffset.ShouldBe(finish);
playout.ProgramScheduleAnchors.Count.ShouldBe(1);
@ -46,7 +51,7 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase @@ -46,7 +51,7 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase
DateTimeOffset start2 = HoursAfterMidnight(0);
DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6);
PlayoutBuildResult result2 = await builder.Build(
Either<BaseError, PlayoutBuildResult> buildResult2 = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
@ -55,7 +60,12 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase @@ -55,7 +60,12 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase
finish2,
CancellationToken);
result2.AddedItems.Count.ShouldBe(6);
buildResult2.IsRight.ShouldBeTrue();
foreach (var result in buildResult2.RightToSeq())
{
result.AddedItems.Count.ShouldBe(6);
}
playout.Anchor.NextStartOffset.ShouldBe(finish);
playout.ProgramScheduleAnchors.Count.ShouldBe(1);

2
ErsatzTV.Core/Interfaces/Scheduling/IBlockPlayoutBuilder.cs

@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling;
public interface IBlockPlayoutBuilder
{
Task<PlayoutBuildResult> Build(
Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,

2
ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs

@ -8,7 +8,7 @@ public interface IPlayoutBuilder @@ -8,7 +8,7 @@ public interface IPlayoutBuilder
bool TrimStart { get; set; }
Playlist DebugPlaylist { get; set; }
Task<PlayoutBuildResult> Build(
Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,

2
ErsatzTV.Core/Interfaces/Scheduling/IScriptedPlayoutBuilder.cs

@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling;
public interface IScriptedPlayoutBuilder
{
Task<PlayoutBuildResult> Build(
Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,

2
ErsatzTV.Core/Interfaces/Scheduling/ISequentialPlayoutBuilder.cs

@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling;
public interface ISequentialPlayoutBuilder
{
Task<PlayoutBuildResult> Build(
Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,

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

@ -27,7 +27,7 @@ public class BlockPlayoutBuilder( @@ -27,7 +27,7 @@ public class BlockPlayoutBuilder(
protected virtual ILogger Logger => logger;
public virtual async Task<PlayoutBuildResult> Build(
public virtual async Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,
@ -57,6 +57,11 @@ public class BlockPlayoutBuilder( @@ -57,6 +57,11 @@ public class BlockPlayoutBuilder(
List<EffectiveBlock> blocksToSchedule =
EffectiveBlock.GetEffectiveBlocks(referenceData.PlayoutTemplates, start, daysToBuild);
if (blocksToSchedule.Count == 0)
{
return result;
}
// always start at the beginning of the block
start = blocksToSchedule.Min(b => b.Start);

11
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutPreviewBuilder.cs

@ -26,7 +26,7 @@ public class BlockPlayoutPreviewBuilder( @@ -26,7 +26,7 @@ public class BlockPlayoutPreviewBuilder(
protected override ILogger Logger => NullLogger.Instance;
public override async Task<PlayoutBuildResult> Build(
public override async Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,
@ -35,11 +35,16 @@ public class BlockPlayoutPreviewBuilder( @@ -35,11 +35,16 @@ public class BlockPlayoutPreviewBuilder(
{
_randomizedCollections.Add(playout.Channel.UniqueId, []);
PlayoutBuildResult result = await base.Build(start, playout, referenceData, mode, cancellationToken);
Either<BaseError, PlayoutBuildResult> buildResult = await base.Build(
start,
playout,
referenceData,
mode,
cancellationToken);
_randomizedCollections.Remove(playout.Channel.UniqueId);
return result;
return buildResult;
}
protected override Task<int> GetDaysToBuild(CancellationToken cancellationToken) => Task.FromResult(1);

8
ErsatzTV.Core/Scheduling/PlayoutBuildException.cs

@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
namespace ErsatzTV.Core.Scheduling;
public class PlayoutBuildException : Exception
{
public PlayoutBuildException() : base() { }
public PlayoutBuildException(string message) : base(message) { }
public PlayoutBuildException(string message, Exception innerException) : base(message, innerException) { }
}

132
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -63,15 +63,13 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -63,15 +63,13 @@ public class PlayoutBuilder : IPlayoutBuilder
}
}
public async Task<PlayoutBuildResult> Build(
public async Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildMode mode,
CancellationToken cancellationToken)
{
PlayoutBuildResult result = PlayoutBuildResult.Empty;
if (playout.ScheduleKind is not PlayoutScheduleKind.Classic)
{
_logger.LogWarning(
@ -80,30 +78,39 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -80,30 +78,39 @@ public class PlayoutBuilder : IPlayoutBuilder
referenceData.Channel.Number,
referenceData.Channel.Name);
return result;
return BaseError.New($"Cannot build playout type {playout.ScheduleKind} using classic playout builder.");
}
foreach (PlayoutParameters parameters in await Validate(start, playout, referenceData, cancellationToken))
{
// for testing purposes
// if (mode == PlayoutBuildMode.Reset)
// {
// return await Build(playout, mode, parameters with { Start = parameters.Start.AddDays(-2) });
// }
result = await Build(playout, referenceData, result, mode, parameters, cancellationToken);
result = result with
Either<BaseError, PlayoutParameters> validateResult = await Validate(start, referenceData, cancellationToken);
return await validateResult.MatchAsync(
async parameters =>
{
RerunHistoryToRemove = _rerunHelper.GetHistoryToRemove(),
AddedRerunHistory = _rerunHelper.GetHistoryToAdd()
};
}
// for testing purposes
// if (mode == PlayoutBuildMode.Reset)
// {
// return await Build(playout, mode, parameters with { Start = parameters.Start.AddDays(-2) });
// }
return result;
Either<BaseError, PlayoutBuildResult> buildResult = await Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
mode,
parameters,
cancellationToken);
return buildResult.Match(
result => result with
{
RerunHistoryToRemove = _rerunHelper.GetHistoryToRemove(),
AddedRerunHistory = _rerunHelper.GetHistoryToAdd()
},
Either<BaseError, PlayoutBuildResult>.Left);
},
Either<BaseError, PlayoutBuildResult>.Left);
}
private Task<PlayoutBuildResult> Build(
private Task<Either<BaseError, PlayoutBuildResult>> Build(
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildResult result,
@ -117,7 +124,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -117,7 +124,7 @@ public class PlayoutBuilder : IPlayoutBuilder
_ => ContinuePlayout(playout, referenceData, result, parameters, cancellationToken)
};
internal async Task<PlayoutBuildResult> Build(
internal async Task<Either<BaseError, PlayoutBuildResult>> Build(
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildResult result,
@ -126,21 +133,19 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -126,21 +133,19 @@ public class PlayoutBuilder : IPlayoutBuilder
DateTimeOffset finish,
CancellationToken cancellationToken)
{
foreach (PlayoutParameters parameters in await Validate(start, playout, referenceData, cancellationToken))
{
result = await Build(
Either<BaseError, PlayoutParameters> validateResult = await Validate(start, referenceData, cancellationToken);
return await validateResult.MatchAsync(
async parameters => await Build(
playout,
referenceData,
result,
mode,
parameters with { Start = start, Finish = finish },
cancellationToken);
}
return result;
cancellationToken),
Either<BaseError, PlayoutBuildResult>.Left);
}
private async Task<PlayoutBuildResult> RefreshPlayout(
private async Task<Either<BaseError, PlayoutBuildResult>> RefreshPlayout(
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildResult result,
@ -266,7 +271,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -266,7 +271,7 @@ public class PlayoutBuilder : IPlayoutBuilder
cancellationToken);
}
private async Task<PlayoutBuildResult> ResetPlayout(
private async Task<Either<BaseError, PlayoutBuildResult>> ResetPlayout(
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildResult result,
@ -310,7 +315,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -310,7 +315,7 @@ public class PlayoutBuilder : IPlayoutBuilder
return result;
}
private async Task<PlayoutBuildResult> ContinuePlayout(
private async Task<Either<BaseError, PlayoutBuildResult>> ContinuePlayout(
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildResult result,
@ -340,9 +345,8 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -340,9 +345,8 @@ public class PlayoutBuilder : IPlayoutBuilder
cancellationToken);
}
private async Task<Option<PlayoutParameters>> Validate(
private async Task<Either<BaseError, PlayoutParameters>> Validate(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,
CancellationToken cancellationToken)
{
@ -351,7 +355,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -351,7 +355,7 @@ public class PlayoutBuilder : IPlayoutBuilder
if (collectionMediaItems.IsEmpty)
{
_logger.LogWarning("Playout {Playout} has no items", referenceData.Channel.Name);
return None;
return BaseError.New($"Playout {referenceData.Channel.Name} has no items");
}
Option<bool> skipMissingItems =
@ -365,24 +369,26 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -365,24 +369,26 @@ public class PlayoutBuilder : IPlayoutBuilder
{
Option<string> maybeName =
await _mediaCollectionRepository.GetNameFromKey(emptyCollection, cancellationToken);
if (maybeName.IsSome)
{
foreach (string name in maybeName)
return maybeName.Match(
name =>
{
_logger.LogError(
"Unable to rebuild playout; {CollectionType} {CollectionName} has no valid items!",
emptyCollection.CollectionType,
name);
}
}
else
{
_logger.LogError(
"Unable to rebuild playout; collection {@CollectionKey} has no valid items!",
emptyCollection);
}
return None;
return BaseError.New(
$"Unable to rebuild playout; {emptyCollection.CollectionType} {name} has no valid items!");
},
() =>
{
_logger.LogError(
"Unable to rebuild playout; collection {@CollectionKey} has no valid items!",
emptyCollection);
return BaseError.New(
$"Unable to rebuild playout; collection {HistoryDetails.KeyForCollectionKey(emptyCollection)} has no valid items!");
});
}
Option<int> daysToBuild = await _configElementRepository.GetValue<int>(
@ -397,7 +403,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -397,7 +403,7 @@ public class PlayoutBuilder : IPlayoutBuilder
collectionMediaItems);
}
private async Task<PlayoutBuildResult> BuildPlayoutItems(
private async Task<Either<BaseError, PlayoutBuildResult>> BuildPlayoutItems(
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildResult result,
@ -431,11 +437,11 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -431,11 +437,11 @@ public class PlayoutBuilder : IPlayoutBuilder
{
if (cancellationToken.IsCancellationRequested)
{
return result;
return BaseError.New("Playout build was canceled");
}
_logger.LogDebug("Building playout from {Start} to {Finish}", start, finish);
result = await BuildPlayoutItems(
Either<BaseError, PlayoutBuildResult> buildResult = await BuildPlayoutItems(
playout,
referenceData,
result,
@ -446,6 +452,16 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -446,6 +452,16 @@ public class PlayoutBuilder : IPlayoutBuilder
randomStartPoint,
cancellationToken);
foreach (BaseError error in buildResult.LeftToSeq())
{
return error;
}
foreach (PlayoutBuildResult r in buildResult.RightToSeq())
{
result = r;
}
// only randomize once (at the start of the playout)
randomStartPoint = false;
@ -455,14 +471,14 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -455,14 +471,14 @@ public class PlayoutBuilder : IPlayoutBuilder
if (cancellationToken.IsCancellationRequested)
{
return result;
return BaseError.New("Playout build was canceled");
}
if (start < playoutFinish)
{
// build one final time without continue anchors
_logger.LogDebug("Building final playout from {Start} to {Finish}", start, playoutFinish);
result = await BuildPlayoutItems(
Either<BaseError, PlayoutBuildResult> buildResult = await BuildPlayoutItems(
playout,
referenceData,
result,
@ -472,6 +488,16 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -472,6 +488,16 @@ public class PlayoutBuilder : IPlayoutBuilder
false,
randomStartPoint,
cancellationToken);
foreach (BaseError error in buildResult.LeftToSeq())
{
return error;
}
foreach (PlayoutBuildResult r in buildResult.RightToSeq())
{
result = r;
}
}
if (TrimStart)
@ -504,7 +530,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -504,7 +530,7 @@ public class PlayoutBuilder : IPlayoutBuilder
return result;
}
private async Task<PlayoutBuildResult> BuildPlayoutItems(
private async Task<Either<BaseError, PlayoutBuildResult>> BuildPlayoutItems(
Playout playout,
PlayoutReferenceData referenceData,
PlayoutBuildResult result,

10
ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs

@ -18,7 +18,7 @@ public class ScriptedPlayoutBuilder( @@ -18,7 +18,7 @@ public class ScriptedPlayoutBuilder(
ILogger<ScriptedPlayoutBuilder> logger)
: IScriptedPlayoutBuilder
{
public async Task<PlayoutBuildResult> Build(
public async Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,
@ -43,7 +43,7 @@ public class ScriptedPlayoutBuilder( @@ -43,7 +43,7 @@ public class ScriptedPlayoutBuilder(
logger.LogError(
"Cannot build scripted playout; schedule file {File} does not exist",
scriptFile);
return result;
return BaseError.New($"Cannot build scripted playout; schedule file {scriptFile} does not exist");
}
var arguments = new List<string>
@ -102,6 +102,8 @@ public class ScriptedPlayoutBuilder( @@ -102,6 +102,8 @@ public class ScriptedPlayoutBuilder(
"Scripted playout process exited with code {Code}: {Error}",
commandResult.ExitCode,
commandResult.StandardError);
return BaseError.New(
$"Scripted playout process exited with code {commandResult.ExitCode}: {commandResult.StandardError}");
}
playout.Anchor = schedulingEngine.GetAnchor();
@ -111,12 +113,12 @@ public class ScriptedPlayoutBuilder( @@ -111,12 +113,12 @@ public class ScriptedPlayoutBuilder(
catch (OperationCanceledException)
{
logger.LogWarning("Scripted playout build timed out after {TimeoutSeconds} seconds", timeoutSeconds);
throw new TimeoutException($"Scripted playout build timed out after {timeoutSeconds} seconds");
return BaseError.New($"Scripted playout build timed out after {timeoutSeconds} seconds");
}
catch (Exception ex)
{
logger.LogWarning(ex, "Unexpected exception building scripted playout");
throw;
return BaseError.New($"Unexpected exception building scripted playout: {ex}");
}
finally
{

14
ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs

@ -26,7 +26,7 @@ public class SequentialPlayoutBuilder( @@ -26,7 +26,7 @@ public class SequentialPlayoutBuilder(
ILogger<SequentialPlayoutBuilder> logger)
: ISequentialPlayoutBuilder
{
public async Task<PlayoutBuildResult> Build(
public async Task<Either<BaseError, PlayoutBuildResult>> Build(
DateTimeOffset start,
Playout playout,
PlayoutReferenceData referenceData,
@ -41,7 +41,7 @@ public class SequentialPlayoutBuilder( @@ -41,7 +41,7 @@ public class SequentialPlayoutBuilder(
if (!localFileSystem.FileExists(playout.ScheduleFile))
{
logger.LogWarning("Sequential schedule file {File} does not exist; aborting.", playout.ScheduleFile);
throw new PlayoutBuildException($"Sequential schedule file {playout.ScheduleFile} does not exist");
return BaseError.New($"Sequential schedule file {playout.ScheduleFile} does not exist");
}
Option<YamlPlayoutDefinition> maybePlayoutDefinition =
@ -49,7 +49,7 @@ public class SequentialPlayoutBuilder( @@ -49,7 +49,7 @@ public class SequentialPlayoutBuilder(
if (maybePlayoutDefinition.IsNone)
{
logger.LogWarning("Sequential schedule file {File} is invalid; aborting.", playout.ScheduleFile);
throw new PlayoutBuildException($"Sequential schedule file {playout.ScheduleFile} is invalid");
return BaseError.New($"Sequential schedule file {playout.ScheduleFile} is invalid");
}
// using ValueUnsafe to avoid nesting
@ -96,13 +96,13 @@ public class SequentialPlayoutBuilder( @@ -96,13 +96,13 @@ public class SequentialPlayoutBuilder(
if (maybeImportedDefinition.IsNone)
{
logger.LogWarning("YAML playout import {File} is invalid; aborting.", import);
throw new PlayoutBuildException($"YAML playout import {import} is invalid");
return BaseError.New($"YAML playout import {import} is invalid");
}
}
catch (Exception ex)
{
logger.LogError(ex, "Unexpected exception loading YAML playout import");
throw new PlayoutBuildException("Unexpected exception loading YAML playout import", ex);
return BaseError.New($"Unexpected exception loading YAML playout import: {ex}");
}
}
@ -236,7 +236,7 @@ public class SequentialPlayoutBuilder( @@ -236,7 +236,7 @@ public class SequentialPlayoutBuilder(
if (DetectCycle(context.Definition))
{
logger.LogError("YAML sequence contains a cycle; unable to build playout");
throw new PlayoutBuildException("YAML sequence contains a cycle; unable to build playout");
return BaseError.New("YAML sequence contains a cycle; unable to build playout");
}
var flattenCount = 0;
@ -536,7 +536,7 @@ public class SequentialPlayoutBuilder( @@ -536,7 +536,7 @@ public class SequentialPlayoutBuilder(
catch (Exception ex)
{
logger.LogWarning(ex, "Error loading YAML playout definition");
throw;
return Option<YamlPlayoutDefinition>.None;
}
}

Loading…
Cancel
Save