diff --git a/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs index 097717038..c9905a494 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs @@ -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( // 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 buildResult = await playoutBuilder.Build( DateTimeOffset.Now, playout, referenceData, PlayoutBuildMode.Reset, cancellationToken); - var maxItems = 0; - Dictionary> 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> 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 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 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) => diff --git a/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs index aa968392f..64b86f062 100644 --- a/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs @@ -124,26 +124,31 @@ public class BuildPlayoutHandler : IRequestHandler 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>( + 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 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 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) { diff --git a/ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs b/ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs index a956b9c90..4f93633d1 100644 --- a/ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs @@ -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( playout.PlayoutHistory.ToList(), TimeSpan.Zero); - PlayoutBuildResult result = + Either buildResult = await blockPlayoutBuilder.Build( DateTimeOffset.Now, playout, @@ -76,40 +77,45 @@ public class PreviewBlockPlayoutHandler( PlayoutBuildMode.Reset, cancellationToken); - // load playout item details for title - foreach (PlayoutItem playoutItem in result.AddedItems) - { - Option 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 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) => diff --git a/ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutBuilderTests.cs b/ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutBuilderTests.cs index f552ed870..5ca98bc36 100644 --- a/ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutBuilderTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutBuilderTests.cs @@ -150,18 +150,22 @@ public class BlockPlayoutBuilderTests [], TimeSpan.Zero); - PlayoutBuildResult result = await builder.Build( + Either 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 [], TimeSpan.Zero); - PlayoutBuildResult result = await builder.Build( + Either 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 [], TimeSpan.Zero); - PlayoutBuildResult result = await builder.Build( + Either 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)); + } } } diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs index b432f5d47..3571032d7 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs @@ -28,7 +28,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start2 = HoursAfterMidnight(1); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start2 = HoursAfterMidnight(1); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(12); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromDays(1); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 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 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 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 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 DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start2 = HoursAfterMidnight(0); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start2 = start.AddHours(1); DateTimeOffset finish2 = start2 + TimeSpan.FromDays(2); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -350,7 +388,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start2 = HoursAfterMidnight(0); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start2 = start.AddHours(i); DateTimeOffset finish2 = start2 + TimeSpan.FromDays(2); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -555,7 +603,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(32); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(5); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 rerunHelper, Logger); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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(); diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs index db8e17bb9..17e14d416 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs @@ -26,10 +26,10 @@ public class NewPlayoutTests : PlayoutBuilderTestBase (PlayoutBuilder builder, Playout playout, PlayoutReferenceData referenceData) = TestDataFloodForItems(mediaItems, PlaybackOrder.Random); - PlayoutBuildResult result = + Either buildResult = await builder.Build(DateTimeOffset.Now, playout, referenceData, PlayoutBuildMode.Reset, CancellationToken); - result.AddedItems.ShouldBeEmpty(); + buildResult.IsLeft.ShouldBeTrue(); } [Test] @@ -46,7 +46,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -55,10 +55,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(1); - result.AddedItems.Head().MediaItemId.ShouldBe(2); - result.AddedItems.Head().StartOffset.ShouldBe(start); - result.AddedItems.Head().FinishOffset.ShouldBe(start + TimeSpan.FromHours(6)); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(1); + result.AddedItems.Head().MediaItemId.ShouldBe(2); + result.AddedItems.Head().StartOffset.ShouldBe(start); + result.AddedItems.Head().FinishOffset.ShouldBe(start + TimeSpan.FromHours(6)); + } playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(6)); } @@ -83,10 +87,10 @@ public class NewPlayoutTests : PlayoutBuilderTestBase (PlayoutBuilder builder, Playout playout, PlayoutReferenceData referenceData) = TestDataFloodForItems(mediaItems, PlaybackOrder.Random, configRepo); - PlayoutBuildResult result = + Either buildResult = await builder.Build(DateTimeOffset.Now, playout, referenceData, PlayoutBuildMode.Reset, CancellationToken); - result.AddedItems.ShouldBeEmpty(); + buildResult.IsLeft.ShouldBeTrue(); } [Test] @@ -112,7 +116,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -121,10 +125,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(1); - result.AddedItems.Head().MediaItemId.ShouldBe(2); - result.AddedItems.Head().StartOffset.ShouldBe(start); - result.AddedItems.Head().FinishOffset.ShouldBe(finish); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(1); + result.AddedItems.Head().MediaItemId.ShouldBe(2); + result.AddedItems.Head().StartOffset.ShouldBe(start); + result.AddedItems.Head().FinishOffset.ShouldBe(finish); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -149,10 +157,10 @@ public class NewPlayoutTests : PlayoutBuilderTestBase (PlayoutBuilder builder, Playout playout, PlayoutReferenceData referenceData) = TestDataFloodForItems(mediaItems, PlaybackOrder.Random, configRepo); - PlayoutBuildResult result = + Either buildResult = await builder.Build(DateTimeOffset.Now, playout, referenceData, PlayoutBuildMode.Reset, CancellationToken); - result.AddedItems.ShouldBeEmpty(); + buildResult.IsLeft.ShouldBeTrue(); } [Test] @@ -178,7 +186,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -187,10 +195,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(1); - result.AddedItems.Head().MediaItemId.ShouldBe(2); - result.AddedItems.Head().StartOffset.ShouldBe(start); - result.AddedItems.Head().FinishOffset.ShouldBe(finish); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(1); + result.AddedItems.Head().MediaItemId.ShouldBe(2); + result.AddedItems.Head().StartOffset.ShouldBe(start); + result.AddedItems.Head().FinishOffset.ShouldBe(finish); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -217,7 +229,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -226,9 +238,13 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(1); - result.AddedItems.Head().StartOffset.ShouldBe(start); - result.AddedItems.Head().FinishOffset.ShouldBe(finish); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(1); + result.AddedItems.Head().StartOffset.ShouldBe(start); + result.AddedItems.Head().FinishOffset.ShouldBe(finish); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -255,7 +271,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -264,9 +280,13 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(1); - result.AddedItems.Head().StartOffset.ShouldBe(start); - result.AddedItems.Head().FinishOffset.ShouldBe(finish); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(1); + result.AddedItems.Head().StartOffset.ShouldBe(start); + result.AddedItems.Head().FinishOffset.ShouldBe(finish); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -284,7 +304,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -293,9 +313,13 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(1); - result.AddedItems.Head().StartOffset.ShouldBe(start); - result.AddedItems.Head().FinishOffset.ShouldBe(finish); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(1); + result.AddedItems.Head().StartOffset.ShouldBe(start); + result.AddedItems.Head().FinishOffset.ShouldBe(finish); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -314,7 +338,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(1); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -323,10 +347,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(2); - result.AddedItems[0].StartOffset.ShouldBe(midnight); - result.AddedItems[1].StartOffset.ShouldBe(midnight + TimeSpan.FromHours(6)); - result.AddedItems[1].FinishOffset.ShouldBe(midnight + TimeSpan.FromHours(12)); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(2); + result.AddedItems[0].StartOffset.ShouldBe(midnight); + result.AddedItems[1].StartOffset.ShouldBe(midnight + TimeSpan.FromHours(6)); + result.AddedItems[1].FinishOffset.ShouldBe(midnight + TimeSpan.FromHours(12)); + } playout.Anchor.NextStartOffset.ShouldBe(midnight + TimeSpan.FromHours(12)); } @@ -345,7 +373,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(4); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -354,15 +382,19 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(4); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(1); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[3].MediaItemId.ShouldBe(2); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(4); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(1); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[3].MediaItemId.ShouldBe(2); + } playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(4)); } @@ -381,7 +413,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -390,8 +422,12 @@ public class NewPlayoutTests : 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); @@ -401,7 +437,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start2 = HoursAfterMidnight(1); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -410,9 +446,13 @@ public class NewPlayoutTests : 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); @@ -433,7 +473,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -442,8 +482,12 @@ public class NewPlayoutTests : 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); @@ -452,7 +496,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start2 = HoursAfterMidnight(1); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(12); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -461,11 +505,15 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish2, CancellationToken); - result2.AddedItems.Count.ShouldBe(2); - result2.AddedItems[0].StartOffset.ShouldBe(finish); - 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(finish); + 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); @@ -564,7 +612,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -573,17 +621,21 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(5); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(1); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[3].MediaItemId.ShouldBe(3); - 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); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(1); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[3].MediaItemId.ShouldBe(3); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); + result.AddedItems[4].MediaItemId.ShouldBe(2); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -680,7 +732,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(30); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -689,63 +741,67 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(28); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(1); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[3].MediaItemId.ShouldBe(3); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); - result.AddedItems[4].MediaItemId.ShouldBe(2); - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); - result.AddedItems[5].MediaItemId.ShouldBe(1); - result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(7)); - result.AddedItems[6].MediaItemId.ShouldBe(2); - result.AddedItems[7].StartOffset.ShouldBe(start + TimeSpan.FromHours(8)); - result.AddedItems[7].MediaItemId.ShouldBe(1); - result.AddedItems[8].StartOffset.ShouldBe(start + TimeSpan.FromHours(9)); - result.AddedItems[8].MediaItemId.ShouldBe(2); - result.AddedItems[9].StartOffset.ShouldBe(start + TimeSpan.FromHours(10)); - result.AddedItems[9].MediaItemId.ShouldBe(1); - result.AddedItems[10].StartOffset.ShouldBe(start + TimeSpan.FromHours(11)); - result.AddedItems[10].MediaItemId.ShouldBe(2); - result.AddedItems[11].StartOffset.ShouldBe(start + TimeSpan.FromHours(12)); - result.AddedItems[11].MediaItemId.ShouldBe(1); - result.AddedItems[12].StartOffset.ShouldBe(start + TimeSpan.FromHours(13)); - result.AddedItems[12].MediaItemId.ShouldBe(2); - result.AddedItems[13].StartOffset.ShouldBe(start + TimeSpan.FromHours(14)); - result.AddedItems[13].MediaItemId.ShouldBe(1); - result.AddedItems[14].StartOffset.ShouldBe(start + TimeSpan.FromHours(15)); - result.AddedItems[14].MediaItemId.ShouldBe(2); - result.AddedItems[15].StartOffset.ShouldBe(start + TimeSpan.FromHours(16)); - result.AddedItems[15].MediaItemId.ShouldBe(1); - result.AddedItems[16].StartOffset.ShouldBe(start + TimeSpan.FromHours(17)); - result.AddedItems[16].MediaItemId.ShouldBe(2); - result.AddedItems[17].StartOffset.ShouldBe(start + TimeSpan.FromHours(18)); - result.AddedItems[17].MediaItemId.ShouldBe(1); - result.AddedItems[18].StartOffset.ShouldBe(start + TimeSpan.FromHours(19)); - result.AddedItems[18].MediaItemId.ShouldBe(2); - result.AddedItems[19].StartOffset.ShouldBe(start + TimeSpan.FromHours(20)); - result.AddedItems[19].MediaItemId.ShouldBe(1); - result.AddedItems[20].StartOffset.ShouldBe(start + TimeSpan.FromHours(21)); - result.AddedItems[20].MediaItemId.ShouldBe(2); - result.AddedItems[21].StartOffset.ShouldBe(start + TimeSpan.FromHours(22)); - result.AddedItems[21].MediaItemId.ShouldBe(1); - result.AddedItems[22].StartOffset.ShouldBe(start + TimeSpan.FromHours(23)); - result.AddedItems[22].MediaItemId.ShouldBe(2); - result.AddedItems[23].StartOffset.ShouldBe(start + TimeSpan.FromHours(24)); - result.AddedItems[23].MediaItemId.ShouldBe(1); - result.AddedItems[24].StartOffset.ShouldBe(start + TimeSpan.FromHours(25)); - result.AddedItems[24].MediaItemId.ShouldBe(2); - result.AddedItems[25].StartOffset.ShouldBe(start + TimeSpan.FromHours(26)); - result.AddedItems[25].MediaItemId.ShouldBe(1); - result.AddedItems[26].StartOffset.ShouldBe(start + TimeSpan.FromHours(27)); - result.AddedItems[26].MediaItemId.ShouldBe(3); - result.AddedItems[27].StartOffset.ShouldBe(start + TimeSpan.FromHours(29)); - result.AddedItems[27].MediaItemId.ShouldBe(2); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(28); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(1); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[3].MediaItemId.ShouldBe(3); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); + result.AddedItems[4].MediaItemId.ShouldBe(2); + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); + result.AddedItems[5].MediaItemId.ShouldBe(1); + result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(7)); + result.AddedItems[6].MediaItemId.ShouldBe(2); + result.AddedItems[7].StartOffset.ShouldBe(start + TimeSpan.FromHours(8)); + result.AddedItems[7].MediaItemId.ShouldBe(1); + result.AddedItems[8].StartOffset.ShouldBe(start + TimeSpan.FromHours(9)); + result.AddedItems[8].MediaItemId.ShouldBe(2); + result.AddedItems[9].StartOffset.ShouldBe(start + TimeSpan.FromHours(10)); + result.AddedItems[9].MediaItemId.ShouldBe(1); + result.AddedItems[10].StartOffset.ShouldBe(start + TimeSpan.FromHours(11)); + result.AddedItems[10].MediaItemId.ShouldBe(2); + result.AddedItems[11].StartOffset.ShouldBe(start + TimeSpan.FromHours(12)); + result.AddedItems[11].MediaItemId.ShouldBe(1); + result.AddedItems[12].StartOffset.ShouldBe(start + TimeSpan.FromHours(13)); + result.AddedItems[12].MediaItemId.ShouldBe(2); + result.AddedItems[13].StartOffset.ShouldBe(start + TimeSpan.FromHours(14)); + result.AddedItems[13].MediaItemId.ShouldBe(1); + result.AddedItems[14].StartOffset.ShouldBe(start + TimeSpan.FromHours(15)); + result.AddedItems[14].MediaItemId.ShouldBe(2); + result.AddedItems[15].StartOffset.ShouldBe(start + TimeSpan.FromHours(16)); + result.AddedItems[15].MediaItemId.ShouldBe(1); + result.AddedItems[16].StartOffset.ShouldBe(start + TimeSpan.FromHours(17)); + result.AddedItems[16].MediaItemId.ShouldBe(2); + result.AddedItems[17].StartOffset.ShouldBe(start + TimeSpan.FromHours(18)); + result.AddedItems[17].MediaItemId.ShouldBe(1); + result.AddedItems[18].StartOffset.ShouldBe(start + TimeSpan.FromHours(19)); + result.AddedItems[18].MediaItemId.ShouldBe(2); + result.AddedItems[19].StartOffset.ShouldBe(start + TimeSpan.FromHours(20)); + result.AddedItems[19].MediaItemId.ShouldBe(1); + result.AddedItems[20].StartOffset.ShouldBe(start + TimeSpan.FromHours(21)); + result.AddedItems[20].MediaItemId.ShouldBe(2); + result.AddedItems[21].StartOffset.ShouldBe(start + TimeSpan.FromHours(22)); + result.AddedItems[21].MediaItemId.ShouldBe(1); + result.AddedItems[22].StartOffset.ShouldBe(start + TimeSpan.FromHours(23)); + result.AddedItems[22].MediaItemId.ShouldBe(2); + result.AddedItems[23].StartOffset.ShouldBe(start + TimeSpan.FromHours(24)); + result.AddedItems[23].MediaItemId.ShouldBe(1); + result.AddedItems[24].StartOffset.ShouldBe(start + TimeSpan.FromHours(25)); + result.AddedItems[24].MediaItemId.ShouldBe(2); + result.AddedItems[25].StartOffset.ShouldBe(start + TimeSpan.FromHours(26)); + result.AddedItems[25].MediaItemId.ShouldBe(1); + result.AddedItems[26].StartOffset.ShouldBe(start + TimeSpan.FromHours(27)); + result.AddedItems[26].MediaItemId.ShouldBe(3); + result.AddedItems[27].StartOffset.ShouldBe(start + TimeSpan.FromHours(29)); + result.AddedItems[27].MediaItemId.ShouldBe(2); + } playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(30)); } @@ -847,7 +903,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(7); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -856,22 +912,26 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(6); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(6); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(1); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(1); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[3].MediaItemId.ShouldBe(3); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); - result.AddedItems[4].MediaItemId.ShouldBe(4); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[3].MediaItemId.ShouldBe(3); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); + result.AddedItems[4].MediaItemId.ShouldBe(4); - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); - result.AddedItems[5].MediaItemId.ShouldBe(2); + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); + result.AddedItems[5].MediaItemId.ShouldBe(2); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -973,7 +1033,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(7); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -982,22 +1042,26 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(6); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(6); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(50)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(50 + 60)); - result.AddedItems[2].MediaItemId.ShouldBe(1); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(50)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(50 + 60)); + result.AddedItems[2].MediaItemId.ShouldBe(1); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[3].MediaItemId.ShouldBe(3); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); - result.AddedItems[4].MediaItemId.ShouldBe(4); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[3].MediaItemId.ShouldBe(3); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); + result.AddedItems[4].MediaItemId.ShouldBe(4); - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); - result.AddedItems[5].MediaItemId.ShouldBe(2); + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); + result.AddedItems[5].MediaItemId.ShouldBe(2); + } playout.Anchor.NextStartOffset.ShouldBe(finish); } @@ -1098,7 +1162,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(24); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1107,21 +1171,25 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(6); - - result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(7)); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(8)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(9)); - result.AddedItems[2].MediaItemId.ShouldBe(1); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(10)); - result.AddedItems[3].MediaItemId.ShouldBe(2); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(11)); - result.AddedItems[4].MediaItemId.ShouldBe(1); - - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(12)); - result.AddedItems[5].MediaItemId.ShouldBe(3); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(6); + + result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromHours(7)); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(8)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(9)); + result.AddedItems[2].MediaItemId.ShouldBe(1); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(10)); + result.AddedItems[3].MediaItemId.ShouldBe(2); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(11)); + result.AddedItems[4].MediaItemId.ShouldBe(1); + + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(12)); + result.AddedItems[5].MediaItemId.ShouldBe(3); + } playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(31)); } @@ -1224,7 +1292,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1233,25 +1301,29 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(7); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(7); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(3); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(3); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(2.75)); - result.AddedItems[3].MediaItemId.ShouldBe(1); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(3.75)); - result.AddedItems[4].MediaItemId.ShouldBe(2); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(2.75)); + result.AddedItems[3].MediaItemId.ShouldBe(1); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(3.75)); + result.AddedItems[4].MediaItemId.ShouldBe(2); - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(4.75)); - result.AddedItems[5].MediaItemId.ShouldBe(1); - result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(5.75)); - result.AddedItems[6].MediaItemId.ShouldBe(2); + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(4.75)); + result.AddedItems[5].MediaItemId.ShouldBe(1); + result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(5.75)); + result.AddedItems[6].MediaItemId.ShouldBe(2); + } playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(6.75)); } @@ -1355,7 +1427,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1364,23 +1436,27 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(6); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(6); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(3); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(3); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(2.75)); - result.AddedItems[3].MediaItemId.ShouldBe(1); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(3.75)); - result.AddedItems[4].MediaItemId.ShouldBe(2); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(2.75)); + result.AddedItems[3].MediaItemId.ShouldBe(1); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(3.75)); + result.AddedItems[4].MediaItemId.ShouldBe(2); - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(4.75)); - result.AddedItems[5].MediaItemId.ShouldBe(4); + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(4.75)); + result.AddedItems[5].MediaItemId.ShouldBe(4); + } playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(6.25)); } @@ -1486,7 +1562,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(5); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1495,19 +1571,23 @@ public class NewPlayoutTests : 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); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(3); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(3); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[3].MediaItemId.ShouldBe(4); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(4)); - result.AddedItems[4].MediaItemId.ShouldBe(5); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[3].MediaItemId.ShouldBe(4); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(4)); + result.AddedItems[4].MediaItemId.ShouldBe(5); + } playout.Anchor.ScheduleItemsEnumeratorState.Index.ShouldBe(0); playout.Anchor.MultipleRemaining.ShouldBeNull(); @@ -1626,7 +1706,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1635,35 +1715,39 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(12); - - result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(0)); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(55)); - result.AddedItems[1].MediaItemId.ShouldBe(1); - result.AddedItems[2].StartOffset.ShouldBe(start + new TimeSpan(1, 50, 0)); - result.AddedItems[2].MediaItemId.ShouldBe(1); - - result.AddedItems[3].StartOffset.ShouldBe(start + new TimeSpan(2, 45, 0)); - result.AddedItems[3].MediaItemId.ShouldBe(3); - result.AddedItems[4].StartOffset.ShouldBe(start + new TimeSpan(2, 50, 0)); - result.AddedItems[4].MediaItemId.ShouldBe(3); - result.AddedItems[5].StartOffset.ShouldBe(start + new TimeSpan(2, 55, 0)); - result.AddedItems[5].MediaItemId.ShouldBe(3); - - result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[6].MediaItemId.ShouldBe(2); - result.AddedItems[7].StartOffset.ShouldBe(start + new TimeSpan(3, 55, 0)); - result.AddedItems[7].MediaItemId.ShouldBe(2); - result.AddedItems[8].StartOffset.ShouldBe(start + new TimeSpan(4, 50, 0)); - result.AddedItems[8].MediaItemId.ShouldBe(2); - - result.AddedItems[9].StartOffset.ShouldBe(start + new TimeSpan(5, 45, 0)); - result.AddedItems[9].MediaItemId.ShouldBe(3); - result.AddedItems[10].StartOffset.ShouldBe(start + new TimeSpan(5, 50, 0)); - result.AddedItems[10].MediaItemId.ShouldBe(3); - result.AddedItems[11].StartOffset.ShouldBe(start + new TimeSpan(5, 55, 0)); - result.AddedItems[11].MediaItemId.ShouldBe(3); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(12); + + result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(0)); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(55)); + result.AddedItems[1].MediaItemId.ShouldBe(1); + result.AddedItems[2].StartOffset.ShouldBe(start + new TimeSpan(1, 50, 0)); + result.AddedItems[2].MediaItemId.ShouldBe(1); + + result.AddedItems[3].StartOffset.ShouldBe(start + new TimeSpan(2, 45, 0)); + result.AddedItems[3].MediaItemId.ShouldBe(3); + result.AddedItems[4].StartOffset.ShouldBe(start + new TimeSpan(2, 50, 0)); + result.AddedItems[4].MediaItemId.ShouldBe(3); + result.AddedItems[5].StartOffset.ShouldBe(start + new TimeSpan(2, 55, 0)); + result.AddedItems[5].MediaItemId.ShouldBe(3); + + result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[6].MediaItemId.ShouldBe(2); + result.AddedItems[7].StartOffset.ShouldBe(start + new TimeSpan(3, 55, 0)); + result.AddedItems[7].MediaItemId.ShouldBe(2); + result.AddedItems[8].StartOffset.ShouldBe(start + new TimeSpan(4, 50, 0)); + result.AddedItems[8].MediaItemId.ShouldBe(2); + + result.AddedItems[9].StartOffset.ShouldBe(start + new TimeSpan(5, 45, 0)); + result.AddedItems[9].MediaItemId.ShouldBe(3); + result.AddedItems[10].StartOffset.ShouldBe(start + new TimeSpan(5, 50, 0)); + result.AddedItems[10].MediaItemId.ShouldBe(3); + result.AddedItems[11].StartOffset.ShouldBe(start + new TimeSpan(5, 55, 0)); + result.AddedItems[11].MediaItemId.ShouldBe(3); + } playout.Anchor.ScheduleItemsEnumeratorState.Index.ShouldBe(0); playout.Anchor.DurationFinish.ShouldBeNull(); @@ -1759,7 +1843,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(1); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1768,12 +1852,16 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(2); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(2); - result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(0)); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(61)); - result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[0].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(0)); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromMinutes(61)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + } playout.Anchor.ScheduleItemsEnumeratorState.Index.ShouldBe(0); playout.Anchor.DurationFinish.ShouldBeNull(); @@ -1858,7 +1946,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1867,20 +1955,24 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(6); - - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].MediaItemId.ShouldBe(2); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); - result.AddedItems[1].MediaItemId.ShouldBe(4); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); - result.AddedItems[2].MediaItemId.ShouldBe(2); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); - result.AddedItems[3].MediaItemId.ShouldBe(4); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(4)); - result.AddedItems[4].MediaItemId.ShouldBe(2); - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); - result.AddedItems[5].MediaItemId.ShouldBe(4); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(6); + + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].MediaItemId.ShouldBe(2); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(1)); + result.AddedItems[1].MediaItemId.ShouldBe(4); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(2)); + result.AddedItems[2].MediaItemId.ShouldBe(2); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(3)); + result.AddedItems[3].MediaItemId.ShouldBe(4); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(4)); + result.AddedItems[4].MediaItemId.ShouldBe(2); + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(5)); + result.AddedItems[5].MediaItemId.ShouldBe(4); + } playout.Anchor.ScheduleItemsEnumeratorState.Index.ShouldBe(0); playout.Anchor.DurationFinish.ShouldBeNull(); @@ -1902,7 +1994,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromDays(2); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -1911,31 +2003,35 @@ public class NewPlayoutTests : PlayoutBuilderTestBase finish, CancellationToken); - result.AddedItems.Count.ShouldBe(8); - result.AddedItems[0].MediaItemId.ShouldBe(1); - result.AddedItems[0].StartOffset.ShouldBe(start); - result.AddedItems[0].FinishOffset.ShouldBe(start + TimeSpan.FromHours(6)); - result.AddedItems[1].MediaItemId.ShouldBe(2); - result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); - result.AddedItems[1].FinishOffset.ShouldBe(start + TimeSpan.FromHours(12)); - result.AddedItems[2].MediaItemId.ShouldBe(3); - result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(12)); - result.AddedItems[2].FinishOffset.ShouldBe(start + TimeSpan.FromHours(18)); - result.AddedItems[3].MediaItemId.ShouldBe(1); - result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(18)); - result.AddedItems[3].FinishOffset.ShouldBe(start + TimeSpan.FromHours(24)); - result.AddedItems[4].MediaItemId.ShouldBe(2); - result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(24)); - result.AddedItems[4].FinishOffset.ShouldBe(start + TimeSpan.FromHours(30)); - result.AddedItems[5].MediaItemId.ShouldBe(3); - result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(30)); - result.AddedItems[5].FinishOffset.ShouldBe(start + TimeSpan.FromHours(36)); - result.AddedItems[6].MediaItemId.ShouldBe(1); - result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(36)); - result.AddedItems[6].FinishOffset.ShouldBe(start + TimeSpan.FromHours(42)); - result.AddedItems[7].MediaItemId.ShouldBe(2); - result.AddedItems[7].StartOffset.ShouldBe(start + TimeSpan.FromHours(42)); - result.AddedItems[7].FinishOffset.ShouldBe(start + TimeSpan.FromHours(48)); + buildResult.IsRight.ShouldBeTrue(); + foreach (var result in buildResult.RightToSeq()) + { + result.AddedItems.Count.ShouldBe(8); + result.AddedItems[0].MediaItemId.ShouldBe(1); + result.AddedItems[0].StartOffset.ShouldBe(start); + result.AddedItems[0].FinishOffset.ShouldBe(start + TimeSpan.FromHours(6)); + result.AddedItems[1].MediaItemId.ShouldBe(2); + result.AddedItems[1].StartOffset.ShouldBe(start + TimeSpan.FromHours(6)); + result.AddedItems[1].FinishOffset.ShouldBe(start + TimeSpan.FromHours(12)); + result.AddedItems[2].MediaItemId.ShouldBe(3); + result.AddedItems[2].StartOffset.ShouldBe(start + TimeSpan.FromHours(12)); + result.AddedItems[2].FinishOffset.ShouldBe(start + TimeSpan.FromHours(18)); + result.AddedItems[3].MediaItemId.ShouldBe(1); + result.AddedItems[3].StartOffset.ShouldBe(start + TimeSpan.FromHours(18)); + result.AddedItems[3].FinishOffset.ShouldBe(start + TimeSpan.FromHours(24)); + result.AddedItems[4].MediaItemId.ShouldBe(2); + result.AddedItems[4].StartOffset.ShouldBe(start + TimeSpan.FromHours(24)); + result.AddedItems[4].FinishOffset.ShouldBe(start + TimeSpan.FromHours(30)); + result.AddedItems[5].MediaItemId.ShouldBe(3); + result.AddedItems[5].StartOffset.ShouldBe(start + TimeSpan.FromHours(30)); + result.AddedItems[5].FinishOffset.ShouldBe(start + TimeSpan.FromHours(36)); + result.AddedItems[6].MediaItemId.ShouldBe(1); + result.AddedItems[6].StartOffset.ShouldBe(start + TimeSpan.FromHours(36)); + result.AddedItems[6].FinishOffset.ShouldBe(start + TimeSpan.FromHours(42)); + result.AddedItems[7].MediaItemId.ShouldBe(2); + result.AddedItems[7].StartOffset.ShouldBe(start + TimeSpan.FromHours(42)); + result.AddedItems[7].FinishOffset.ShouldBe(start + TimeSpan.FromHours(48)); + } playout.ProgramScheduleAnchors.Count.ShouldBe(2); playout.ProgramScheduleAnchors.Count(a => a.EnumeratorState.Index == 4 % 3).ShouldBe(1); diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs index ac1af8571..0332a1e51 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs @@ -116,7 +116,7 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(24); DateTimeOffset finish = start + TimeSpan.FromDays(1); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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)); } diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs index 733441dbd..de792ea29 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs @@ -26,7 +26,7 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build( + Either buildResult = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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 DateTimeOffset start2 = HoursAfterMidnight(0); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6); - PlayoutBuildResult result2 = await builder.Build( + Either buildResult2 = await builder.Build( playout, referenceData, PlayoutBuildResult.Empty, @@ -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); diff --git a/ErsatzTV.Core/Interfaces/Scheduling/IBlockPlayoutBuilder.cs b/ErsatzTV.Core/Interfaces/Scheduling/IBlockPlayoutBuilder.cs index 6779f6a1e..c7b3fb8c7 100644 --- a/ErsatzTV.Core/Interfaces/Scheduling/IBlockPlayoutBuilder.cs +++ b/ErsatzTV.Core/Interfaces/Scheduling/IBlockPlayoutBuilder.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; public interface IBlockPlayoutBuilder { - Task Build( + Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, diff --git a/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs b/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs index 0426b5ff7..10cf4f663 100644 --- a/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs +++ b/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs @@ -8,7 +8,7 @@ public interface IPlayoutBuilder bool TrimStart { get; set; } Playlist DebugPlaylist { get; set; } - Task Build( + Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, diff --git a/ErsatzTV.Core/Interfaces/Scheduling/IScriptedPlayoutBuilder.cs b/ErsatzTV.Core/Interfaces/Scheduling/IScriptedPlayoutBuilder.cs index fdf284ae6..f99658632 100644 --- a/ErsatzTV.Core/Interfaces/Scheduling/IScriptedPlayoutBuilder.cs +++ b/ErsatzTV.Core/Interfaces/Scheduling/IScriptedPlayoutBuilder.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; public interface IScriptedPlayoutBuilder { - Task Build( + Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, diff --git a/ErsatzTV.Core/Interfaces/Scheduling/ISequentialPlayoutBuilder.cs b/ErsatzTV.Core/Interfaces/Scheduling/ISequentialPlayoutBuilder.cs index ff9e16a07..1d7c5645b 100644 --- a/ErsatzTV.Core/Interfaces/Scheduling/ISequentialPlayoutBuilder.cs +++ b/ErsatzTV.Core/Interfaces/Scheduling/ISequentialPlayoutBuilder.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; public interface ISequentialPlayoutBuilder { - Task Build( + Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, diff --git a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs index bbbc9eceb..3d1ae1d2c 100644 --- a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs @@ -27,7 +27,7 @@ public class BlockPlayoutBuilder( protected virtual ILogger Logger => logger; - public virtual async Task Build( + public virtual async Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, @@ -57,6 +57,11 @@ public class BlockPlayoutBuilder( List 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); diff --git a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutPreviewBuilder.cs b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutPreviewBuilder.cs index 58715cf24..faeb9df9f 100644 --- a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutPreviewBuilder.cs +++ b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutPreviewBuilder.cs @@ -26,7 +26,7 @@ public class BlockPlayoutPreviewBuilder( protected override ILogger Logger => NullLogger.Instance; - public override async Task Build( + public override async Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, @@ -35,11 +35,16 @@ public class BlockPlayoutPreviewBuilder( { _randomizedCollections.Add(playout.Channel.UniqueId, []); - PlayoutBuildResult result = await base.Build(start, playout, referenceData, mode, cancellationToken); + Either buildResult = await base.Build( + start, + playout, + referenceData, + mode, + cancellationToken); _randomizedCollections.Remove(playout.Channel.UniqueId); - return result; + return buildResult; } protected override Task GetDaysToBuild(CancellationToken cancellationToken) => Task.FromResult(1); diff --git a/ErsatzTV.Core/Scheduling/PlayoutBuildException.cs b/ErsatzTV.Core/Scheduling/PlayoutBuildException.cs deleted file mode 100644 index bd31511dc..000000000 --- a/ErsatzTV.Core/Scheduling/PlayoutBuildException.cs +++ /dev/null @@ -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) { } -} diff --git a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs index 66af191b4..ccf9bfb68 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs @@ -63,15 +63,13 @@ public class PlayoutBuilder : IPlayoutBuilder } } - public async Task Build( + public async Task> 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 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 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 buildResult = await Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + mode, + parameters, + cancellationToken); + + return buildResult.Match( + result => result with + { + RerunHistoryToRemove = _rerunHelper.GetHistoryToRemove(), + AddedRerunHistory = _rerunHelper.GetHistoryToAdd() + }, + Either.Left); + }, + Either.Left); } - private Task Build( + private Task> Build( Playout playout, PlayoutReferenceData referenceData, PlayoutBuildResult result, @@ -117,7 +124,7 @@ public class PlayoutBuilder : IPlayoutBuilder _ => ContinuePlayout(playout, referenceData, result, parameters, cancellationToken) }; - internal async Task Build( + internal async Task> Build( Playout playout, PlayoutReferenceData referenceData, PlayoutBuildResult result, @@ -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 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.Left); } - private async Task RefreshPlayout( + private async Task> RefreshPlayout( Playout playout, PlayoutReferenceData referenceData, PlayoutBuildResult result, @@ -266,7 +271,7 @@ public class PlayoutBuilder : IPlayoutBuilder cancellationToken); } - private async Task ResetPlayout( + private async Task> ResetPlayout( Playout playout, PlayoutReferenceData referenceData, PlayoutBuildResult result, @@ -310,7 +315,7 @@ public class PlayoutBuilder : IPlayoutBuilder return result; } - private async Task ContinuePlayout( + private async Task> ContinuePlayout( Playout playout, PlayoutReferenceData referenceData, PlayoutBuildResult result, @@ -340,9 +345,8 @@ public class PlayoutBuilder : IPlayoutBuilder cancellationToken); } - private async Task> Validate( + private async Task> Validate( DateTimeOffset start, - Playout playout, PlayoutReferenceData referenceData, CancellationToken cancellationToken) { @@ -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 skipMissingItems = @@ -365,24 +369,26 @@ public class PlayoutBuilder : IPlayoutBuilder { Option 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 daysToBuild = await _configElementRepository.GetValue( @@ -397,7 +403,7 @@ public class PlayoutBuilder : IPlayoutBuilder collectionMediaItems); } - private async Task BuildPlayoutItems( + private async Task> BuildPlayoutItems( Playout playout, PlayoutReferenceData referenceData, PlayoutBuildResult result, @@ -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 buildResult = await BuildPlayoutItems( playout, referenceData, result, @@ -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 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 buildResult = await BuildPlayoutItems( playout, referenceData, result, @@ -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 return result; } - private async Task BuildPlayoutItems( + private async Task> BuildPlayoutItems( Playout playout, PlayoutReferenceData referenceData, PlayoutBuildResult result, diff --git a/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs index 834a2abcb..7ff714f8f 100644 --- a/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs @@ -18,7 +18,7 @@ public class ScriptedPlayoutBuilder( ILogger logger) : IScriptedPlayoutBuilder { - public async Task Build( + public async Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, @@ -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 @@ -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( 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 { diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs index 65701d8a4..adc5c0a33 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs @@ -26,7 +26,7 @@ public class SequentialPlayoutBuilder( ILogger logger) : ISequentialPlayoutBuilder { - public async Task Build( + public async Task> Build( DateTimeOffset start, Playout playout, PlayoutReferenceData referenceData, @@ -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 maybePlayoutDefinition = @@ -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( 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( 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( catch (Exception ex) { logger.LogWarning(ex, "Error loading YAML playout definition"); - throw; + return Option.None; } }