fix editorconfig and run code cleanup (#2324)
* fix formatting rules * reformat ersatztv * reformat ersatztv.application * reformat ersatztv.core * refactor ersatztv.core.tests * reformat ersatztv.ffmpeg * reformat ersatztv.ffmpeg.tests * reformat ersatztv.infrastructure * cleanup infra mysql * cleanup infra sqlite * cleanup infra tests * cleanup ersatztv.scanner * cleanup ersatztv.scanner.tests * sln cleanup * update dependencies
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"jetbrains.resharper.globaltools": {
|
||||
"version": "2025.1.4",
|
||||
"version": "2025.2.0",
|
||||
"commands": [
|
||||
"jb"
|
||||
],
|
||||
|
||||
@@ -1,28 +1,11 @@
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
trim_trailing_whitespace=true
|
||||
insert_final_newline=false
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
|
||||
[*.json]
|
||||
ij_json_array_wrapping = normal
|
||||
ij_json_keep_blank_lines_in_code = 0
|
||||
ij_json_keep_indents_on_empty_lines = false
|
||||
ij_json_keep_line_breaks = true
|
||||
ij_json_keep_trailing_comma = false
|
||||
ij_json_object_wrapping = normal
|
||||
ij_json_property_alignment = do_not_align
|
||||
ij_json_space_after_colon = true
|
||||
ij_json_space_after_comma = true
|
||||
ij_json_space_before_colon = false
|
||||
ij_json_space_before_comma = false
|
||||
ij_json_spaces_within_braces = true
|
||||
ij_json_spaces_within_brackets = true
|
||||
ij_json_wrap_long_lines = false
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_new_line_before_members_in_object_initializers=false
|
||||
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||
@@ -58,6 +41,8 @@ resharper_braces_for_for=required
|
||||
resharper_braces_for_foreach=required
|
||||
resharper_braces_for_ifelse=required
|
||||
resharper_braces_for_while=required
|
||||
resharper_csharp_arguments_literal=positional
|
||||
resharper_csharp_arguments_named=positional
|
||||
resharper_csharp_insert_final_newline=true
|
||||
resharper_csharp_max_attribute_length_for_same_line=0
|
||||
resharper_csharp_place_accessorholder_attribute_on_same_line=never
|
||||
@@ -100,7 +85,22 @@ tab_width=4
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.json]
|
||||
ij_json_array_wrapping = normal
|
||||
ij_json_keep_blank_lines_in_code = 0
|
||||
ij_json_keep_indents_on_empty_lines = false
|
||||
ij_json_keep_line_breaks = true
|
||||
ij_json_keep_trailing_comma = false
|
||||
ij_json_object_wrapping = normal
|
||||
ij_json_property_alignment = do_not_align
|
||||
ij_json_space_after_colon = true
|
||||
ij_json_space_after_comma = true
|
||||
ij_json_space_before_colon = false
|
||||
ij_json_space_before_comma = false
|
||||
ij_json_spaces_within_braces = true
|
||||
ij_json_spaces_within_brackets = true
|
||||
ij_json_wrap_long_lines = false
|
||||
|
||||
[*.cs]
|
||||
# disable CA1848: Use the LoggerMessage delegates`
|
||||
dotnet_diagnostic.ca1848.severity = none
|
||||
dotnet_diagnostic.ca1848.severity = none
|
||||
|
||||
@@ -27,7 +27,7 @@ public class GetChannelPlaylistHandler : IRequestHandler<GetChannelPlaylist, Cha
|
||||
var result = new List<Channel>();
|
||||
foreach (Channel channel in channels)
|
||||
{
|
||||
if (channel.IsEnabled == false)
|
||||
if (!channel.IsEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Globalization;
|
||||
using System.Threading.Channels;
|
||||
using ErsatzTV.Application.Libraries;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Errors;
|
||||
@@ -5,8 +7,6 @@ using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Globalization;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace ErsatzTV.Application.Emby;
|
||||
|
||||
@@ -67,16 +67,12 @@ public class CallEmbyShowScannerHandler : CallLibraryScannerHandler<SynchronizeE
|
||||
|
||||
protected override Task<DateTimeOffset> GetLastScan(
|
||||
TvContext dbContext,
|
||||
SynchronizeEmbyShowById request)
|
||||
{
|
||||
return Task.FromResult(DateTimeOffset.MinValue);
|
||||
}
|
||||
SynchronizeEmbyShowById request) =>
|
||||
Task.FromResult(DateTimeOffset.MinValue);
|
||||
|
||||
protected override bool ScanIsRequired(
|
||||
DateTimeOffset lastScan,
|
||||
int libraryRefreshInterval,
|
||||
SynchronizeEmbyShowById request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SynchronizeEmbyShowById request) =>
|
||||
true;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class
|
||||
UpdateEmbyLibraryPreferences request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var toDisable = request.Preferences.Filter(p => p.ShouldSyncItems == false).Map(p => p.Id).ToList();
|
||||
var toDisable = request.Preferences.Filter(p => !p.ShouldSyncItems).Map(p => p.Id).ToList();
|
||||
List<int> ids = await _mediaSourceRepository.DisableEmbyLibrarySync(toDisable);
|
||||
await _searchIndex.RemoveItems(ids);
|
||||
_searchIndex.Commit();
|
||||
|
||||
@@ -30,4 +30,4 @@
|
||||
<ProjectReference Include="..\ErsatzTV.Infrastructure\ErsatzTV.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -97,7 +97,7 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler<UpdateFFmpegSettings,
|
||||
request.Settings.UseEmbeddedSubtitles);
|
||||
|
||||
// do not extract when subtitles are not used
|
||||
if (request.Settings.UseEmbeddedSubtitles == false)
|
||||
if (!request.Settings.UseEmbeddedSubtitles)
|
||||
{
|
||||
request.Settings.ExtractEmbeddedSubtitles = false;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ public class CreateFillerPresetHandler : IRequestHandler<CreateFillerPreset, Eit
|
||||
SmartCollectionId = request.SmartCollectionId,
|
||||
PlaylistId = request.PlaylistId,
|
||||
Expression = request.FillerKind is FillerKind.MidRoll ? request.Expression : null,
|
||||
UseChaptersAsMediaItems = request.FillerKind is not FillerKind.Fallback && request.UseChaptersAsMediaItems
|
||||
UseChaptersAsMediaItems =
|
||||
request.FillerKind is not FillerKind.Fallback && request.UseChaptersAsMediaItems
|
||||
};
|
||||
|
||||
await dbContext.FillerPresets.AddAsync(fillerPreset, cancellationToken);
|
||||
|
||||
@@ -39,7 +39,8 @@ public class UpdateFillerPresetHandler : IRequestHandler<UpdateFillerPreset, Eit
|
||||
existing.SmartCollectionId = request.SmartCollectionId;
|
||||
existing.PlaylistId = request.PlaylistId;
|
||||
existing.Expression = request.FillerKind is FillerKind.MidRoll ? request.Expression : null;
|
||||
existing.UseChaptersAsMediaItems = request.FillerKind is not FillerKind.Fallback && request.UseChaptersAsMediaItems;
|
||||
existing.UseChaptersAsMediaItems =
|
||||
request.FillerKind is not FillerKind.Fallback && request.UseChaptersAsMediaItems;
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ public class RefreshGraphicsElementsHandler(
|
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
// cleanup existing elements
|
||||
var allExisting = await dbContext.GraphicsElements
|
||||
List<GraphicsElement> allExisting = await dbContext.GraphicsElements
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var existing in allExisting.Where(e => !localFileSystem.FileExists(e.Path)))
|
||||
foreach (GraphicsElement existing in allExisting.Where(e => !localFileSystem.FileExists(e.Path)))
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Removing graphics element that references non-existing file {File}",
|
||||
@@ -35,7 +35,7 @@ public class RefreshGraphicsElementsHandler(
|
||||
.Where(f => allExisting.All(e => e.Path != f))
|
||||
.ToList();
|
||||
|
||||
foreach (var path in newTextPaths)
|
||||
foreach (string path in newTextPaths)
|
||||
{
|
||||
logger.LogDebug("Adding new graphics element from file {File}", path);
|
||||
|
||||
@@ -53,7 +53,7 @@ public class RefreshGraphicsElementsHandler(
|
||||
.Where(f => allExisting.All(e => e.Path != f))
|
||||
.ToList();
|
||||
|
||||
foreach (var path in newImagePaths)
|
||||
foreach (string path in newImagePaths)
|
||||
{
|
||||
logger.LogDebug("Adding new graphics element from file {File}", path);
|
||||
|
||||
@@ -71,7 +71,7 @@ public class RefreshGraphicsElementsHandler(
|
||||
.Where(f => allExisting.All(e => e.Path != f))
|
||||
.ToList();
|
||||
|
||||
foreach (var path in newSubtitlePaths)
|
||||
foreach (string path in newSubtitlePaths)
|
||||
{
|
||||
logger.LogDebug("Adding new graphics element from file {File}", path);
|
||||
|
||||
@@ -86,4 +86,4 @@ public class RefreshGraphicsElementsHandler(
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
namespace ErsatzTV.Application.Graphics;
|
||||
|
||||
public record GraphicsElementViewModel(int Id, string Name);
|
||||
public record GraphicsElementViewModel(int Id, string Name);
|
||||
|
||||
@@ -6,13 +6,13 @@ public static class Mapper
|
||||
{
|
||||
public static GraphicsElementViewModel ProjectToViewModel(GraphicsElement graphicsElement)
|
||||
{
|
||||
var fileName = Path.GetFileName(graphicsElement.Path);
|
||||
string fileName = Path.GetFileName(graphicsElement.Path);
|
||||
return graphicsElement.Kind switch
|
||||
{
|
||||
GraphicsElementKind.Text => new GraphicsElementViewModel(graphicsElement.Id, $"text/{fileName}"),
|
||||
GraphicsElementKind.Image => new GraphicsElementViewModel(graphicsElement.Id, $"image/{fileName}"),
|
||||
GraphicsElementKind.Subtitle => new GraphicsElementViewModel(graphicsElement.Id, $"subtitle/{fileName}"),
|
||||
GraphicsElementKind.Subtitle => new GraphicsElementViewModel(graphicsElement.Id, $"subtitle/{fileName}"),
|
||||
_ => new GraphicsElementViewModel(graphicsElement.Id, graphicsElement.Path)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
namespace ErsatzTV.Application.Graphics;
|
||||
|
||||
public record GetAllGraphicsElements : IRequest<List<GraphicsElementViewModel>>;
|
||||
public record GetAllGraphicsElements : IRequest<List<GraphicsElementViewModel>>;
|
||||
|
||||
@@ -16,4 +16,4 @@ public class GetAllGraphicsElementsHandler(IDbContextFactory<TvContext> dbContex
|
||||
.ToListAsync(cancellationToken)
|
||||
.Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Globalization;
|
||||
using System.Threading.Channels;
|
||||
using ErsatzTV.Application.Libraries;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Errors;
|
||||
@@ -5,8 +7,6 @@ using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Globalization;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace ErsatzTV.Application.Jellyfin;
|
||||
|
||||
@@ -67,16 +67,12 @@ public class CallJellyfinShowScannerHandler : CallLibraryScannerHandler<Synchron
|
||||
|
||||
protected override Task<DateTimeOffset> GetLastScan(
|
||||
TvContext dbContext,
|
||||
SynchronizeJellyfinShowById request)
|
||||
{
|
||||
return Task.FromResult(DateTimeOffset.MinValue);
|
||||
}
|
||||
SynchronizeJellyfinShowById request) =>
|
||||
Task.FromResult(DateTimeOffset.MinValue);
|
||||
|
||||
protected override bool ScanIsRequired(
|
||||
DateTimeOffset lastScan,
|
||||
int libraryRefreshInterval,
|
||||
SynchronizeJellyfinShowById request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SynchronizeJellyfinShowById request) =>
|
||||
true;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class
|
||||
UpdateJellyfinLibraryPreferences request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var toDisable = request.Preferences.Filter(p => p.ShouldSyncItems == false).Map(p => p.Id).ToList();
|
||||
var toDisable = request.Preferences.Filter(p => !p.ShouldSyncItems).Map(p => p.Id).ToList();
|
||||
List<int> ids = await _mediaSourceRepository.DisableJellyfinLibrarySync(toDisable);
|
||||
await _searchIndex.RemoveItems(ids);
|
||||
_searchIndex.Commit();
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
public interface ILocalLibraryRequest
|
||||
{
|
||||
public string Name { get; }
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
namespace ErsatzTV.Application.Libraries;
|
||||
|
||||
public record QueueShowScanByLibraryId(int LibraryId, int ShowId, string ShowTitle, bool DeepScan) : IRequest<bool>;
|
||||
public record QueueShowScanByLibraryId(int LibraryId, int ShowId, string ShowTitle, bool DeepScan) : IRequest<bool>;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ErsatzTV.Application.Emby;
|
||||
using ErsatzTV.Application.Jellyfin;
|
||||
using ErsatzTV.Application.Plex;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Locking;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
@@ -48,25 +49,28 @@ public class QueueShowScanByLibraryIdHandler(
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.LogDebug("Queued show scan for library id {Id}, show: {ShowTitle}, deepScan: {DeepScan}",
|
||||
library.Id, request.ShowTitle, request.DeepScan);
|
||||
logger.LogDebug(
|
||||
"Queued show scan for library id {Id}, show: {ShowTitle}, deepScan: {DeepScan}",
|
||||
library.Id,
|
||||
request.ShowTitle,
|
||||
request.DeepScan);
|
||||
|
||||
try
|
||||
{
|
||||
switch (library)
|
||||
{
|
||||
case PlexLibrary:
|
||||
var plexResult = await mediator.Send(
|
||||
Either<BaseError, string> plexResult = await mediator.Send(
|
||||
new SynchronizePlexShowById(library.Id, request.ShowId, request.DeepScan),
|
||||
cancellationToken);
|
||||
return plexResult.IsRight;
|
||||
case JellyfinLibrary:
|
||||
var jellyfinResult = await mediator.Send(
|
||||
Either<BaseError, string> jellyfinResult = await mediator.Send(
|
||||
new SynchronizeJellyfinShowById(library.Id, request.ShowId, request.DeepScan),
|
||||
cancellationToken);
|
||||
return jellyfinResult.IsRight;
|
||||
case EmbyLibrary:
|
||||
var embyResult = await mediator.Send(
|
||||
Either<BaseError, string> embyResult = await mediator.Send(
|
||||
new SynchronizeEmbyShowById(library.Id, request.ShowId, request.DeepScan),
|
||||
cancellationToken);
|
||||
return embyResult.IsRight;
|
||||
@@ -87,4 +91,4 @@ public class QueueShowScanByLibraryIdHandler(
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class UpdateLocalLibraryHandler : LocalLibraryHandlerBase,
|
||||
|
||||
var toRemoveIds = toRemove.Map(lp => lp.Id).ToHashSet();
|
||||
|
||||
int changeCount = 0;
|
||||
var changeCount = 0;
|
||||
|
||||
// save item ids first; will need to remove from search index
|
||||
List<int> itemsToRemove = await dbContext.MediaItems
|
||||
|
||||
@@ -55,7 +55,11 @@ public class PreviewPlaylistPlayoutHandler(
|
||||
// TODO: make an explicit method to preview, this is ugly
|
||||
playoutBuilder.TrimStart = false;
|
||||
playoutBuilder.DebugPlaylist = playout.ProgramSchedule.Items[0].Playlist;
|
||||
var result = await playoutBuilder.Build(playout, referenceData, PlayoutBuildMode.Reset, cancellationToken);
|
||||
PlayoutBuildResult result = await playoutBuilder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildMode.Reset,
|
||||
cancellationToken);
|
||||
|
||||
var maxItems = 0;
|
||||
Dictionary<PlaylistItem, List<MediaItem>> map =
|
||||
|
||||
@@ -57,7 +57,7 @@ public class GetPlaylistItemsHandler(IDbContextFactory<TvContext> dbContextFacto
|
||||
.ThenInclude(mm => mm.Artwork)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (allItems.All(bi => bi.IncludeInProgramGuide == false))
|
||||
if (allItems.All(bi => !bi.IncludeInProgramGuide))
|
||||
{
|
||||
foreach (PlaylistItem bi in allItems)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using ErsatzTV.Core;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems;
|
||||
|
||||
public record GetRemoteStreamById(int RemoteStreamId) : IRequest<Option<RemoteStreamViewModel>>;
|
||||
|
||||
@@ -13,6 +13,7 @@ using ErsatzTV.Core.Scheduling;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using ErsatzTV.Infrastructure.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Channel = ErsatzTV.Core.Domain.Channel;
|
||||
|
||||
namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
@@ -23,10 +24,10 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
private readonly IClient _client;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IEntityLocker _entityLocker;
|
||||
private readonly IPlayoutTimeShifter _playoutTimeShifter;
|
||||
private readonly IExternalJsonPlayoutBuilder _externalJsonPlayoutBuilder;
|
||||
private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
|
||||
private readonly IPlayoutBuilder _playoutBuilder;
|
||||
private readonly IPlayoutTimeShifter _playoutTimeShifter;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
private readonly IYamlPlayoutBuilder _yamlPlayoutBuilder;
|
||||
|
||||
@@ -78,9 +79,9 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
}
|
||||
|
||||
// after dbcontext is closed
|
||||
foreach (var playoutBuildResult in result.RightToSeq())
|
||||
foreach (PlayoutBuildResult playoutBuildResult in result.RightToSeq())
|
||||
{
|
||||
foreach (var timeShiftTo in playoutBuildResult.TimeShiftTo)
|
||||
foreach (DateTimeOffset timeShiftTo in playoutBuildResult.TimeShiftTo)
|
||||
{
|
||||
await _playoutTimeShifter.TimeShift(request.PlayoutId, timeShiftTo, false);
|
||||
}
|
||||
@@ -100,20 +101,28 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
Playout playout,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
string channelName = "[unknown]";
|
||||
var channelName = "[unknown]";
|
||||
|
||||
try
|
||||
{
|
||||
var referenceData = await GetReferenceData(dbContext, playout.Id, playout.ProgramSchedulePlayoutType);
|
||||
var channelNumber = referenceData.Channel.Number;
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
dbContext,
|
||||
playout.Id,
|
||||
playout.ProgramSchedulePlayoutType);
|
||||
string channelNumber = referenceData.Channel.Number;
|
||||
channelName = referenceData.Channel.Name;
|
||||
var result = PlayoutBuildResult.Empty;
|
||||
PlayoutBuildResult result = PlayoutBuildResult.Empty;
|
||||
|
||||
switch (playout.ProgramSchedulePlayoutType)
|
||||
{
|
||||
case ProgramSchedulePlayoutType.Block:
|
||||
result = await _blockPlayoutBuilder.Build(playout, referenceData, request.Mode, cancellationToken);
|
||||
result = await _blockPlayoutFillerBuilder.Build(playout, referenceData, result, request.Mode, cancellationToken);
|
||||
result = await _blockPlayoutFillerBuilder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
result,
|
||||
request.Mode,
|
||||
cancellationToken);
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.Yaml:
|
||||
result = await _yamlPlayoutBuilder.Build(playout, referenceData, request.Mode, cancellationToken);
|
||||
@@ -128,7 +137,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
break;
|
||||
}
|
||||
|
||||
int changeCount = 0;
|
||||
var changeCount = 0;
|
||||
|
||||
if (result.ClearItems)
|
||||
{
|
||||
@@ -137,7 +146,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
}
|
||||
|
||||
foreach (var removeBefore in result.RemoveBefore)
|
||||
foreach (DateTimeOffset removeBefore in result.RemoveBefore)
|
||||
{
|
||||
changeCount += await dbContext.PlayoutItems
|
||||
.Where(pi => pi.PlayoutId == playout.Id)
|
||||
@@ -145,7 +154,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
}
|
||||
|
||||
foreach (var removeAfter in result.RemoveAfter)
|
||||
foreach (DateTimeOffset removeAfter in result.RemoveAfter)
|
||||
{
|
||||
changeCount += await dbContext.PlayoutItems
|
||||
.Where(pi => pi.PlayoutId == playout.Id)
|
||||
@@ -163,8 +172,10 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
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);
|
||||
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
|
||||
@@ -254,11 +265,11 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
TvContext dbContext,
|
||||
BuildPlayout buildPlayout)
|
||||
{
|
||||
var maybePlayout = await dbContext.Playouts
|
||||
Option<Playout> maybePlayout = await dbContext.Playouts
|
||||
.Include(p => p.Anchor)
|
||||
.SelectOneAsync(p => p.Id, p => p.Id == buildPlayout.PlayoutId);
|
||||
|
||||
foreach (var playout in maybePlayout)
|
||||
foreach (Playout playout in maybePlayout)
|
||||
{
|
||||
switch (playout.ProgramSchedulePlayoutType)
|
||||
{
|
||||
@@ -267,7 +278,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.Collection(p => p.FillGroupIndices)
|
||||
.LoadAsync();
|
||||
|
||||
foreach (var fillGroupIndex in playout.FillGroupIndices)
|
||||
foreach (PlayoutScheduleItemFillGroupIndex fillGroupIndex in playout.FillGroupIndices)
|
||||
{
|
||||
await dbContext.Entry(fillGroupIndex)
|
||||
.Reference(fgi => fgi.EnumeratorState)
|
||||
@@ -278,7 +289,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.Collection(p => p.ProgramScheduleAnchors)
|
||||
.LoadAsync();
|
||||
|
||||
foreach (var anchor in playout.ProgramScheduleAnchors)
|
||||
foreach (PlayoutProgramScheduleAnchor anchor in playout.ProgramScheduleAnchors)
|
||||
{
|
||||
await dbContext.Entry(anchor)
|
||||
.Reference(a => a.EnumeratorState)
|
||||
@@ -297,12 +308,12 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
int playoutId,
|
||||
ProgramSchedulePlayoutType playoutType)
|
||||
{
|
||||
var channel = await dbContext.Channels
|
||||
Channel channel = await dbContext.Channels
|
||||
.AsNoTracking()
|
||||
.Where(c => c.Playouts.Any(p => p.Id == playoutId))
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var deco = Option<Deco>.None;
|
||||
Option<Deco> deco = Option<Deco>.None;
|
||||
List<PlayoutItem> existingItems = [];
|
||||
List<PlayoutTemplate> playoutTemplates = [];
|
||||
|
||||
@@ -332,7 +343,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
var programSchedule = await dbContext.ProgramSchedules
|
||||
ProgramSchedule programSchedule = await dbContext.ProgramSchedules
|
||||
.AsNoTracking()
|
||||
.Where(ps => ps.Playouts.Any(p => p.Id == playoutId))
|
||||
.Include(ps => ps.Items)
|
||||
@@ -354,7 +365,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var programScheduleAlternates = await dbContext.ProgramScheduleAlternates
|
||||
List<ProgramScheduleAlternate> programScheduleAlternates = await dbContext.ProgramScheduleAlternates
|
||||
.AsNoTracking()
|
||||
.Where(pt => pt.PlayoutId == playoutId)
|
||||
.Include(a => a.ProgramSchedule)
|
||||
@@ -384,7 +395,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
.ToListAsync();
|
||||
|
||||
var playoutHistory = await dbContext.PlayoutHistory
|
||||
List<PlayoutHistory> playoutHistory = await dbContext.PlayoutHistory
|
||||
.AsNoTracking()
|
||||
.Where(h => h.PlayoutId == playoutId)
|
||||
.ToListAsync();
|
||||
|
||||
@@ -33,6 +33,7 @@ public class CreateBlockPlayoutHandler(
|
||||
{
|
||||
await channel.WriteAsync(new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false));
|
||||
}
|
||||
|
||||
await channel.WriteAsync(new RefreshChannelList());
|
||||
return new CreatePlayoutResponse(playout.Id);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ public class CreateYamlPlayoutHandler
|
||||
{
|
||||
await _channel.WriteAsync(new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false));
|
||||
}
|
||||
|
||||
await _channel.WriteAsync(new RefreshChannelList());
|
||||
return new CreatePlayoutResponse(playout.Id);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,8 @@ internal static class Mapper
|
||||
case Image i:
|
||||
return i.ImageMetadata.HeadOrNone().Map(im => im.Title ?? string.Empty).IfNone("[unknown image]");
|
||||
case RemoteStream rs:
|
||||
return rs.RemoteStreamMetadata.HeadOrNone().Map(im => im.Title ?? string.Empty).IfNone("[unknown remote stream]");
|
||||
return rs.RemoteStreamMetadata.HeadOrNone().Map(im => im.Title ?? string.Empty)
|
||||
.IfNone("[unknown remote stream]");
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -18,22 +19,21 @@ public class CheckForOverlappingPlayoutItemsHandler(
|
||||
.AnyAsync(
|
||||
a => dbContext.PlayoutItems
|
||||
.Where(b => b.PlayoutId == a.PlayoutId)
|
||||
.Any(
|
||||
b =>
|
||||
a.Id < b.Id &&
|
||||
a.Start < b.Finish &&
|
||||
a.Finish > b.Start),
|
||||
.Any(b =>
|
||||
a.Id < b.Id &&
|
||||
a.Start < b.Finish &&
|
||||
a.Finish > b.Start),
|
||||
cancellationToken);
|
||||
|
||||
if (hasConflict)
|
||||
{
|
||||
var maybeChannel = await dbContext.Channels
|
||||
Option<Channel> maybeChannel = await dbContext.Channels
|
||||
.AsNoTracking()
|
||||
.Where(c => c.Playouts.Any(p => p.Id == request.PlayoutId))
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.Map(Optional);
|
||||
|
||||
foreach (var channel in maybeChannel)
|
||||
foreach (Channel channel in maybeChannel)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Playout for channel {ChannelName} has overlapping playout items; this may be a bug.",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Globalization;
|
||||
using System.Threading.Channels;
|
||||
using ErsatzTV.Application.Libraries;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Errors;
|
||||
@@ -5,8 +7,6 @@ using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Globalization;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace ErsatzTV.Application.Plex;
|
||||
|
||||
@@ -67,16 +67,12 @@ public class CallPlexShowScannerHandler : CallLibraryScannerHandler<SynchronizeP
|
||||
|
||||
protected override Task<DateTimeOffset> GetLastScan(
|
||||
TvContext dbContext,
|
||||
SynchronizePlexShowById request)
|
||||
{
|
||||
return Task.FromResult(DateTimeOffset.MinValue);
|
||||
}
|
||||
SynchronizePlexShowById request) =>
|
||||
Task.FromResult(DateTimeOffset.MinValue);
|
||||
|
||||
protected override bool ScanIsRequired(
|
||||
DateTimeOffset lastScan,
|
||||
int libraryRefreshInterval,
|
||||
SynchronizePlexShowById request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SynchronizePlexShowById request) =>
|
||||
true;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class
|
||||
UpdatePlexLibraryPreferences request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var toDisable = request.Preferences.Filter(p => p.ShouldSyncItems == false).Map(p => p.Id).ToList();
|
||||
var toDisable = request.Preferences.Filter(p => !p.ShouldSyncItems).Map(p => p.Id).ToList();
|
||||
List<int> ids = await _mediaSourceRepository.DisablePlexLibrarySync(toDisable);
|
||||
await _searchIndex.RemoveItems(ids);
|
||||
_searchIndex.Commit();
|
||||
|
||||
@@ -302,7 +302,7 @@ public abstract class ProgramScheduleItemCommandBase
|
||||
_ => throw new NotSupportedException($"Unsupported playout mode {item.PlayoutMode}")
|
||||
};
|
||||
|
||||
foreach (var watermarkId in item.WatermarkIds)
|
||||
foreach (int watermarkId in item.WatermarkIds)
|
||||
{
|
||||
result.ProgramScheduleItemWatermarks ??= [];
|
||||
result.ProgramScheduleItemWatermarks.Add(
|
||||
|
||||
@@ -66,7 +66,8 @@ internal static class Mapper
|
||||
duration.FallbackFiller != null
|
||||
? Filler.Mapper.ProjectToViewModel(duration.FallbackFiller)
|
||||
: null,
|
||||
duration.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(),
|
||||
duration.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
|
||||
.ToList(),
|
||||
duration.PreferredAudioLanguageCode,
|
||||
duration.PreferredAudioTitle,
|
||||
duration.PreferredSubtitleLanguageCode,
|
||||
@@ -117,7 +118,8 @@ internal static class Mapper
|
||||
flood.FallbackFiller != null
|
||||
? Filler.Mapper.ProjectToViewModel(flood.FallbackFiller)
|
||||
: null,
|
||||
flood.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(),
|
||||
flood.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
|
||||
.ToList(),
|
||||
flood.PreferredAudioLanguageCode,
|
||||
flood.PreferredAudioTitle,
|
||||
flood.PreferredSubtitleLanguageCode,
|
||||
@@ -170,7 +172,8 @@ internal static class Mapper
|
||||
multiple.FallbackFiller != null
|
||||
? Filler.Mapper.ProjectToViewModel(multiple.FallbackFiller)
|
||||
: null,
|
||||
multiple.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(),
|
||||
multiple.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
|
||||
.ToList(),
|
||||
multiple.PreferredAudioLanguageCode,
|
||||
multiple.PreferredAudioTitle,
|
||||
multiple.PreferredSubtitleLanguageCode,
|
||||
@@ -221,7 +224,8 @@ internal static class Mapper
|
||||
one.FallbackFiller != null
|
||||
? Filler.Mapper.ProjectToViewModel(one.FallbackFiller)
|
||||
: null,
|
||||
one.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(),
|
||||
one.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
|
||||
.ToList(),
|
||||
one.PreferredAudioLanguageCode,
|
||||
one.PreferredAudioTitle,
|
||||
one.PreferredSubtitleLanguageCode,
|
||||
|
||||
@@ -2,4 +2,10 @@ using ErsatzTV.Core.Domain.Scheduling;
|
||||
|
||||
namespace ErsatzTV.Application.Scheduling;
|
||||
|
||||
public record BlockViewModel(int Id, int GroupId, string GroupName, string Name, int Minutes, BlockStopScheduling StopScheduling);
|
||||
public record BlockViewModel(
|
||||
int Id,
|
||||
int GroupId,
|
||||
string GroupName,
|
||||
string Name,
|
||||
int Minutes,
|
||||
BlockStopScheduling StopScheduling);
|
||||
|
||||
@@ -43,4 +43,4 @@ public class CreateDecoGroupHandler(IDbContextFactory<TvContext> dbContextFactor
|
||||
|
||||
return (result1, result2).Apply((_, _) => createDecoGroup.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ public class CreateDecoTemplateGroupHandler(IDbContextFactory<TvContext> dbConte
|
||||
return Mapper.ProjectToViewModel(decoDecoTemplateGroup);
|
||||
}
|
||||
|
||||
private static Task<Validation<BaseError, DecoTemplateGroup>> Validate(TvContext dbContext, CreateDecoTemplateGroup request) =>
|
||||
private static Task<Validation<BaseError, DecoTemplateGroup>> Validate(
|
||||
TvContext dbContext,
|
||||
CreateDecoTemplateGroup request) =>
|
||||
ValidateName(dbContext, request).MapT(name => new DecoTemplateGroup { Name = name, DecoTemplates = [] });
|
||||
|
||||
private static async Task<Validation<BaseError, string>> ValidateName(
|
||||
|
||||
@@ -26,7 +26,9 @@ public class CreateTemplateGroupHandler(IDbContextFactory<TvContext> dbContextFa
|
||||
return Mapper.ProjectToViewModel(templateGroup);
|
||||
}
|
||||
|
||||
private static Task<Validation<BaseError, TemplateGroup>> Validate(TvContext dbContext, CreateTemplateGroup request) =>
|
||||
private static Task<Validation<BaseError, TemplateGroup>> Validate(
|
||||
TvContext dbContext,
|
||||
CreateTemplateGroup request) =>
|
||||
ValidateName(dbContext, request).MapT(name => new TemplateGroup { Name = name, Templates = [] });
|
||||
|
||||
private static async Task<Validation<BaseError, string>> ValidateName(
|
||||
|
||||
@@ -32,10 +32,12 @@ public class UpdateDecoHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
if (request.WatermarkMode is DecoMode.Override)
|
||||
{
|
||||
// this is different than schedule item/playout item because we have to merge watermark ids
|
||||
var toAdd = request.WatermarkIds.Where(id => existing.DecoWatermarks.All(wm => wm.WatermarkId != id));
|
||||
var toRemove = existing.DecoWatermarks.Where(wm => !request.WatermarkIds.Contains(wm.WatermarkId));
|
||||
IEnumerable<int> toAdd =
|
||||
request.WatermarkIds.Where(id => existing.DecoWatermarks.All(wm => wm.WatermarkId != id));
|
||||
IEnumerable<DecoWatermark> toRemove =
|
||||
existing.DecoWatermarks.Where(wm => !request.WatermarkIds.Contains(wm.WatermarkId));
|
||||
existing.DecoWatermarks.RemoveAll(toRemove.Contains);
|
||||
foreach (var watermarkId in toAdd)
|
||||
foreach (int watermarkId in toAdd)
|
||||
{
|
||||
existing.DecoWatermarks.Add(
|
||||
new DecoWatermark
|
||||
@@ -75,6 +77,7 @@ public class UpdateDecoHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
existing.DefaultFillerTrimToFit = request.DefaultFillerTrimToFit;
|
||||
|
||||
// dead air fallback
|
||||
|
||||
@@ -9,9 +9,10 @@ internal static class Mapper
|
||||
internal static TreeViewModel ProjectToViewModel(List<DecoTemplateGroup> decoTemplateGroups) =>
|
||||
new(
|
||||
decoTemplateGroups.OrderBy(dtg => dtg.Name).Map(dtg => new TreeGroupViewModel(
|
||||
dtg.Id,
|
||||
dtg.Name,
|
||||
dtg.DecoTemplates.OrderBy(dt => dt.Name).Map(dt => new TreeItemViewModel(dt.Id, dt.Name)).ToList())).ToList());
|
||||
dtg.Id,
|
||||
dtg.Name,
|
||||
dtg.DecoTemplates.OrderBy(dt => dt.Name).Map(dt => new TreeItemViewModel(dt.Id, dt.Name)).ToList()))
|
||||
.ToList());
|
||||
|
||||
internal static TreeViewModel ProjectToViewModel(List<DecoGroup> decoGroups) =>
|
||||
new(
|
||||
@@ -30,9 +31,11 @@ internal static class Mapper
|
||||
internal static BlockTreeViewModel ProjectToViewModel(List<BlockGroup> blockGroups) =>
|
||||
new(
|
||||
blockGroups.OrderBy(bg => bg.Name).Map(bg => new BlockTreeBlockGroupViewModel(
|
||||
bg.Id,
|
||||
bg.Name,
|
||||
bg.Blocks.OrderBy(b => b.Name).Map(b => new BlockTreeBlockViewModel(b.Id, b.Name, b.Minutes)).ToList())).ToList());
|
||||
bg.Id,
|
||||
bg.Name,
|
||||
bg.Blocks.OrderBy(b => b.Name).Map(b => new BlockTreeBlockViewModel(b.Id, b.Name, b.Minutes))
|
||||
.ToList()))
|
||||
.ToList());
|
||||
|
||||
internal static BlockGroupViewModel ProjectToViewModel(BlockGroup blockGroup) =>
|
||||
new(blockGroup.Id, blockGroup.Name);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class GetBlockItemsHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
.ThenInclude(am => am.Artwork)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (allItems.All(bi => bi.IncludeInProgramGuide == false))
|
||||
if (allItems.All(bi => !bi.IncludeInProgramGuide))
|
||||
{
|
||||
foreach (BlockItem bi in allItems)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using ErsatzTV.Application.Tree;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Domain.Scheduling;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using ErsatzTV.Application.Tree;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Domain.Scheduling;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
|
||||
namespace ErsatzTV.Application.Search;
|
||||
|
||||
public record QuerySearchIndexRemoteStreams(string Query, int PageNumber, int PageSize) : IRequest<RemoteStreamCardResultsViewModel>;
|
||||
public record QuerySearchIndexRemoteStreams(string Query, int PageNumber, int PageSize)
|
||||
: IRequest<RemoteStreamCardResultsViewModel>;
|
||||
|
||||
@@ -21,8 +21,8 @@ public class StartFFmpegSessionHandler : IRequestHandler<StartFFmpegSession, Eit
|
||||
{
|
||||
private readonly IClient _client;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IHlsPlaylistFilter _hlsPlaylistFilter;
|
||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
|
||||
@@ -24,9 +24,9 @@ namespace ErsatzTV.Application.Streaming;
|
||||
public class HlsSessionWorker : IHlsSessionWorker
|
||||
{
|
||||
private static int _workAheadCount;
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IClient _client;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IHlsPlaylistFilter _hlsPlaylistFilter;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ILogger<HlsSessionWorker> _logger;
|
||||
@@ -36,6 +36,7 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
private readonly Option<int> _targetFramerate;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private string _channelNumber;
|
||||
private DateTimeOffset _channelStart;
|
||||
private bool _disposedValue;
|
||||
private bool _hasWrittenSegments;
|
||||
private DateTimeOffset _lastAccess;
|
||||
@@ -44,7 +45,6 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
private HlsSessionState _state;
|
||||
private Timer _timer;
|
||||
private DateTimeOffset _transcodedUntil;
|
||||
private DateTimeOffset _channelStart;
|
||||
|
||||
public HlsSessionWorker(
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
@@ -152,7 +152,10 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task Run(string channelNumber, Option<TimeSpan> idleTimeout, CancellationToken incomingCancellationToken)
|
||||
public async Task Run(
|
||||
string channelNumber,
|
||||
Option<TimeSpan> idleTimeout,
|
||||
CancellationToken incomingCancellationToken)
|
||||
{
|
||||
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(incomingCancellationToken);
|
||||
|
||||
@@ -160,7 +163,7 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
{
|
||||
_channelNumber = channelNumber;
|
||||
|
||||
foreach (var timeout in idleTimeout)
|
||||
foreach (TimeSpan timeout in idleTimeout)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
@@ -181,14 +184,14 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
Touch();
|
||||
_transcodedUntil = DateTimeOffset.Now;
|
||||
PlaylistStart = _transcodedUntil;
|
||||
_channelStart = _transcodedUntil;
|
||||
_channelStart = _transcodedUntil;
|
||||
|
||||
var maybePlayoutId = await _mediator.Send(
|
||||
Option<int> maybePlayoutId = await _mediator.Send(
|
||||
new GetPlayoutIdByChannelNumber(_channelNumber),
|
||||
cancellationToken);
|
||||
|
||||
// time shift on-demand playout if needed
|
||||
foreach (var playoutId in maybePlayoutId)
|
||||
foreach (int playoutId in maybePlayoutId)
|
||||
{
|
||||
await _mediator.Send(
|
||||
new TimeShiftOnDemandPlayout(playoutId, _transcodedUntil, true),
|
||||
@@ -205,7 +208,7 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
foreach (var timeout in idleTimeout)
|
||||
foreach (TimeSpan timeout in idleTimeout)
|
||||
{
|
||||
if (DateTimeOffset.Now - _lastAccess > timeout)
|
||||
{
|
||||
@@ -461,7 +464,7 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
{
|
||||
await TrimAndDelete(cancellationToken);
|
||||
|
||||
var maybePipe = Option<Pipe>.None;
|
||||
Option<Pipe> maybePipe = Option<Pipe>.None;
|
||||
var stdErrBuffer = new StringBuilder();
|
||||
|
||||
Command process = processModel.Process;
|
||||
@@ -472,8 +475,8 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
{
|
||||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
|
||||
var processWithPipe = process;
|
||||
foreach (var graphicsEngineContext in processModel.GraphicsEngineContext)
|
||||
Command processWithPipe = process;
|
||||
foreach (GraphicsEngineContext graphicsEngineContext in processModel.GraphicsEngineContext)
|
||||
{
|
||||
var pipe = new Pipe();
|
||||
maybePipe = pipe;
|
||||
@@ -505,7 +508,7 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
await linkedCts.CancelAsync();
|
||||
|
||||
// detect the non-zero exit code and transcode the ffmpeg error message instead
|
||||
string errorMessage = stdErrBuffer.ToString();
|
||||
var errorMessage = stdErrBuffer.ToString();
|
||||
if (string.IsNullOrWhiteSpace(errorMessage))
|
||||
{
|
||||
errorMessage = $"Unknown FFMPEG error; exit code {commandResult.ExitCode}";
|
||||
@@ -563,7 +566,7 @@ public class HlsSessionWorker : IHlsSessionWorker
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var pipe in maybePipe)
|
||||
foreach (Pipe pipe in maybePipe)
|
||||
{
|
||||
await pipe.Writer.CompleteAsync();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker
|
||||
private readonly Option<int> _targetFramerate;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private string _channelNumber;
|
||||
private DateTimeOffset _channelStart;
|
||||
private bool _disposedValue;
|
||||
private DateTimeOffset _lastAccess;
|
||||
private Option<PlayoutItemProcessModel> _lastProcessModel;
|
||||
@@ -35,7 +36,6 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker
|
||||
private HlsSessionState _state;
|
||||
private Timer _timer;
|
||||
private DateTimeOffset _transcodedUntil;
|
||||
private DateTimeOffset _channelStart;
|
||||
|
||||
public HlsSessionWorkerV2(
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
@@ -99,7 +99,10 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task Run(string channelNumber, Option<TimeSpan> idleTimeout, CancellationToken incomingCancellationToken)
|
||||
public async Task Run(
|
||||
string channelNumber,
|
||||
Option<TimeSpan> idleTimeout,
|
||||
CancellationToken incomingCancellationToken)
|
||||
{
|
||||
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(incomingCancellationToken);
|
||||
|
||||
@@ -107,7 +110,7 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker
|
||||
{
|
||||
_channelNumber = channelNumber;
|
||||
|
||||
foreach (var timeout in idleTimeout)
|
||||
foreach (TimeSpan timeout in idleTimeout)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
@@ -130,12 +133,12 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker
|
||||
PlaylistStart = _transcodedUntil;
|
||||
_channelStart = _transcodedUntil;
|
||||
|
||||
var maybePlayoutId = await _mediator.Send(
|
||||
Option<int> maybePlayoutId = await _mediator.Send(
|
||||
new GetPlayoutIdByChannelNumber(_channelNumber),
|
||||
cancellationToken);
|
||||
|
||||
// time shift on-demand playout if needed
|
||||
foreach (var playoutId in maybePlayoutId)
|
||||
foreach (int playoutId in maybePlayoutId)
|
||||
{
|
||||
await _mediator.Send(
|
||||
new TimeShiftOnDemandPlayout(playoutId, _transcodedUntil, true),
|
||||
|
||||
@@ -14,6 +14,7 @@ public record PtsTime(long Value)
|
||||
{
|
||||
ptsTime += duration;
|
||||
}
|
||||
|
||||
return new PtsTime(ptsTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,10 @@ public class GetLastPtsTimeHandler : IRequestHandler<GetLastPtsTime, Either<Base
|
||||
return BaseError.New($"Failed to determine last pts duration for channel {parameters.ChannelNumber}");
|
||||
}
|
||||
|
||||
private async Task<Option<PtsTime>> GetPts(RequestParameters parameters, FileInfo segment, CancellationToken cancellationToken)
|
||||
private async Task<Option<PtsTime>> GetPts(
|
||||
RequestParameters parameters,
|
||||
FileInfo segment,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
string[] argumentList =
|
||||
{
|
||||
|
||||
@@ -290,18 +290,19 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
|
||||
|
||||
disableWatermarks = false;
|
||||
playoutItemWatermarks.Clear();
|
||||
playoutItemWatermarks.Add(new ChannelWatermark
|
||||
{
|
||||
Mode = ChannelWatermarkMode.Permanent,
|
||||
Size = WatermarkSize.Scaled,
|
||||
WidthPercent = 100,
|
||||
HorizontalMarginPercent = 0,
|
||||
VerticalMarginPercent = 0,
|
||||
Opacity = 100,
|
||||
Location = WatermarkLocation.TopLeft,
|
||||
ImageSource = ChannelWatermarkImageSource.Resource,
|
||||
Image = image
|
||||
});
|
||||
playoutItemWatermarks.Add(
|
||||
new ChannelWatermark
|
||||
{
|
||||
Mode = ChannelWatermarkMode.Permanent,
|
||||
Size = WatermarkSize.Scaled,
|
||||
WidthPercent = 100,
|
||||
HorizontalMarginPercent = 0,
|
||||
VerticalMarginPercent = 0,
|
||||
Opacity = 100,
|
||||
Location = WatermarkLocation.TopLeft,
|
||||
ImageSource = ChannelWatermarkImageSource.Resource,
|
||||
Image = image
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +356,9 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
|
||||
channel.FFmpegProfile.VaapiDevice,
|
||||
Optional(channel.FFmpegProfile.QsvExtraHardwareFrames),
|
||||
hlsRealtime: request.HlsRealtime,
|
||||
playoutItemWithPath.PlayoutItem.MediaItem is RemoteStream { IsLive: true } ? StreamInputKind.Live : StreamInputKind.Vod,
|
||||
playoutItemWithPath.PlayoutItem.MediaItem is RemoteStream { IsLive: true }
|
||||
? StreamInputKind.Live
|
||||
: StreamInputKind.Vod,
|
||||
playoutItemWithPath.PlayoutItem.FillerKind,
|
||||
inPoint,
|
||||
outPoint,
|
||||
|
||||
@@ -280,7 +280,7 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler<ExtractEmbeddedSu
|
||||
.Filter(s => s.SubtitleKind == SubtitleKind.Embedded)
|
||||
.Filter(s => s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" &&
|
||||
s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs")
|
||||
.Filter(s => s.IsExtracted == false || string.IsNullOrWhiteSpace(s.Path) ||
|
||||
.Filter(s => !s.IsExtracted || string.IsNullOrWhiteSpace(s.Path) ||
|
||||
FileDoesntExist(mediaItem.Id, s));
|
||||
|
||||
// find cache paths for each subtitle
|
||||
@@ -347,7 +347,7 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler<ExtractEmbeddedSu
|
||||
{
|
||||
foreach (string path in GetRelativeOutputPath(mediaItemId, subtitle))
|
||||
{
|
||||
return _localFileSystem.FileExists(path) == false;
|
||||
return !_localFileSystem.FileExists(path);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace ErsatzTV.Application.Television;
|
||||
|
||||
public class GetTelevisionShowByIdHandler : IRequestHandler<GetTelevisionShowById, Option<TelevisionShowViewModel>>
|
||||
{
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly ISearchRepository _searchRepository;
|
||||
|
||||
public GetTelevisionShowByIdHandler(
|
||||
|
||||
@@ -34,12 +34,16 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
ILogger<PrepareTroubleshootingPlaybackHandler> logger)
|
||||
: IRequestHandler<PrepareTroubleshootingPlayback, Either<BaseError, PlayoutItemResult>>
|
||||
{
|
||||
public async Task<Either<BaseError, PlayoutItemResult>> Handle(PrepareTroubleshootingPlayback request, CancellationToken cancellationToken)
|
||||
public async Task<Either<BaseError, PlayoutItemResult>> Handle(
|
||||
PrepareTroubleshootingPlayback request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, Tuple<MediaItem, string, string, FFmpegProfile>> validation = await Validate(dbContext, request);
|
||||
Validation<BaseError, Tuple<MediaItem, string, string, FFmpegProfile>> validation = await Validate(
|
||||
dbContext,
|
||||
request);
|
||||
return await validation.Match(
|
||||
tuple => GetProcess(dbContext, request, tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4),
|
||||
error => Task.FromResult<Either<BaseError, PlayoutItemResult>>(error.Join()));
|
||||
@@ -96,8 +100,8 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
List<ChannelWatermark> watermarks = [];
|
||||
if (request.WatermarkIds.Count > 0)
|
||||
{
|
||||
var channelWatermarks = await dbContext.ChannelWatermarks
|
||||
.Where(w => request.WatermarkIds.Contains(w.Id))
|
||||
List<ChannelWatermark> channelWatermarks = await dbContext.ChannelWatermarks
|
||||
.Where(w => request.WatermarkIds.Contains(w.Id))
|
||||
.ToListAsync();
|
||||
|
||||
watermarks.AddRange(channelWatermarks);
|
||||
@@ -126,18 +130,19 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
string image = is43 ? "song_progress_overlay_43.png" : "song_progress_overlay.png";
|
||||
|
||||
watermarks.Clear();
|
||||
watermarks.Add(new ChannelWatermark
|
||||
{
|
||||
Mode = ChannelWatermarkMode.Permanent,
|
||||
Size = WatermarkSize.Scaled,
|
||||
WidthPercent = 100,
|
||||
HorizontalMarginPercent = 0,
|
||||
VerticalMarginPercent = 0,
|
||||
Opacity = 100,
|
||||
Location = WatermarkLocation.TopLeft,
|
||||
ImageSource = ChannelWatermarkImageSource.Resource,
|
||||
Image = image
|
||||
});
|
||||
watermarks.Add(
|
||||
new ChannelWatermark
|
||||
{
|
||||
Mode = ChannelWatermarkMode.Permanent,
|
||||
Size = WatermarkSize.Scaled,
|
||||
WidthPercent = 100,
|
||||
HorizontalMarginPercent = 0,
|
||||
VerticalMarginPercent = 0,
|
||||
Opacity = 100,
|
||||
Location = WatermarkLocation.TopLeft,
|
||||
ImageSource = ChannelWatermarkImageSource.Resource,
|
||||
Image = image
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +161,7 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
TimeSpan outPoint = duration;
|
||||
if (!hlsRealtime)
|
||||
{
|
||||
foreach (var seekSeconds in request.SeekSeconds)
|
||||
foreach (int seekSeconds in request.SeekSeconds)
|
||||
{
|
||||
inPoint = TimeSpan.FromSeconds(seekSeconds);
|
||||
if (inPoint > version.Duration)
|
||||
@@ -173,7 +178,7 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
}
|
||||
}
|
||||
|
||||
var graphicsElements = await dbContext.GraphicsElements
|
||||
List<GraphicsElement> graphicsElements = await dbContext.GraphicsElements
|
||||
.Where(ge => request.GraphicsElementIds.Contains(ge.Id))
|
||||
.ToListAsync();
|
||||
|
||||
@@ -216,7 +221,9 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
return playoutItemResult;
|
||||
}
|
||||
|
||||
private static async Task<List<Subtitle>> GetSelectedSubtitle(MediaItem mediaItem, PrepareTroubleshootingPlayback request)
|
||||
private static async Task<List<Subtitle>> GetSelectedSubtitle(
|
||||
MediaItem mediaItem,
|
||||
PrepareTroubleshootingPlayback request)
|
||||
{
|
||||
if (request.SubtitleId is not null)
|
||||
{
|
||||
@@ -268,9 +275,8 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
|
||||
private static async Task<Validation<BaseError, MediaItem>> MediaItemMustExist(
|
||||
TvContext dbContext,
|
||||
PrepareTroubleshootingPlayback request)
|
||||
{
|
||||
return await dbContext.MediaItems
|
||||
PrepareTroubleshootingPlayback request) =>
|
||||
await dbContext.MediaItems
|
||||
.AsNoTracking()
|
||||
.Include(mi => (mi as Episode).EpisodeMetadata)
|
||||
.ThenInclude(em => em.Subtitles)
|
||||
@@ -325,7 +331,6 @@ public class PrepareTroubleshootingPlaybackHandler(
|
||||
.Include(mi => (mi as RemoteStream).RemoteStreamMetadata)
|
||||
.SelectOneAsync(mi => mi.Id, mi => mi.Id == request.MediaItemId)
|
||||
.Map(o => o.ToValidation<BaseError>(new UnableToLocatePlayoutItem()));
|
||||
}
|
||||
|
||||
private static Task<Validation<BaseError, string>> FFmpegPathMustExist(TvContext dbContext) =>
|
||||
dbContext.ConfigElements.GetValue<string>(ConfigElementKey.FFmpegPath)
|
||||
|
||||
@@ -70,8 +70,8 @@ public class StartTroubleshootingPlaybackHandler(
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
if (hwAccel is HardwareAccelerationKind.Vaapi || (hwAccel is HardwareAccelerationKind.Qsv &&
|
||||
runtimeInfo.IsOSPlatform(OSPlatform.Linux)))
|
||||
if (hwAccel is HardwareAccelerationKind.Vaapi || hwAccel is HardwareAccelerationKind.Qsv &&
|
||||
runtimeInfo.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
await File.WriteAllTextAsync(
|
||||
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "capabilities_vaapi.txt"),
|
||||
@@ -87,20 +87,23 @@ public class StartTroubleshootingPlaybackHandler(
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
logger.LogDebug("ffmpeg troubleshooting arguments {FFmpegArguments}", request.PlayoutItemResult.Process.Arguments);
|
||||
logger.LogDebug(
|
||||
"ffmpeg troubleshooting arguments {FFmpegArguments}",
|
||||
request.PlayoutItemResult.Process.Arguments);
|
||||
|
||||
var maybePipe = Option<Pipe>.None;
|
||||
Option<Pipe> maybePipe = Option<Pipe>.None;
|
||||
|
||||
try
|
||||
{
|
||||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
|
||||
var processWithPipe = request.PlayoutItemResult.Process;
|
||||
foreach (var graphicsEngineContext in request.PlayoutItemResult.GraphicsEngineContext)
|
||||
Command processWithPipe = request.PlayoutItemResult.Process;
|
||||
foreach (GraphicsEngineContext graphicsEngineContext in request.PlayoutItemResult.GraphicsEngineContext)
|
||||
{
|
||||
var pipe = new Pipe();
|
||||
maybePipe = pipe;
|
||||
processWithPipe = processWithPipe.WithStandardInputPipe(PipeSource.FromStream(pipe.Reader.AsStream()));
|
||||
processWithPipe =
|
||||
processWithPipe.WithStandardInputPipe(PipeSource.FromStream(pipe.Reader.AsStream()));
|
||||
|
||||
// fire and forget graphics engine task
|
||||
_ = graphicsEngine.Run(
|
||||
@@ -136,7 +139,7 @@ public class StartTroubleshootingPlaybackHandler(
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var pipe in maybePipe)
|
||||
foreach (Pipe pipe in maybePipe)
|
||||
{
|
||||
await pipe.Writer.CompleteAsync();
|
||||
}
|
||||
|
||||
@@ -135,23 +135,25 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
|
||||
|
||||
if (_runtimeInfo.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
var decoders = _hardwareCapabilitiesFactory.GetVideoToolboxDecoders();
|
||||
List<string> decoders = _hardwareCapabilitiesFactory.GetVideoToolboxDecoders();
|
||||
videoToolboxCapabilities.AppendLine("VideoToolbox Decoders: ");
|
||||
videoToolboxCapabilities.AppendLine();
|
||||
foreach (var decoder in decoders)
|
||||
foreach (string decoder in decoders)
|
||||
{
|
||||
videoToolboxCapabilities.AppendLine(CultureInfo.InvariantCulture, $"\t{decoder}");
|
||||
}
|
||||
|
||||
videoToolboxCapabilities.AppendLine();
|
||||
videoToolboxCapabilities.AppendLine();
|
||||
|
||||
var encoders = _hardwareCapabilitiesFactory.GetVideoToolboxEncoders();
|
||||
List<string> encoders = _hardwareCapabilitiesFactory.GetVideoToolboxEncoders();
|
||||
videoToolboxCapabilities.AppendLine("VideoToolbox Encoders: ");
|
||||
videoToolboxCapabilities.AppendLine();
|
||||
foreach (var encoder in encoders)
|
||||
foreach (string encoder in encoders)
|
||||
{
|
||||
videoToolboxCapabilities.AppendLine(CultureInfo.InvariantCulture, $"\t{encoder}");
|
||||
}
|
||||
|
||||
videoToolboxCapabilities.AppendLine();
|
||||
videoToolboxCapabilities.AppendLine();
|
||||
}
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
<ProjectReference Include="..\ErsatzTV.Infrastructure\ErsatzTV.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -58,7 +58,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -83,7 +87,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -108,7 +116,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -139,7 +151,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -165,7 +181,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -192,7 +212,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -219,7 +243,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeFalse();
|
||||
}
|
||||
@@ -241,7 +269,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -269,7 +301,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -303,7 +339,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -334,7 +374,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -367,7 +411,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -481,7 +529,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -509,7 +561,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -539,7 +595,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -569,7 +629,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -596,7 +660,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -623,7 +691,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -650,7 +722,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -677,7 +753,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -703,7 +783,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -730,7 +814,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.Subtitle.IsSome.ShouldBeTrue();
|
||||
|
||||
@@ -758,7 +846,11 @@ public class CustomStreamSelectorTests
|
||||
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]),
|
||||
new NullLogger<CustomStreamSelector>());
|
||||
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles);
|
||||
StreamSelectorResult result = await streamSelector.SelectStreams(
|
||||
_channel,
|
||||
DateTimeOffset.Now,
|
||||
_audioVersion,
|
||||
_subtitles);
|
||||
|
||||
result.AudioStream.IsSome.ShouldBeFalse();
|
||||
result.Subtitle.IsSome.ShouldBeFalse();
|
||||
|
||||
@@ -28,7 +28,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().MediaItemId.ShouldBe(1);
|
||||
@@ -73,7 +80,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().MediaItemId.ShouldBe(1);
|
||||
@@ -119,7 +133,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromDays(1);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(4);
|
||||
result.AddedItems.Map(i => i.MediaItemId).ToList().ShouldBe([1, 2, 1, 2]);
|
||||
@@ -156,7 +177,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start2 = HoursAfterMidnight(1);
|
||||
DateTimeOffset finish2 = start2 + TimeSpan.FromDays(1);
|
||||
|
||||
result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Continue, start2, finish2, CancellationToken);
|
||||
result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue,
|
||||
start2,
|
||||
finish2,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems[0].StartOffset.ShouldBe(finish);
|
||||
@@ -171,7 +199,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start3 = HoursAfterMidnight(2);
|
||||
DateTimeOffset finish3 = start3 + TimeSpan.FromDays(1);
|
||||
|
||||
result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Continue, start3, finish3, CancellationToken);
|
||||
result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue,
|
||||
start3,
|
||||
finish3,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(0);
|
||||
|
||||
@@ -196,8 +231,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
playout.ProgramScheduleAnchors.Count.ShouldBe(1);
|
||||
@@ -235,11 +276,19 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
mediaItems.Add(TestMovie(i, TimeSpan.FromMinutes(55), DateTime.Today.AddHours(i)));
|
||||
}
|
||||
|
||||
(PlayoutBuilder builder, Playout playout, PlayoutReferenceData referenceData) = TestDataFloodForItems(mediaItems, PlaybackOrder.Shuffle);
|
||||
(PlayoutBuilder builder, Playout playout, PlayoutReferenceData referenceData) =
|
||||
TestDataFloodForItems(mediaItems, PlaybackOrder.Shuffle);
|
||||
DateTimeOffset start = HoursAfterMidnight(0).AddSeconds(5);
|
||||
DateTimeOffset finish = start + TimeSpan.FromDays(2);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(53);
|
||||
playout.ProgramScheduleAnchors.Count.ShouldBe(2);
|
||||
@@ -301,7 +350,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
playout.ProgramScheduleAnchors.Count.ShouldBe(2);
|
||||
@@ -346,7 +402,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0).AddSeconds(5);
|
||||
DateTimeOffset finish = start + TimeSpan.FromDays(2);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(53);
|
||||
playout.ProgramScheduleAnchors.Count.ShouldBe(4);
|
||||
@@ -482,8 +545,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(32);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(5);
|
||||
|
||||
@@ -595,8 +664,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(5);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(4);
|
||||
|
||||
@@ -707,8 +782,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
localFileSystem,
|
||||
Logger);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Continue,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(5);
|
||||
|
||||
@@ -728,4 +809,4 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
|
||||
playout.Anchor.DurationFinish.ShouldBeNull();
|
||||
playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Scheduling;
|
||||
using Shouldly;
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
|
||||
namespace ErsatzTV.Core.Tests.Scheduling.ClassicScheduling;
|
||||
|
||||
@@ -30,4 +30,4 @@ public class GetStartTimeAfterTests
|
||||
|
||||
result.ShouldBe(DateTimeOffset.Parse("2025-11-02T02:00:00-06:00"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().MediaItemId.ShouldBe(2);
|
||||
@@ -102,8 +108,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().MediaItemId.ShouldBe(2);
|
||||
@@ -158,8 +170,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().MediaItemId.ShouldBe(2);
|
||||
@@ -189,8 +207,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().StartOffset.ShouldBe(start);
|
||||
@@ -219,8 +243,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().StartOffset.ShouldBe(start);
|
||||
@@ -242,8 +272,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().StartOffset.ShouldBe(start);
|
||||
@@ -266,8 +302,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(1);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(2);
|
||||
result.AddedItems[0].StartOffset.ShouldBe(midnight);
|
||||
@@ -291,8 +333,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(4);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(4);
|
||||
result.AddedItems[0].StartOffset.ShouldBe(start);
|
||||
@@ -321,8 +369,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().MediaItemId.ShouldBe(1);
|
||||
@@ -367,8 +421,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(1);
|
||||
result.AddedItems.Head().MediaItemId.ShouldBe(1);
|
||||
@@ -482,8 +542,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(5);
|
||||
result.AddedItems[0].StartOffset.ShouldBe(start);
|
||||
@@ -582,8 +648,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(30);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(28);
|
||||
result.AddedItems[0].StartOffset.ShouldBe(start);
|
||||
@@ -733,8 +805,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(7);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
|
||||
@@ -843,8 +921,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(7);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
|
||||
@@ -952,8 +1036,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(24);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
|
||||
@@ -1062,8 +1152,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(7);
|
||||
|
||||
@@ -1177,8 +1273,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
|
||||
@@ -1292,8 +1394,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(5);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(5);
|
||||
|
||||
@@ -1416,8 +1524,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(12);
|
||||
|
||||
@@ -1533,8 +1647,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(1);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(2);
|
||||
|
||||
@@ -1616,8 +1736,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
|
||||
@@ -1654,8 +1780,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromDays(2);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(8);
|
||||
result.AddedItems[0].MediaItemId.ShouldBe(1);
|
||||
@@ -1692,4 +1824,4 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
|
||||
|
||||
playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(48));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ namespace ErsatzTV.Core.Tests.Scheduling.ClassicScheduling;
|
||||
|
||||
public abstract class PlayoutBuilderTestBase
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => CancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
protected readonly ILogger<PlayoutBuilder> Logger;
|
||||
protected CancellationToken CancellationToken;
|
||||
|
||||
@@ -35,6 +32,9 @@ public abstract class PlayoutBuilderTestBase
|
||||
Logger = loggerFactory.CreateLogger<PlayoutBuilder>();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => CancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
protected static DateTimeOffset HoursAfterMidnight(int hours)
|
||||
{
|
||||
DateTimeOffset now = DateTimeOffset.Now;
|
||||
@@ -88,7 +88,14 @@ public abstract class PlayoutBuilderTestBase
|
||||
FillGroupIndices = []
|
||||
};
|
||||
|
||||
var referenceData = new PlayoutReferenceData(playout.Channel, Option<Deco>.None, [], [], playout.ProgramSchedule, [], []);
|
||||
var referenceData = new PlayoutReferenceData(
|
||||
playout.Channel,
|
||||
Option<Deco>.None,
|
||||
[],
|
||||
[],
|
||||
playout.ProgramSchedule,
|
||||
[],
|
||||
[]);
|
||||
|
||||
return new TestData(builder, playout, referenceData);
|
||||
}
|
||||
@@ -194,10 +201,17 @@ public abstract class PlayoutBuilderTestBase
|
||||
FillGroupIndices = []
|
||||
};
|
||||
|
||||
var referenceData = new PlayoutReferenceData(playout.Channel, Option<Deco>.None, [], [], playout.ProgramSchedule, [], []);
|
||||
var referenceData = new PlayoutReferenceData(
|
||||
playout.Channel,
|
||||
Option<Deco>.None,
|
||||
[],
|
||||
[],
|
||||
playout.ProgramSchedule,
|
||||
[],
|
||||
[]);
|
||||
|
||||
return new TestData(builder, playout, referenceData);
|
||||
}
|
||||
|
||||
protected record TestData(PlayoutBuilder Builder, Playout Playout, PlayoutReferenceData ReferenceData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,14 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase
|
||||
Playout = playout
|
||||
});
|
||||
|
||||
var referenceData = new PlayoutReferenceData(playout.Channel, Option<Deco>.None, [], [], playout.ProgramSchedule, [], []);
|
||||
var referenceData = new PlayoutReferenceData(
|
||||
playout.Channel,
|
||||
Option<Deco>.None,
|
||||
[],
|
||||
[],
|
||||
playout.ProgramSchedule,
|
||||
[],
|
||||
[]);
|
||||
|
||||
IConfigElementRepository configRepo = Substitute.For<IConfigElementRepository>();
|
||||
var televisionRepo = new FakeTelevisionRepository();
|
||||
@@ -106,7 +113,14 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(24);
|
||||
DateTimeOffset finish = start + TimeSpan.FromDays(1);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Refresh, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Refresh,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(4);
|
||||
result.AddedItems[0].MediaItemId.ShouldBe(2);
|
||||
@@ -124,4 +138,4 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase
|
||||
|
||||
playout.Anchor.NextStartOffset.ShouldBe(HoursAfterMidnight(48));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,14 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish = start + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start, finish, CancellationToken);
|
||||
PlayoutBuildResult result = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
CancellationToken);
|
||||
|
||||
result.AddedItems.Count.ShouldBe(6);
|
||||
playout.Anchor.NextStartOffset.ShouldBe(finish);
|
||||
@@ -40,8 +46,14 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase
|
||||
DateTimeOffset start2 = HoursAfterMidnight(0);
|
||||
DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6);
|
||||
|
||||
PlayoutBuildResult result2 = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset, start2, finish2, CancellationToken);
|
||||
PlayoutBuildResult result2 = await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start2,
|
||||
finish2,
|
||||
CancellationToken);
|
||||
|
||||
result2.AddedItems.Count.ShouldBe(6);
|
||||
playout.Anchor.NextStartOffset.ShouldBe(finish);
|
||||
@@ -53,4 +65,4 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase
|
||||
|
||||
firstSeedValue.ShouldNotBe(secondSeedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,8 @@ public class FillerExpressionTests
|
||||
var fillerPreset = new FillerPreset
|
||||
{
|
||||
FillerKind = FillerKind.MidRoll,
|
||||
Expression = "(total_progress >= 0.2 and matched_points = 0) or (total_progress >= 0.6 and matched_points = 1)"
|
||||
Expression =
|
||||
"(total_progress >= 0.2 and matched_points = 0) or (total_progress >= 0.6 and matched_points = 1)"
|
||||
};
|
||||
|
||||
List<MediaChapter> result = FillerExpression.FilterChapters(fillerPreset.Expression, chapters, playoutItem);
|
||||
|
||||
@@ -38,7 +38,6 @@ public class MultiPartEpisodeGrouperTests
|
||||
[TestCase("Episode 1 Part One", "Episode 2 (II)", "Episode 3")]
|
||||
[TestCase("Episode 1 (Part One)", "Episode 2 (II)", "Episode 3")]
|
||||
[TestCase("Episode 1 (1)", "Episode 2 (Part 2)", "Episode 3")]
|
||||
|
||||
public void MixedNaming_Group(string one, string two, string three)
|
||||
{
|
||||
var mediaItems = new List<MediaItem>
|
||||
@@ -58,7 +57,10 @@ public class MultiPartEpisodeGrouperTests
|
||||
[Test]
|
||||
[TestCase("The Meddlers (Part One)", "The Meddlers (Part Two)", "The Meddlers (Part Three)")]
|
||||
[TestCase("The Slaves of Jedikiah, Part 1", "The Slaves of Jedikiah, Part 2", "The Slaves of Jedikiah, Part 3")]
|
||||
[TestCase("An Unearthly Child: An Unearthly Child (1)", "An Unearthly Child: The Cave of Skulls (2)", "An Unearthly Child: The Forest of Fear (3)")]
|
||||
[TestCase(
|
||||
"An Unearthly Child: An Unearthly Child (1)",
|
||||
"An Unearthly Child: The Cave of Skulls (2)",
|
||||
"An Unearthly Child: The Forest of Fear (3)")]
|
||||
[TestCase("The Savages (1)", "The Savages (2)", "The Savages (3)")]
|
||||
public void All_Grouped(string one, string two, string three)
|
||||
{
|
||||
|
||||
@@ -130,9 +130,19 @@ public class ScheduleIntegrationTests
|
||||
|
||||
Option<Playout> maybePlayout = await GetPlayout(context, PLAYOUT_ID);
|
||||
Playout playout = maybePlayout.ValueUnsafe();
|
||||
var referenceData = await GetReferenceData(context, PLAYOUT_ID, ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
PLAYOUT_ID,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
|
||||
await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, _cancellationToken);
|
||||
await builder.Build(
|
||||
playout,
|
||||
referenceData,
|
||||
PlayoutBuildResult.Empty,
|
||||
PlayoutBuildMode.Reset,
|
||||
start,
|
||||
finish,
|
||||
_cancellationToken);
|
||||
|
||||
// TODO: would need to apply changes from build result
|
||||
await context.SaveChangesAsync(_cancellationToken);
|
||||
@@ -144,7 +154,10 @@ public class ScheduleIntegrationTests
|
||||
|
||||
Option<Playout> maybePlayout = await GetPlayout(context, PLAYOUT_ID);
|
||||
Playout playout = maybePlayout.ValueUnsafe();
|
||||
var referenceData = await GetReferenceData(context, PLAYOUT_ID, ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
PLAYOUT_ID,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
|
||||
await builder.Build(
|
||||
playout,
|
||||
@@ -165,7 +178,10 @@ public class ScheduleIntegrationTests
|
||||
|
||||
Option<Playout> maybePlayout = await GetPlayout(context, PLAYOUT_ID);
|
||||
Playout playout = maybePlayout.ValueUnsafe();
|
||||
var referenceData = await GetReferenceData(context, PLAYOUT_ID, ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
PLAYOUT_ID,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
|
||||
await builder.Build(
|
||||
playout,
|
||||
@@ -310,7 +326,10 @@ public class ScheduleIntegrationTests
|
||||
|
||||
Option<Playout> maybePlayout = await GetPlayout(context, playoutId);
|
||||
Playout playout = maybePlayout.ValueUnsafe();
|
||||
var referenceData = await GetReferenceData(context, playoutId, ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
playoutId,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
|
||||
await builder.Build(
|
||||
playout,
|
||||
@@ -380,7 +399,7 @@ public class ScheduleIntegrationTests
|
||||
int playoutId,
|
||||
ProgramSchedulePlayoutType playoutType)
|
||||
{
|
||||
var channel = await dbContext.Channels
|
||||
Channel channel = await dbContext.Channels
|
||||
.AsNoTracking()
|
||||
.Where(c => c.Playouts.Any(p => p.Id == playoutId))
|
||||
.FirstOrDefaultAsync();
|
||||
@@ -408,7 +427,7 @@ public class ScheduleIntegrationTests
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
var programSchedule = await dbContext.ProgramSchedules
|
||||
ProgramSchedule programSchedule = await dbContext.ProgramSchedules
|
||||
.AsNoTracking()
|
||||
.Where(ps => ps.Playouts.Any(p => p.Id == playoutId))
|
||||
.Include(ps => ps.Items)
|
||||
@@ -430,7 +449,7 @@ public class ScheduleIntegrationTests
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var programScheduleAlternates = await dbContext.ProgramScheduleAlternates
|
||||
List<ProgramScheduleAlternate> programScheduleAlternates = await dbContext.ProgramScheduleAlternates
|
||||
.AsNoTracking()
|
||||
.Where(pt => pt.PlayoutId == playoutId)
|
||||
.Include(a => a.ProgramSchedule)
|
||||
@@ -460,7 +479,7 @@ public class ScheduleIntegrationTests
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
.ToListAsync();
|
||||
|
||||
var playoutHistory = await dbContext.PlayoutHistory
|
||||
List<PlayoutHistory> playoutHistory = await dbContext.PlayoutHistory
|
||||
.AsNoTracking()
|
||||
.Where(h => h.PlayoutId == playoutId)
|
||||
.ToListAsync();
|
||||
|
||||
@@ -7,4 +7,4 @@ public class GraphicsElement
|
||||
public GraphicsElementKind Kind { get; set; }
|
||||
public List<PlayoutItem> PlayoutItems { get; set; }
|
||||
public List<PlayoutItemGraphicsElement> PlayoutItemGraphicsElements { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ public enum GraphicsElementKind
|
||||
Image = 0,
|
||||
Text = 1,
|
||||
Subtitle = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,4 +44,4 @@
|
||||
<ProjectReference Include="..\ErsatzTV.FFmpeg\ErsatzTV.FFmpeg.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -199,7 +199,7 @@ public class FFmpegComplexFilterBuilder
|
||||
}
|
||||
});
|
||||
|
||||
if (scaleOrPad && _boxBlur == false)
|
||||
if (scaleOrPad && !_boxBlur)
|
||||
{
|
||||
videoFilterQueue.Add("setsar=1");
|
||||
}
|
||||
|
||||
@@ -216,7 +216,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
videoPath != audioPath, // still image when paths are different
|
||||
videoVersion.VideoScanKind == VideoScanKind.Progressive ? ScanKind.Progressive : ScanKind.Interlaced);
|
||||
|
||||
var videoInputFile = new VideoInputFile(videoPath, new List<VideoStream> { ffmpegVideoStream }, streamInputKind);
|
||||
var videoInputFile = new VideoInputFile(
|
||||
videoPath,
|
||||
new List<VideoStream> { ffmpegVideoStream },
|
||||
streamInputKind);
|
||||
|
||||
Option<AudioInputFile> audioInputFile = maybeAudioStream.Map(audioStream =>
|
||||
{
|
||||
@@ -347,7 +350,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
None,
|
||||
None);
|
||||
|
||||
foreach (var watermark in options.Watermark)
|
||||
foreach (ChannelWatermark watermark in options.Watermark)
|
||||
{
|
||||
// don't allow duplicates
|
||||
watermarks.TryAdd(watermark.Id, new WatermarkElementContext(options));
|
||||
@@ -355,7 +358,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
}
|
||||
|
||||
// load all playout item watermarks
|
||||
foreach (var playoutItemWatermark in playoutItemWatermarks)
|
||||
foreach (ChannelWatermark playoutItemWatermark in playoutItemWatermarks)
|
||||
{
|
||||
WatermarkOptions options = await _ffmpegProcessService.GetWatermarkOptions(
|
||||
ffprobePath,
|
||||
@@ -366,7 +369,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
None,
|
||||
None);
|
||||
|
||||
foreach (var watermark in options.Watermark)
|
||||
foreach (ChannelWatermark watermark in options.Watermark)
|
||||
{
|
||||
// don't allow duplicates
|
||||
watermarks.TryAdd(watermark.Id, new WatermarkElementContext(options));
|
||||
@@ -448,13 +451,13 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
playbackSettings.VideoTrackTimeScale,
|
||||
playbackSettings.Deinterlace);
|
||||
|
||||
foreach (var playoutItemGraphicsElement in graphicsElements)
|
||||
foreach (PlayoutItemGraphicsElement playoutItemGraphicsElement in graphicsElements)
|
||||
{
|
||||
switch (playoutItemGraphicsElement.GraphicsElement.Kind)
|
||||
{
|
||||
case GraphicsElementKind.Text:
|
||||
{
|
||||
var maybeElement =
|
||||
Option<TextGraphicsElement> maybeElement =
|
||||
await TextGraphicsElement.FromFile(playoutItemGraphicsElement.GraphicsElement.Path);
|
||||
if (maybeElement.IsNone)
|
||||
{
|
||||
@@ -463,7 +466,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
playoutItemGraphicsElement.GraphicsElement.Path);
|
||||
}
|
||||
|
||||
foreach (var element in maybeElement)
|
||||
foreach (TextGraphicsElement element in maybeElement)
|
||||
{
|
||||
var variables = new Dictionary<string, string>();
|
||||
if (!string.IsNullOrWhiteSpace(playoutItemGraphicsElement.Variables))
|
||||
@@ -479,7 +482,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
}
|
||||
case GraphicsElementKind.Image:
|
||||
{
|
||||
var maybeElement =
|
||||
Option<ImageGraphicsElement> maybeElement =
|
||||
await ImageGraphicsElement.FromFile(playoutItemGraphicsElement.GraphicsElement.Path);
|
||||
if (maybeElement.IsNone)
|
||||
{
|
||||
@@ -488,7 +491,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
playoutItemGraphicsElement.GraphicsElement.Path);
|
||||
}
|
||||
|
||||
foreach (var element in maybeElement)
|
||||
foreach (ImageGraphicsElement element in maybeElement)
|
||||
{
|
||||
graphicsElementContexts.Add(new ImageElementContext(element));
|
||||
}
|
||||
@@ -497,7 +500,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
}
|
||||
case GraphicsElementKind.Subtitle:
|
||||
{
|
||||
var maybeElement =
|
||||
Option<SubtitlesGraphicsElement> maybeElement =
|
||||
await SubtitlesGraphicsElement.FromFile(playoutItemGraphicsElement.GraphicsElement.Path);
|
||||
if (maybeElement.IsNone)
|
||||
{
|
||||
@@ -506,7 +509,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
playoutItemGraphicsElement.GraphicsElement.Path);
|
||||
}
|
||||
|
||||
foreach (var element in maybeElement)
|
||||
foreach (SubtitlesGraphicsElement element in maybeElement)
|
||||
{
|
||||
var variables = new Dictionary<string, string>();
|
||||
if (!string.IsNullOrWhiteSpace(playoutItemGraphicsElement.Variables))
|
||||
@@ -540,8 +543,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
new Resolution { Width = desiredState.ScaledSize.Width, Height = desiredState.ScaledSize.Height },
|
||||
channel.FFmpegProfile.Resolution,
|
||||
await playbackSettings.FrameRate.IfNoneAsync(24),
|
||||
ChannelStartTime: channelStartTime,
|
||||
ContentStartTime: start,
|
||||
channelStartTime,
|
||||
start,
|
||||
await playbackSettings.StreamSeek.IfNoneAsync(TimeSpan.Zero),
|
||||
finish - now);
|
||||
}
|
||||
@@ -592,7 +595,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
|
||||
pipelineAction?.Invoke(pipeline);
|
||||
|
||||
var command = GetCommand(
|
||||
Command command = GetCommand(
|
||||
ffmpegPath,
|
||||
videoInputFile,
|
||||
audioInputFile,
|
||||
|
||||
@@ -114,7 +114,7 @@ public static class FFmpegPlaybackSettingsCalculator
|
||||
|
||||
result.VideoTrackTimeScale = 90000;
|
||||
|
||||
foreach (MediaStream stream in videoStream.Where(s => s.AttachedPic == false))
|
||||
foreach (MediaStream stream in videoStream.Where(s => !s.AttachedPic))
|
||||
{
|
||||
result.VideoFormat = ffmpegProfile.VideoFormat;
|
||||
result.VideoBitrate = ffmpegProfile.VideoBitrate;
|
||||
|
||||
@@ -153,10 +153,11 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector
|
||||
candidateSubtitles = candidateSubtitles.Filter(s => s.SubtitleKind is not SubtitleKind.Embedded).ToList();
|
||||
}
|
||||
|
||||
foreach (Subtitle subtitle in candidateSubtitles.Filter(s => s.SubtitleKind is SubtitleKind.Embedded && !s.IsImage)
|
||||
foreach (Subtitle subtitle in candidateSubtitles
|
||||
.Filter(s => s.SubtitleKind is SubtitleKind.Embedded && !s.IsImage)
|
||||
.ToList())
|
||||
{
|
||||
if (subtitle.IsExtracted == false)
|
||||
if (!subtitle.IsExtracted)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Ignoring embedded subtitle with index {Index} that has not been extracted",
|
||||
|
||||
@@ -15,19 +15,24 @@ public class ImageGraphicsElement
|
||||
public string OpacityExpression { get; set; }
|
||||
|
||||
public WatermarkLocation Location { get; set; }
|
||||
|
||||
[YamlMember(Alias = "horizontal_margin_percent", ApplyNamingConventions = false)]
|
||||
public double? HorizontalMarginPercent { get; set; }
|
||||
|
||||
[YamlMember(Alias = "vertical_margin_percent", ApplyNamingConventions = false)]
|
||||
public double? VerticalMarginPercent { get; set; }
|
||||
|
||||
[YamlMember(Alias = "location_x", ApplyNamingConventions = false)]
|
||||
public double? LocationX { get; set; }
|
||||
|
||||
[YamlMember(Alias = "location_y", ApplyNamingConventions = false)]
|
||||
public double? LocationY { get; set; }
|
||||
|
||||
[YamlMember(Alias = "z_index", ApplyNamingConventions = false)]
|
||||
public int? ZIndex { get; set; }
|
||||
|
||||
public bool Scale { get; set; }
|
||||
|
||||
[YamlMember(Alias = "scale_width_percent", ApplyNamingConventions = false)]
|
||||
public double? ScaleWidthPercent { get; set; }
|
||||
|
||||
@@ -54,4 +59,4 @@ public class ImageGraphicsElement
|
||||
return Option<ImageGraphicsElement>.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
namespace ErsatzTV.Core.Health;
|
||||
|
||||
public record HealthCheckResult(string Title, HealthCheckStatus Status, string Message, string BriefMessage, Option<HealthCheckLink> Link);
|
||||
public record HealthCheckResult(
|
||||
string Title,
|
||||
HealthCheckStatus Status,
|
||||
string Message,
|
||||
string BriefMessage,
|
||||
Option<HealthCheckLink> Link);
|
||||
|
||||
@@ -3,4 +3,4 @@ using ErsatzTV.Core.Interfaces.Streaming;
|
||||
|
||||
namespace ErsatzTV.Core.Interfaces.FFmpeg;
|
||||
|
||||
public record PlayoutItemResult(Command Process, Option<GraphicsEngineContext> GraphicsEngineContext);
|
||||
public record PlayoutItemResult(Command Process, Option<GraphicsEngineContext> GraphicsEngineContext);
|
||||
|
||||
@@ -14,4 +14,4 @@ public interface IPlexTelevisionRepository : IMediaServerTelevisionRepository<Pl
|
||||
|
||||
public record PlexShowAddTagResult(Option<int> Existing, Option<int> Added);
|
||||
|
||||
public record PlexShowTitleKeyResult(string Title, string Key);
|
||||
public record PlexShowTitleKeyResult(string Title, string Key);
|
||||
|
||||
@@ -4,9 +4,9 @@ namespace ErsatzTV.Core.Interfaces.Repositories;
|
||||
|
||||
public interface ITemplateDataRepository
|
||||
{
|
||||
public Task<Option<Dictionary<string, object>>> GetMediaItemTemplateData(MediaItem mediaItem);
|
||||
Task<Option<Dictionary<string, object>>> GetMediaItemTemplateData(MediaItem mediaItem);
|
||||
|
||||
public Task<Option<Dictionary<string, object>>> GetEpgTemplateData(
|
||||
Task<Option<Dictionary<string, object>>> GetEpgTemplateData(
|
||||
string channelNumber,
|
||||
DateTimeOffset time,
|
||||
int count);
|
||||
|
||||
@@ -5,8 +5,8 @@ namespace ErsatzTV.Core.Interfaces.Scheduling;
|
||||
|
||||
public interface IPlayoutBuilder
|
||||
{
|
||||
public bool TrimStart { get; set; }
|
||||
public Playlist DebugPlaylist { get; set; }
|
||||
bool TrimStart { get; set; }
|
||||
Playlist DebugPlaylist { get; set; }
|
||||
|
||||
Task<PlayoutBuildResult> Build(
|
||||
Playout playout,
|
||||
|
||||
@@ -2,5 +2,5 @@ namespace ErsatzTV.Core.Interfaces.Scheduling;
|
||||
|
||||
public interface IPlayoutTimeShifter
|
||||
{
|
||||
public Task TimeShift(int playoutId, DateTimeOffset now, bool force);
|
||||
Task TimeShift(int playoutId, DateTimeOffset now, bool force);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace ErsatzTV.Core.Interfaces.Search;
|
||||
|
||||
public interface ISearchIndex : IDisposable
|
||||
{
|
||||
public int Version { get; }
|
||||
int Version { get; }
|
||||
Task<bool> IndexExists();
|
||||
Task<bool> Initialize(ILocalFileSystem localFileSystem, IConfigElementRepository configElementRepository);
|
||||
Task<Unit> Rebuild(ICachingSearchRepository searchRepository, IFallbackMetadataProvider fallbackMetadataProvider);
|
||||
|
||||
@@ -28,7 +28,9 @@ public record TextElementDataContext(TextGraphicsElement TextElement, Dictionary
|
||||
|
||||
public record ImageElementContext(ImageGraphicsElement ImageElement) : GraphicsElementContext;
|
||||
|
||||
public record SubtitleElementDataContext(SubtitlesGraphicsElement SubtitlesElement, Dictionary<string, string> Variables)
|
||||
public record SubtitleElementDataContext(
|
||||
SubtitlesGraphicsElement SubtitlesElement,
|
||||
Dictionary<string, string> Variables)
|
||||
: GraphicsElementContext, ITemplateDataContext
|
||||
{
|
||||
public int EpgEntries => SubtitlesElement.EpgEntries;
|
||||
|
||||
@@ -5,4 +5,4 @@ namespace ErsatzTV.Core.Interfaces.Streaming;
|
||||
public interface IGraphicsEngine
|
||||
{
|
||||
Task Run(GraphicsEngineContext context, PipeWriter pipeWriter, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public static class PathUtils
|
||||
{
|
||||
builder.Append(b.ToString("x2", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class BlockPlayoutBuilder(
|
||||
PlayoutBuildMode mode,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = PlayoutBuildResult.Empty;
|
||||
PlayoutBuildResult result = PlayoutBuildResult.Empty;
|
||||
|
||||
logger.LogDebug(
|
||||
"Building block playout {PlayoutId} for channel {ChannelNumber} - {ChannelName}",
|
||||
@@ -72,7 +72,8 @@ public class BlockPlayoutBuilder(
|
||||
BlockPlayoutChangeDetection.GetPlayoutItemToBlockKeyMap(referenceData);
|
||||
|
||||
// remove items without a block key (shouldn't happen often, just upgrades)
|
||||
foreach (var item in referenceData.ExistingItems.Where(i => i.FillerKind is not FillerKind.DecoDefault && !itemBlockKeys.ContainsKey(i)))
|
||||
foreach (PlayoutItem item in referenceData.ExistingItems.Where(i =>
|
||||
i.FillerKind is not FillerKind.DecoDefault && !itemBlockKeys.ContainsKey(i)))
|
||||
{
|
||||
result.ItemsToRemove.Add(item.Id);
|
||||
}
|
||||
@@ -327,9 +328,12 @@ public class BlockPlayoutBuilder(
|
||||
return $"{showTitle}s{e.Season.SeasonNumber:00}{numbersString} - {titlesString}";
|
||||
}
|
||||
|
||||
private static PlayoutBuildResult CleanUpHistory(PlayoutReferenceData referenceData, DateTimeOffset start, PlayoutBuildResult result)
|
||||
private static PlayoutBuildResult CleanUpHistory(
|
||||
PlayoutReferenceData referenceData,
|
||||
DateTimeOffset start,
|
||||
PlayoutBuildResult result)
|
||||
{
|
||||
var allItemsToDelete = referenceData.PlayoutHistory
|
||||
IEnumerable<PlayoutHistory> allItemsToDelete = referenceData.PlayoutHistory
|
||||
.Append(result.AddedHistory)
|
||||
.GroupBy(h => (h.BlockId, h.Key))
|
||||
.SelectMany(group => group
|
||||
|
||||
@@ -46,7 +46,8 @@ internal static class BlockPlayoutChangeDetection
|
||||
{
|
||||
foreach (PlayoutItem playoutItem in playoutItems)
|
||||
{
|
||||
if (!itemBlockKeys.TryGetValue(playoutItem, out var blockKey) || effectiveBlock.Block.Id != blockKey.b)
|
||||
if (!itemBlockKeys.TryGetValue(playoutItem, out BlockKey blockKey) ||
|
||||
effectiveBlock.Block.Id != blockKey.b)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class BlockPlayoutFillerBuilder(
|
||||
{
|
||||
// remove all playout items with type filler
|
||||
// except block items that are hidden from the guide (guide mode)
|
||||
foreach (var item in filteredExistingItems)
|
||||
foreach (PlayoutItem item in filteredExistingItems)
|
||||
{
|
||||
if (item.FillerKind is FillerKind.None or FillerKind.GuideMode)
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St
|
||||
private static EffectiveBlock NormalizeGuideMode(EffectiveBlock effectiveBlock)
|
||||
{
|
||||
if (effectiveBlock.Block.Items is not null &&
|
||||
effectiveBlock.Block.Items.All(bi => bi.IncludeInProgramGuide == false))
|
||||
effectiveBlock.Block.Items.All(bi => !bi.IncludeInProgramGuide))
|
||||
{
|
||||
foreach (BlockItem blockItem in effectiveBlock.Block.Items)
|
||||
{
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace ErsatzTV.Core.Scheduling;
|
||||
|
||||
public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnumerator
|
||||
{
|
||||
private readonly Lazy<Dictionary<int, int>> _lazyMediaItemGroupSize;
|
||||
private readonly Lazy<Option<TimeSpan>> _lazyMinimumDuration;
|
||||
private readonly List<MediaItem> _sortedMediaItems;
|
||||
private readonly Lazy<Dictionary<int, int>> _lazyMediaItemGroupSize;
|
||||
|
||||
public ChronologicalMediaCollectionEnumerator(
|
||||
IEnumerable<MediaItem> mediaItems,
|
||||
@@ -36,6 +36,21 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetState(CollectionEnumeratorState state) =>
|
||||
// seed doesn't matter in chronological
|
||||
State.Index = state.Index;
|
||||
|
||||
public CollectionEnumeratorState State { get; }
|
||||
|
||||
public Option<MediaItem> Current => _sortedMediaItems.Count != 0 ? _sortedMediaItems[State.Index] : None;
|
||||
public Option<bool> CurrentIncludeInProgramGuide { get; }
|
||||
|
||||
public void MoveNext() => State.Index = (State.Index + 1) % _sortedMediaItems.Count;
|
||||
|
||||
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;
|
||||
|
||||
public int Count => _sortedMediaItems.Count;
|
||||
|
||||
private Dictionary<int, int> CalculateMediaItemGroupSizes()
|
||||
{
|
||||
var result = new Dictionary<int, int>();
|
||||
@@ -54,21 +69,6 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ResetState(CollectionEnumeratorState state) =>
|
||||
// seed doesn't matter in chronological
|
||||
State.Index = state.Index;
|
||||
|
||||
public CollectionEnumeratorState State { get; }
|
||||
|
||||
public Option<MediaItem> Current => _sortedMediaItems.Count != 0 ? _sortedMediaItems[State.Index] : None;
|
||||
public Option<bool> CurrentIncludeInProgramGuide { get; }
|
||||
|
||||
public void MoveNext() => State.Index = (State.Index + 1) % _sortedMediaItems.Count;
|
||||
|
||||
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;
|
||||
|
||||
public int Count => _sortedMediaItems.Count;
|
||||
|
||||
public int GroupSizeForMediaItem(MediaItem mediaItem) =>
|
||||
_lazyMediaItemGroupSize.Value.GetValueOrDefault(mediaItem.Id, 1);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ namespace ErsatzTV.Core.Scheduling;
|
||||
|
||||
public static class FillerExpression
|
||||
{
|
||||
public static List<MediaChapter> FilterChapters(string fillerExpression, List<MediaChapter> effectiveChapters, PlayoutItem playoutItem)
|
||||
public static List<MediaChapter> FilterChapters(
|
||||
string fillerExpression,
|
||||
List<MediaChapter> effectiveChapters,
|
||||
PlayoutItem playoutItem)
|
||||
{
|
||||
if (effectiveChapters.Count == 0 || string.IsNullOrWhiteSpace(fillerExpression))
|
||||
{
|
||||
|
||||
@@ -14,5 +14,13 @@ public record PlayoutBuildResult(
|
||||
Option<DateTimeOffset> TimeShiftTo)
|
||||
{
|
||||
public static PlayoutBuildResult Empty =>
|
||||
new(false, Option<DateTimeOffset>.None, Option<DateTimeOffset>.None, [], [], [], [], Option<DateTimeOffset>.None);
|
||||
}
|
||||
new(
|
||||
false,
|
||||
Option<DateTimeOffset>.None,
|
||||
Option<DateTimeOffset>.None,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
Option<DateTimeOffset>.None);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
PlayoutBuildMode mode,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = PlayoutBuildResult.Empty;
|
||||
PlayoutBuildResult result = PlayoutBuildResult.Empty;
|
||||
|
||||
if (playout.ProgramSchedulePlayoutType is not ProgramSchedulePlayoutType.Classic)
|
||||
{
|
||||
@@ -117,7 +117,13 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
{
|
||||
foreach (PlayoutParameters parameters in await Validate(playout, referenceData))
|
||||
{
|
||||
result = await Build(playout, referenceData, result, mode, parameters with { Start = start, Finish = finish }, cancellationToken);
|
||||
result = await Build(
|
||||
playout,
|
||||
referenceData,
|
||||
result,
|
||||
mode,
|
||||
parameters with { Start = start, Finish = finish },
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -443,7 +449,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
{
|
||||
// check for future items that aren't grouped inside range
|
||||
var futureItems = result.AddedItems.Filter(i => i.StartOffset > trimAfter).ToList();
|
||||
var futureItemCount = futureItems.Count(futureItem =>
|
||||
int futureItemCount = futureItems.Count(futureItem =>
|
||||
result.AddedItems.All(i => i == futureItem || i.GuideGroup != futureItem.GuideGroup));
|
||||
|
||||
// it feels hacky to have to clean up a playlist like this,
|
||||
@@ -808,7 +814,8 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
playoutBuilderState.CurrentTime);
|
||||
|
||||
// if we ended in a different alternate schedule, fix the anchor data
|
||||
if (playoutBuilderState.CurrentTime > playoutFinish && activeScheduleAtAnchor.Id != activeSchedule.Id && activeScheduleAtAnchor.Items.Count > 0)
|
||||
if (playoutBuilderState.CurrentTime > playoutFinish && activeScheduleAtAnchor.Id != activeSchedule.Id &&
|
||||
activeScheduleAtAnchor.Items.Count > 0)
|
||||
{
|
||||
PlayoutBuilderState cleanState = playoutBuilderState with
|
||||
{
|
||||
@@ -875,7 +882,8 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
|
||||
private async Task<Map<CollectionKey, List<MediaItem>>> GetCollectionMediaItems(PlayoutReferenceData referenceData)
|
||||
{
|
||||
IEnumerable<KeyValuePair<CollectionKey, Option<FillerPreset>>> collectionKeys = GetAllCollectionKeys(referenceData);
|
||||
IEnumerable<KeyValuePair<CollectionKey, Option<FillerPreset>>> collectionKeys =
|
||||
GetAllCollectionKeys(referenceData);
|
||||
|
||||
IEnumerable<Task<KeyValuePair<CollectionKey, List<MediaItem>>>> tasks = collectionKeys.Select(async key =>
|
||||
{
|
||||
@@ -886,14 +894,13 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
return Map.createRange(await Task.WhenAll(tasks));
|
||||
}
|
||||
|
||||
private static IEnumerable<KeyValuePair<CollectionKey, Option<FillerPreset>>> GetAllCollectionKeys(PlayoutReferenceData referenceData)
|
||||
{
|
||||
return referenceData.ProgramSchedule.Items
|
||||
private static IEnumerable<KeyValuePair<CollectionKey, Option<FillerPreset>>> GetAllCollectionKeys(
|
||||
PlayoutReferenceData referenceData) =>
|
||||
referenceData.ProgramSchedule.Items
|
||||
.Append(referenceData.ProgramScheduleAlternates.Bind(psa => psa.ProgramSchedule.Items))
|
||||
.DistinctBy(item => item.Id)
|
||||
.SelectMany(CollectionKeysForItem)
|
||||
.DistinctBy(kvp => kvp.Key);
|
||||
}
|
||||
|
||||
private async Task<List<MediaItem>> FetchMediaItemsForKeyAsync(
|
||||
CollectionKey collectionKey,
|
||||
|
||||
@@ -286,7 +286,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
|
||||
|
||||
// missing pad-to-nearest-minute value is invalid; use no filler
|
||||
FillerPreset invalidPadFiller = allFiller
|
||||
.FirstOrDefault(f => f.FillerMode == FillerMode.Pad && f.PadToNearestMinute.HasValue == false);
|
||||
.FirstOrDefault(f => f.FillerMode == FillerMode.Pad && !f.PadToNearestMinute.HasValue);
|
||||
if (invalidPadFiller is not null)
|
||||
{
|
||||
Logger.LogError(
|
||||
@@ -340,7 +340,8 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
|
||||
// convert playlist filler
|
||||
if (allFiller.Any(f => f.CollectionType is ProgramScheduleItemCollectionType.Playlist))
|
||||
{
|
||||
var toRemove = allFiller.Filter(f => f.CollectionType is ProgramScheduleItemCollectionType.Playlist).ToList();
|
||||
var toRemove = allFiller.Filter(f => f.CollectionType is ProgramScheduleItemCollectionType.Playlist)
|
||||
.ToList();
|
||||
allFiller.RemoveAll(toRemove.Contains);
|
||||
|
||||
foreach (FillerPreset playlistFiller in toRemove)
|
||||
@@ -367,7 +368,8 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
|
||||
};
|
||||
|
||||
// if filler count is 2, we need to schedule 2 * (number of items in one full playlist iteration)
|
||||
var fillerEnumerator = enumerators[CollectionKey.ForFillerPreset(playlistFiller)];
|
||||
IMediaCollectionEnumerator fillerEnumerator =
|
||||
enumerators[CollectionKey.ForFillerPreset(playlistFiller)];
|
||||
if (fillerEnumerator is PlaylistEnumerator playlistEnumerator)
|
||||
{
|
||||
clone.Count *= playlistEnumerator.CountForFiller;
|
||||
@@ -428,7 +430,10 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
|
||||
foreach (FillerPreset filler in allFiller.Filter(f =>
|
||||
f.FillerKind == FillerKind.MidRoll && f.FillerMode != FillerMode.Pad))
|
||||
{
|
||||
List<MediaChapter> filteredChapters = FillerExpression.FilterChapters(filler.Expression, effectiveChapters, playoutItem);
|
||||
List<MediaChapter> filteredChapters = FillerExpression.FilterChapters(
|
||||
filler.Expression,
|
||||
effectiveChapters,
|
||||
playoutItem);
|
||||
if (filteredChapters.Count <= 1)
|
||||
{
|
||||
result.Add(playoutItem);
|
||||
@@ -797,7 +802,6 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
|
||||
|
||||
if (remainingToFill - itemDuration >= TimeSpan.Zero)
|
||||
{
|
||||
|
||||
var playoutItem = new PlayoutItem
|
||||
{
|
||||
PlayoutId = playoutBuilderState.PlayoutId,
|
||||
|
||||
@@ -55,7 +55,7 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche
|
||||
|
||||
if (itemStartTime >= nextState.DurationFinish.IfNone(SystemTime.MaxValueUtc) ||
|
||||
// don't start if the first item will already be after the hard stop
|
||||
(playoutItems.Count == 0 && itemStartTime >= hardStop))
|
||||
playoutItems.Count == 0 && itemStartTime >= hardStop)
|
||||
{
|
||||
nextState = nextState with { CurrentTime = hardStop };
|
||||
break;
|
||||
@@ -165,7 +165,8 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche
|
||||
PlayoutItemWatermarks = []
|
||||
};
|
||||
|
||||
foreach (var programScheduleItemWatermark in scheduleItem.ProgramScheduleItemWatermarks ?? [])
|
||||
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
|
||||
.ProgramScheduleItemWatermarks ?? [])
|
||||
{
|
||||
playoutItem.PlayoutItemWatermarks.Add(
|
||||
new PlayoutItemWatermark
|
||||
|
||||
@@ -84,7 +84,8 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
|
||||
PlayoutItemWatermarks = []
|
||||
};
|
||||
|
||||
foreach (var programScheduleItemWatermark in scheduleItem.ProgramScheduleItemWatermarks ?? [])
|
||||
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
|
||||
.ProgramScheduleItemWatermarks ?? [])
|
||||
{
|
||||
playoutItem.PlayoutItemWatermarks.Add(
|
||||
new PlayoutItemWatermark
|
||||
|
||||
@@ -60,6 +60,7 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche
|
||||
.Enumerator.Count
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
case MultipleMode.MultiEpisodeGroupSize:
|
||||
if (contentEnumerator is ChronologicalMediaCollectionEnumerator chronologicalEnumerator)
|
||||
@@ -72,6 +73,7 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -108,7 +110,8 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche
|
||||
PlayoutItemWatermarks = []
|
||||
};
|
||||
|
||||
foreach (var programScheduleItemWatermark in scheduleItem.ProgramScheduleItemWatermarks ?? [])
|
||||
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
|
||||
.ProgramScheduleItemWatermarks ?? [])
|
||||
{
|
||||
playoutItem.PlayoutItemWatermarks.Add(
|
||||
new PlayoutItemWatermark
|
||||
|
||||
@@ -55,7 +55,8 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI
|
||||
PlayoutItemWatermarks = []
|
||||
};
|
||||
|
||||
foreach (var programScheduleItemWatermark in scheduleItem.ProgramScheduleItemWatermarks ?? [])
|
||||
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
|
||||
.ProgramScheduleItemWatermarks ?? [])
|
||||
{
|
||||
playoutItem.PlayoutItemWatermarks.Add(
|
||||
new PlayoutItemWatermark
|
||||
|
||||
@@ -10,4 +10,4 @@ public record PlayoutReferenceData(
|
||||
List<PlayoutTemplate> PlayoutTemplates,
|
||||
ProgramSchedule ProgramSchedule,
|
||||
List<ProgramScheduleAlternate> ProgramScheduleAlternates,
|
||||
List<PlayoutHistory> PlayoutHistory);
|
||||
List<PlayoutHistory> PlayoutHistory);
|
||||
|
||||
@@ -185,9 +185,9 @@ public abstract class YamlPlayoutContentHandler(EnumeratorCache enumeratorCache)
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var midRollSequence in maybeMidRollSequence)
|
||||
foreach (YamlPlayoutContext.MidRollSequence midRollSequence in maybeMidRollSequence)
|
||||
{
|
||||
var filteredChapters = FillerExpression.FilterChapters(
|
||||
List<MediaChapter> filteredChapters = FillerExpression.FilterChapters(
|
||||
midRollSequence.Expression,
|
||||
itemChapters,
|
||||
playoutItem);
|
||||
@@ -201,7 +201,7 @@ public abstract class YamlPlayoutContentHandler(EnumeratorCache enumeratorCache)
|
||||
{
|
||||
for (var j = 0; j < filteredChapters.Count; j++)
|
||||
{
|
||||
var nextItem = playoutItem.ForChapter(filteredChapters[j]);
|
||||
PlayoutItem nextItem = playoutItem.ForChapter(filteredChapters[j]);
|
||||
|
||||
nextItem.Start = context.CurrentTime.UtcDateTime;
|
||||
nextItem.Finish = context.CurrentTime.UtcDateTime + (nextItem.OutPoint - nextItem.InPoint);
|
||||
|
||||
@@ -29,7 +29,7 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP
|
||||
return false;
|
||||
}
|
||||
|
||||
if (duration.StopBeforeEnd == false && duration.OfflineTail)
|
||||
if (!duration.StopBeforeEnd && duration.OfflineTail)
|
||||
{
|
||||
logger.LogError("offline_tail must be false when stop_before_end is false");
|
||||
return false;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var ge in await GetGraphicsElementByPath(graphicsOff.GraphicsOff))
|
||||
foreach (GraphicsElement ge in await GetGraphicsElementByPath(graphicsOff.GraphicsOff))
|
||||
{
|
||||
context.RemoveGraphicsElement(ge.Id);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl
|
||||
|
||||
private async Task<Option<GraphicsElement>> GetGraphicsElementByPath(string path)
|
||||
{
|
||||
if (_graphicsElementCache.TryGetValue(path, out var cachedGraphicsElement))
|
||||
if (_graphicsElementCache.TryGetValue(path, out Option<GraphicsElement> cachedGraphicsElement))
|
||||
{
|
||||
foreach (GraphicsElement graphicsElement in cachedGraphicsElement)
|
||||
{
|
||||
@@ -61,4 +61,4 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl
|
||||
|
||||
return Option<GraphicsElement>.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var ge in await GetGraphicsElementByPath(graphicsOn.GraphicsOn))
|
||||
foreach (GraphicsElement ge in await GetGraphicsElementByPath(graphicsOn.GraphicsOn))
|
||||
{
|
||||
string variables = null;
|
||||
if (graphicsOn.Variables.Count > 0)
|
||||
@@ -51,7 +51,7 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle
|
||||
|
||||
private async Task<Option<GraphicsElement>> GetGraphicsElementByPath(string path)
|
||||
{
|
||||
if (_graphicsElementCache.TryGetValue(path, out var cachedGraphicsElement))
|
||||
if (_graphicsElementCache.TryGetValue(path, out Option<GraphicsElement> cachedGraphicsElement))
|
||||
{
|
||||
foreach (GraphicsElement graphicsElement in cachedGraphicsElement)
|
||||
{
|
||||
@@ -71,4 +71,4 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle
|
||||
|
||||
return Option<GraphicsElement>.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user