Browse Source

template playout => yaml playout (#1813)

pull/1814/head
Jason Dove 1 year ago committed by GitHub
parent
commit
fa20c5e01e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      CHANGELOG.md
  2. 10
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  3. 4
      ErsatzTV.Application/Playouts/Commands/CreatePlayout.cs
  4. 28
      ErsatzTV.Application/Playouts/Commands/CreateYamlPlayoutHandler.cs
  5. 10
      ErsatzTV.Application/Playouts/Commands/UpdateTemplatePlayoutHandler.cs
  6. 2
      ErsatzTV.Application/Playouts/Commands/UpdateYamlPlayout.cs
  7. 2
      ErsatzTV.Core/Domain/ProgramSchedulePlayoutType.cs
  8. 2
      ErsatzTV.Core/Interfaces/Scheduling/IYamlPlayoutBuilder.cs
  9. 7
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplate.cs
  10. 7
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentGuid.cs
  11. 7
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentItem.cs
  12. 7
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentSearchItem.cs
  13. 7
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentShowItem.cs
  14. 6
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateCountItem.cs
  15. 6
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateItem.cs
  16. 5
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplatePlayout.cs
  17. 6
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateRepeatItem.cs
  18. 76
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs
  19. 7
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentGuid.cs
  20. 7
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentItem.cs
  21. 7
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentSearchItem.cs
  22. 7
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentShowItem.cs
  23. 6
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutCountInstruction.cs
  24. 7
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDefinition.cs
  25. 4
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDurationInstruction.cs
  26. 6
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutInstruction.cs
  27. 4
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutPadToNextInstruction.cs
  28. 6
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutRepeatInstruction.cs
  29. 4
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutScheduler.cs
  30. 6
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutSchedulerCount.cs
  31. 6
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutSchedulerDuration.cs
  32. 6
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutSchedulerPadToNext.cs
  33. 8
      ErsatzTV/Pages/PlayoutEditor.razor
  34. 26
      ErsatzTV/Pages/Playouts.razor
  35. 2
      ErsatzTV/PlayoutKind.cs
  36. 14
      ErsatzTV/Shared/EditYamlFileDialog.razor
  37. 4
      ErsatzTV/Startup.cs
  38. 4
      ErsatzTV/ViewModels/PlayoutEditViewModel.cs

5
CHANGELOG.md

@ -22,13 +22,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -22,13 +22,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Default filler will stop scheduling when the next item would extend into primary content
- Alternatively, default filler can be configured to `Trim To Fit`
- In this case, the last item that would extend into primary content is trimmed to end exactly when the primary content starts
- Add **experimental** playout type `Template`
- Add **experimental** playout type `YAML`
- This playout type uses a YAML file to declare content and describe how the playout should be built
- Content currently supports search queries
- Playout instructions currently include `count`, `pad to next`, and `repeat`
- `count`: add the specified number of items (from the referenced content) to the playout
- `duration`: play the referenced content for the specified duration
- `pad to next`: add items from the referenced content until the wall clock is a multiple of the specified minutes value
- `repeat`: continue building the playout from the first instruction in the template
- `repeat`: continue building the playout from the first instruction in the YAML file
### Fixed
- Add basic cache busting to XMLTV image URLs

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

@ -19,7 +19,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -19,7 +19,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
{
private readonly IBlockPlayoutBuilder _blockPlayoutBuilder;
private readonly IBlockPlayoutFillerBuilder _blockPlayoutFillerBuilder;
private readonly ITemplatePlayoutBuilder _templatePlayoutBuilder;
private readonly IYamlPlayoutBuilder _yamlPlayoutBuilder;
private readonly IClient _client;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IEntityLocker _entityLocker;
@ -34,7 +34,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -34,7 +34,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
IPlayoutBuilder playoutBuilder,
IBlockPlayoutBuilder blockPlayoutBuilder,
IBlockPlayoutFillerBuilder blockPlayoutFillerBuilder,
ITemplatePlayoutBuilder templatePlayoutBuilder,
IYamlPlayoutBuilder yamlPlayoutBuilder,
IExternalJsonPlayoutBuilder externalJsonPlayoutBuilder,
IFFmpegSegmenterService ffmpegSegmenterService,
IEntityLocker entityLocker,
@ -45,7 +45,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -45,7 +45,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
_playoutBuilder = playoutBuilder;
_blockPlayoutBuilder = blockPlayoutBuilder;
_blockPlayoutFillerBuilder = blockPlayoutFillerBuilder;
_templatePlayoutBuilder = templatePlayoutBuilder;
_yamlPlayoutBuilder = yamlPlayoutBuilder;
_externalJsonPlayoutBuilder = externalJsonPlayoutBuilder;
_ffmpegSegmenterService = ffmpegSegmenterService;
_entityLocker = entityLocker;
@ -77,8 +77,8 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -77,8 +77,8 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
await _blockPlayoutBuilder.Build(playout, request.Mode, cancellationToken);
await _blockPlayoutFillerBuilder.Build(playout, request.Mode, cancellationToken);
break;
case ProgramSchedulePlayoutType.Template:
await _templatePlayoutBuilder.Build(playout, request.Mode, cancellationToken);
case ProgramSchedulePlayoutType.Yaml:
await _yamlPlayoutBuilder.Build(playout, request.Mode, cancellationToken);
break;
case ProgramSchedulePlayoutType.ExternalJson:
await _externalJsonPlayoutBuilder.Build(playout, request.Mode, cancellationToken);

4
ErsatzTV.Application/Playouts/Commands/CreatePlayout.cs

@ -12,8 +12,8 @@ public record CreateFloodPlayout(int ChannelId, int ProgramScheduleId) @@ -12,8 +12,8 @@ public record CreateFloodPlayout(int ChannelId, int ProgramScheduleId)
public record CreateBlockPlayout(int ChannelId)
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.Block);
public record CreateTemplatePlayout(int ChannelId, string TemplateFile)
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.Template);
public record CreateYamlPlayout(int ChannelId, string TemplateFile)
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.Yaml);
public record CreateExternalJsonPlayout(int ChannelId, string ExternalJsonFile)
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.ExternalJson);

28
ErsatzTV.Application/Playouts/Commands/CreateTemplatePlayoutHandler.cs → ErsatzTV.Application/Playouts/Commands/CreateYamlPlayoutHandler.cs

@ -11,14 +11,14 @@ using Channel = ErsatzTV.Core.Domain.Channel; @@ -11,14 +11,14 @@ using Channel = ErsatzTV.Core.Domain.Channel;
namespace ErsatzTV.Application.Playouts;
public class CreateTemplatePlayoutHandler
: IRequestHandler<CreateTemplatePlayout, Either<BaseError, CreatePlayoutResponse>>
public class CreateYamlPlayoutHandler
: IRequestHandler<CreateYamlPlayout, Either<BaseError, CreatePlayoutResponse>>
{
private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ILocalFileSystem _localFileSystem;
public CreateTemplatePlayoutHandler(
public CreateYamlPlayoutHandler(
ILocalFileSystem localFileSystem,
ChannelWriter<IBackgroundServiceRequest> channel,
IDbContextFactory<TvContext> dbContextFactory)
@ -29,7 +29,7 @@ public class CreateTemplatePlayoutHandler @@ -29,7 +29,7 @@ public class CreateTemplatePlayoutHandler
}
public async Task<Either<BaseError, CreatePlayoutResponse>> Handle(
CreateTemplatePlayout request,
CreateYamlPlayout request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
@ -48,8 +48,8 @@ public class CreateTemplatePlayoutHandler @@ -48,8 +48,8 @@ public class CreateTemplatePlayoutHandler
private async Task<Validation<BaseError, Playout>> Validate(
TvContext dbContext,
CreateTemplatePlayout request) =>
(await ValidateChannel(dbContext, request), ValidateTemplateFile(request), ValidatePlayoutType(request))
CreateYamlPlayout request) =>
(await ValidateChannel(dbContext, request), ValidateYamlFile(request), ValidatePlayoutType(request))
.Apply(
(channel, externalJsonFile, playoutType) => new Playout
{
@ -60,10 +60,10 @@ public class CreateTemplatePlayoutHandler @@ -60,10 +60,10 @@ public class CreateTemplatePlayoutHandler
private static Task<Validation<BaseError, Channel>> ValidateChannel(
TvContext dbContext,
CreateTemplatePlayout createTemplatePlayout) =>
CreateYamlPlayout createYamlPlayout) =>
dbContext.Channels
.Include(c => c.Playouts)
.SelectOneAsync(c => c.Id, c => c.Id == createTemplatePlayout.ChannelId)
.SelectOneAsync(c => c.Id, c => c.Id == createYamlPlayout.ChannelId)
.Map(o => o.ToValidation<BaseError>("Channel does not exist"))
.BindT(ChannelMustNotHavePlayouts);
@ -73,19 +73,19 @@ public class CreateTemplatePlayoutHandler @@ -73,19 +73,19 @@ public class CreateTemplatePlayoutHandler
.Map(_ => channel)
.ToValidation<BaseError>("Channel already has one playout");
private Validation<BaseError, string> ValidateTemplateFile(CreateTemplatePlayout request)
private Validation<BaseError, string> ValidateYamlFile(CreateYamlPlayout request)
{
if (!_localFileSystem.FileExists(request.TemplateFile))
{
return BaseError.New("Template file does not exist!");
return BaseError.New("YAML file does not exist!");
}
return request.TemplateFile;
}
private static Validation<BaseError, ProgramSchedulePlayoutType> ValidatePlayoutType(
CreateTemplatePlayout createTemplatePlayout) =>
Optional(createTemplatePlayout.ProgramSchedulePlayoutType)
.Filter(playoutType => playoutType == ProgramSchedulePlayoutType.Template)
.ToValidation<BaseError>("[ProgramSchedulePlayoutType] must be Template");
CreateYamlPlayout createYamlPlayout) =>
Optional(createYamlPlayout.ProgramSchedulePlayoutType)
.Filter(playoutType => playoutType == ProgramSchedulePlayoutType.Yaml)
.ToValidation<BaseError>("[ProgramSchedulePlayoutType] must be YAML");
}

10
ErsatzTV.Application/Playouts/Commands/UpdateTemplatePlayoutHandler.cs

@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore; @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Playouts;
public class
UpdateTemplatePlayoutHandler : IRequestHandler<UpdateTemplatePlayout,
UpdateTemplatePlayoutHandler : IRequestHandler<UpdateYamlPlayout,
Either<BaseError, PlayoutNameViewModel>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
@ -24,7 +24,7 @@ public class @@ -24,7 +24,7 @@ public class
}
public async Task<Either<BaseError, PlayoutNameViewModel>> Handle(
UpdateTemplatePlayout request,
UpdateYamlPlayout request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
@ -34,7 +34,7 @@ public class @@ -34,7 +34,7 @@ public class
private async Task<PlayoutNameViewModel> ApplyUpdateRequest(
TvContext dbContext,
UpdateTemplatePlayout request,
UpdateYamlPlayout request,
Playout playout)
{
playout.TemplateFile = request.TemplateFile;
@ -58,12 +58,12 @@ public class @@ -58,12 +58,12 @@ public class
private static Task<Validation<BaseError, Playout>> Validate(
TvContext dbContext,
UpdateTemplatePlayout request) =>
UpdateYamlPlayout request) =>
PlayoutMustExist(dbContext, request);
private static Task<Validation<BaseError, Playout>> PlayoutMustExist(
TvContext dbContext,
UpdateTemplatePlayout updatePlayout) =>
UpdateYamlPlayout updatePlayout) =>
dbContext.Playouts
.Include(p => p.Channel)
.SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId)

2
ErsatzTV.Application/Playouts/Commands/UpdateTemplatePlayout.cs → ErsatzTV.Application/Playouts/Commands/UpdateYamlPlayout.cs

@ -2,5 +2,5 @@ @@ -2,5 +2,5 @@
namespace ErsatzTV.Application.Playouts;
public record UpdateTemplatePlayout(int PlayoutId, string TemplateFile)
public record UpdateYamlPlayout(int PlayoutId, string TemplateFile)
: IRequest<Either<BaseError, PlayoutNameViewModel>>;

2
ErsatzTV.Core/Domain/ProgramSchedulePlayoutType.cs

@ -5,7 +5,7 @@ public enum ProgramSchedulePlayoutType @@ -5,7 +5,7 @@ public enum ProgramSchedulePlayoutType
None = 0,
Flood = 1,
Block = 2,
Template = 3,
Yaml = 3,
ExternalJson = 20
}

2
ErsatzTV.Core/Interfaces/Scheduling/ITemplatePlayoutBuilder.cs → ErsatzTV.Core/Interfaces/Scheduling/IYamlPlayoutBuilder.cs

@ -3,7 +3,7 @@ using ErsatzTV.Core.Scheduling; @@ -3,7 +3,7 @@ using ErsatzTV.Core.Scheduling;
namespace ErsatzTV.Core.Interfaces.Scheduling;
public interface ITemplatePlayoutBuilder
public interface IYamlPlayoutBuilder
{
Task<Playout> Build(Playout playout, PlayoutBuildMode mode, CancellationToken cancellationToken);
}

7
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplate.cs

@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplate
{
public List<PlayoutTemplateContentItem> Content { get; set; } = [];
public List<PlayoutTemplateItem> Playout { get; set; } = [];
}

7
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentGuid.cs

@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplateContentGuid
{
public string Source { get; set; }
public string Value { get; set; }
}

7
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentItem.cs

@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplateContentItem
{
public string Key { get; set; }
public string Order { get; set; }
}

7
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentSearchItem.cs

@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplateContentSearchItem : PlayoutTemplateContentItem
{
public string Search { get; set; }
public string Query { get; set; }
}

7
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateContentShowItem.cs

@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplateContentShowItem : PlayoutTemplateContentItem
{
public string Show { get; set; }
public List<PlayoutTemplateContentGuid> Guids { get; set; } = [];
}

6
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateCountItem.cs

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplateCountItem : PlayoutTemplateItem
{
public int Count { get; set; }
}

6
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateItem.cs

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplateItem
{
public string Content { get; set; }
}

5
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplatePlayout.cs

@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplatePlayout
{
}

6
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateRepeatItem.cs

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
public class PlayoutTemplateRepeatItem : PlayoutTemplateItem
{
public bool Repeat { get; set; }
}

76
ErsatzTV.Core/Scheduling/TemplateScheduling/TemplatePlayoutBuilder.cs → ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs

@ -7,24 +7,24 @@ using Microsoft.Extensions.Logging; @@ -7,24 +7,24 @@ using Microsoft.Extensions.Logging;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class TemplatePlayoutBuilder(
public class YamlPlayoutBuilder(
ILocalFileSystem localFileSystem,
IConfigElementRepository configElementRepository,
IMediaCollectionRepository mediaCollectionRepository,
ILogger<TemplatePlayoutBuilder> logger)
: ITemplatePlayoutBuilder
ILogger<YamlPlayoutBuilder> logger)
: IYamlPlayoutBuilder
{
public async Task<Playout> Build(Playout playout, PlayoutBuildMode mode, CancellationToken cancellationToken)
{
if (!localFileSystem.FileExists(playout.TemplateFile))
{
logger.LogWarning("Playout template file {File} does not exist; aborting.", playout.TemplateFile);
logger.LogWarning("YAML playout file {File} does not exist; aborting.", playout.TemplateFile);
return playout;
}
PlayoutTemplate playoutTemplate = await LoadTemplate(playout, cancellationToken);
YamlPlayoutDefinition playoutDefinition = await LoadYamlDefinition(playout, cancellationToken);
DateTimeOffset start = DateTimeOffset.Now;
int daysToBuild = await GetDaysToBuild();
@ -32,7 +32,7 @@ public class TemplatePlayoutBuilder( @@ -32,7 +32,7 @@ public class TemplatePlayoutBuilder(
if (mode is not PlayoutBuildMode.Reset)
{
logger.LogWarning("Template playouts can only be reset; ignoring build mode {Mode}", mode.ToString());
logger.LogWarning("YAML playouts can only be reset; ignoring build mode {Mode}", mode.ToString());
return playout;
}
@ -50,16 +50,16 @@ public class TemplatePlayoutBuilder( @@ -50,16 +50,16 @@ public class TemplatePlayoutBuilder(
var index = 0;
while (currentTime < finish)
{
if (index >= playoutTemplate.Playout.Count)
if (index >= playoutDefinition.Playout.Count)
{
logger.LogInformation("Reached the end of the playout template; stopping");
logger.LogInformation("Reached the end of the YAML playout definition; stopping");
break;
}
PlayoutTemplateItem playoutItem = playoutTemplate.Playout[index];
YamlPlayoutInstruction playoutItem = playoutDefinition.Playout[index];
// repeat resets index into template playout
if (playoutItem is PlayoutTemplateRepeatItem)
// repeat resets index into YAML playout
if (playoutItem is YamlPlayoutRepeatInstruction)
{
index = 0;
if (playout.Items.Count == itemsAfterRepeat)
@ -74,7 +74,7 @@ public class TemplatePlayoutBuilder( @@ -74,7 +74,7 @@ public class TemplatePlayoutBuilder(
Option<IMediaCollectionEnumerator> maybeEnumerator = await GetCachedEnumeratorForContent(
playout,
playoutTemplate,
playoutDefinition,
enumerators,
playoutItem.Content,
cancellationToken);
@ -92,31 +92,31 @@ public class TemplatePlayoutBuilder( @@ -92,31 +92,31 @@ public class TemplatePlayoutBuilder(
{
switch (playoutItem)
{
case PlayoutTemplateCountItem count:
currentTime = PlayoutTemplateSchedulerCount.Schedule(playout, currentTime, count, enumerator);
case YamlPlayoutCountInstruction count:
currentTime = YamlPlayoutSchedulerCount.Schedule(playout, currentTime, count, enumerator);
break;
case PlayoutTemplateDurationItem duration:
case YamlPlayoutDurationInstruction duration:
Option<IMediaCollectionEnumerator> durationFallbackEnumerator = await GetCachedEnumeratorForContent(
playout,
playoutTemplate,
playoutDefinition,
enumerators,
duration.Fallback,
cancellationToken);
currentTime = PlayoutTemplateSchedulerDuration.Schedule(
currentTime = YamlPlayoutSchedulerDuration.Schedule(
playout,
currentTime,
duration,
enumerator,
durationFallbackEnumerator);
break;
case PlayoutTemplatePadToNextItem padToNext:
case YamlPlayoutPadToNextInstruction padToNext:
Option<IMediaCollectionEnumerator> fallbackEnumerator = await GetCachedEnumeratorForContent(
playout,
playoutTemplate,
playoutDefinition,
enumerators,
padToNext.Fallback,
cancellationToken);
currentTime = PlayoutTemplateSchedulerPadToNext.Schedule(
currentTime = YamlPlayoutSchedulerPadToNext.Schedule(
playout,
currentTime,
padToNext,
@ -139,7 +139,7 @@ public class TemplatePlayoutBuilder( @@ -139,7 +139,7 @@ public class TemplatePlayoutBuilder(
private async Task<Option<IMediaCollectionEnumerator>> GetCachedEnumeratorForContent(
Playout playout,
PlayoutTemplate playoutTemplate,
YamlPlayoutDefinition playoutDefinition,
Dictionary<string, IMediaCollectionEnumerator> enumerators,
string contentKey,
CancellationToken cancellationToken)
@ -152,7 +152,7 @@ public class TemplatePlayoutBuilder( @@ -152,7 +152,7 @@ public class TemplatePlayoutBuilder(
if (!enumerators.TryGetValue(contentKey, out IMediaCollectionEnumerator enumerator))
{
Option<IMediaCollectionEnumerator> maybeEnumerator =
await GetEnumeratorForContent(playout, contentKey, playoutTemplate, cancellationToken);
await GetEnumeratorForContent(playout, contentKey, playoutDefinition, cancellationToken);
if (maybeEnumerator.IsNone)
{
@ -172,10 +172,10 @@ public class TemplatePlayoutBuilder( @@ -172,10 +172,10 @@ public class TemplatePlayoutBuilder(
private async Task<Option<IMediaCollectionEnumerator>> GetEnumeratorForContent(
Playout playout,
string contentKey,
PlayoutTemplate playoutTemplate,
YamlPlayoutDefinition playoutDefinition,
CancellationToken cancellationToken)
{
int index = playoutTemplate.Content.FindIndex(c => c.Key == contentKey);
int index = playoutDefinition.Content.FindIndex(c => c.Key == contentKey);
if (index < 0)
{
return Option<IMediaCollectionEnumerator>.None;
@ -183,13 +183,13 @@ public class TemplatePlayoutBuilder( @@ -183,13 +183,13 @@ public class TemplatePlayoutBuilder(
List<MediaItem> items = [];
PlayoutTemplateContentItem content = playoutTemplate.Content[index];
YamlPlayoutContentItem content = playoutDefinition.Content[index];
switch (content)
{
case PlayoutTemplateContentSearchItem search:
case YamlPlayoutContentSearchItem search:
items = await mediaCollectionRepository.GetSmartCollectionItems(search.Query);
break;
case PlayoutTemplateContentShowItem show:
case YamlPlayoutContentShowItem show:
items = await mediaCollectionRepository.GetShowItemsByShowGuids(
show.Guids.Map(g => $"{g.Source}://{g.Value}").ToList());
break;
@ -209,7 +209,7 @@ public class TemplatePlayoutBuilder( @@ -209,7 +209,7 @@ public class TemplatePlayoutBuilder(
return Option<IMediaCollectionEnumerator>.None;
}
private static async Task<PlayoutTemplate> LoadTemplate(Playout playout, CancellationToken cancellationToken)
private static async Task<YamlPlayoutDefinition> LoadYamlDefinition(Playout playout, CancellationToken cancellationToken)
{
string yaml = await File.ReadAllTextAsync(playout.TemplateFile, cancellationToken);
@ -220,24 +220,24 @@ public class TemplatePlayoutBuilder( @@ -220,24 +220,24 @@ public class TemplatePlayoutBuilder(
{
var contentKeyMappings = new Dictionary<string, Type>
{
{ "search", typeof(PlayoutTemplateContentSearchItem) },
{ "show", typeof(PlayoutTemplateContentShowItem) }
{ "search", typeof(YamlPlayoutContentSearchItem) },
{ "show", typeof(YamlPlayoutContentShowItem) }
};
o.AddUniqueKeyTypeDiscriminator<PlayoutTemplateContentItem>(contentKeyMappings);
o.AddUniqueKeyTypeDiscriminator<YamlPlayoutContentItem>(contentKeyMappings);
var instructionKeyMappings = new Dictionary<string, Type>
{
{ "count", typeof(PlayoutTemplateCountItem) },
{ "duration", typeof(PlayoutTemplateDurationItem) },
{ "pad_to_next", typeof(PlayoutTemplatePadToNextItem) },
{ "repeat", typeof(PlayoutTemplateRepeatItem) }
{ "count", typeof(YamlPlayoutCountInstruction) },
{ "duration", typeof(YamlPlayoutDurationInstruction) },
{ "pad_to_next", typeof(YamlPlayoutPadToNextInstruction) },
{ "repeat", typeof(YamlPlayoutRepeatInstruction) }
};
o.AddUniqueKeyTypeDiscriminator<PlayoutTemplateItem>(instructionKeyMappings);
o.AddUniqueKeyTypeDiscriminator<YamlPlayoutInstruction>(instructionKeyMappings);
})
.Build();
return deserializer.Deserialize<PlayoutTemplate>(yaml);
return deserializer.Deserialize<YamlPlayoutDefinition>(yaml);
}
}

7
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentGuid.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutContentGuid
{
public string Source { get; set; }
public string Value { get; set; }
}

7
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentItem.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutContentItem
{
public string Key { get; set; }
public string Order { get; set; }
}

7
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentSearchItem.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutContentSearchItem : YamlPlayoutContentItem
{
public string Search { get; set; }
public string Query { get; set; }
}

7
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContentShowItem.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutContentShowItem : YamlPlayoutContentItem
{
public string Show { get; set; }
public List<YamlPlayoutContentGuid> Guids { get; set; } = [];
}

6
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutCountInstruction.cs

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutCountInstruction : YamlPlayoutInstruction
{
public int Count { get; set; }
}

7
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDefinition.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutDefinition
{
public List<YamlPlayoutContentItem> Content { get; set; } = [];
public List<YamlPlayoutInstruction> Playout { get; set; } = [];
}

4
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateDurationItem.cs → ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDurationInstruction.cs

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class PlayoutTemplateDurationItem : PlayoutTemplateItem
public class YamlPlayoutDurationInstruction : YamlPlayoutInstruction
{
public string Duration { get; set; }

6
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutInstruction.cs

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutInstruction
{
public string Content { get; set; }
}

4
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplatePadToNextItem.cs → ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutPadToNextInstruction.cs

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class PlayoutTemplatePadToNextItem : PlayoutTemplateItem
public class YamlPlayoutPadToNextInstruction : YamlPlayoutInstruction
{
[YamlMember(Alias = "pad_to_next", ApplyNamingConventions = false)]
public int PadToNext { get; set; }

6
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutRepeatInstruction.cs

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class YamlPlayoutRepeatInstruction : YamlPlayoutInstruction
{
public bool Repeat { get; set; }
}

4
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateScheduler.cs → ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutScheduler.cs

@ -1,9 +1,9 @@ @@ -1,9 +1,9 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public abstract class PlayoutTemplateScheduler
public abstract class YamlPlayoutScheduler
{
protected static TimeSpan DurationForMediaItem(MediaItem mediaItem)
{

6
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateSchedulerCount.cs → ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutSchedulerCount.cs

@ -2,14 +2,14 @@ using ErsatzTV.Core.Domain; @@ -2,14 +2,14 @@ using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.Scheduling;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class PlayoutTemplateSchedulerCount : PlayoutTemplateScheduler
public class YamlPlayoutSchedulerCount : YamlPlayoutScheduler
{
public static DateTimeOffset Schedule(
Playout playout,
DateTimeOffset currentTime,
PlayoutTemplateCountItem count,
YamlPlayoutCountInstruction count,
IMediaCollectionEnumerator enumerator)
{
for (int i = 0; i < count.Count; i++)

6
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateSchedulerDuration.cs → ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutSchedulerDuration.cs

@ -3,14 +3,14 @@ using ErsatzTV.Core.Domain.Filler; @@ -3,14 +3,14 @@ using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.Scheduling;
using TimeSpanParserUtil;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class PlayoutTemplateSchedulerDuration : PlayoutTemplateScheduler
public class YamlPlayoutSchedulerDuration : YamlPlayoutScheduler
{
public static DateTimeOffset Schedule(
Playout playout,
DateTimeOffset currentTime,
PlayoutTemplateDurationItem duration,
YamlPlayoutDurationInstruction duration,
IMediaCollectionEnumerator enumerator,
Option<IMediaCollectionEnumerator> fallbackEnumerator)
{

6
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateSchedulerPadToNext.cs → ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutSchedulerPadToNext.cs

@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Scheduling;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling;
public class PlayoutTemplateSchedulerPadToNext : PlayoutTemplateSchedulerDuration
public class YamlPlayoutSchedulerPadToNext : YamlPlayoutSchedulerDuration
{
public static DateTimeOffset Schedule(
Playout playout,
DateTimeOffset currentTime,
PlayoutTemplatePadToNextItem padToNext,
YamlPlayoutPadToNextInstruction padToNext,
IMediaCollectionEnumerator enumerator,
Option<IMediaCollectionEnumerator> fallbackEnumerator)
{

8
ErsatzTV/Pages/PlayoutEditor.razor

@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
case PlayoutKind.ExternalJson:
<span>Add External Json Playout</span>
break;
case PlayoutKind.Template:
<span>Add Template Playout</span>
case PlayoutKind.Yaml:
<span>Add YAML Playout</span>
break;
case PlayoutKind.Block:
<span>Add Block Playout</span>
@ -47,8 +47,8 @@ @@ -47,8 +47,8 @@
case PlayoutKind.ExternalJson:
<MudTextField Label="External Json File" @bind-Value="_model.ExternalJsonFile" For="@(() => _model.ExternalJsonFile)"/>
break;
case PlayoutKind.Template:
<MudTextField Label="Template File" @bind-Value="_model.TemplateFile" For="@(() => _model.TemplateFile)"/>
case PlayoutKind.Yaml:
<MudTextField Label="YAML File" @bind-Value="_model.YamlFile" For="@(() => _model.YamlFile)"/>
break;
default:
<MudSelect Class="mt-3"

26
ErsatzTV/Pages/Playouts.razor

@ -19,8 +19,8 @@ @@ -19,8 +19,8 @@
</MudButton>
</MudTooltip>
<MudTooltip Text="This feature is experimental">
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Warning" Href="@($"playouts/add/{PlayoutKind.Template}")">
Add Template Playout
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Warning" Href="@($"playouts/add/{PlayoutKind.Yaml}")">
Add YAML Playout
</MudButton>
</MudTooltip>
<MudTooltip Text="This feature is experimental">
@ -68,8 +68,8 @@ @@ -68,8 +68,8 @@
case ProgramSchedulePlayoutType.Block:
<span>Block</span>
break;
case ProgramSchedulePlayoutType.Template:
<span>Template</span>
case ProgramSchedulePlayoutType.Yaml:
<span>YAML</span>
break;
case ProgramSchedulePlayoutType.ExternalJson:
<span>External Json</span>
@ -131,12 +131,12 @@ @@ -131,12 +131,12 @@
<div style="width: 48px"></div>
<div style="width: 48px"></div>
}
else if (context.PlayoutType == ProgramSchedulePlayoutType.Template)
else if (context.PlayoutType == ProgramSchedulePlayoutType.Yaml)
{
<MudTooltip Text="Edit Template File">
<MudTooltip Text="Edit YAML File">
<MudIconButton Icon="@Icons.Material.Filled.Edit"
Disabled="@EntityLocker.IsPlayoutLocked(context.PlayoutId)"
OnClick="@(_ => EditTemplateFile(context))">
OnClick="@(_ => EditYamlFile(context))">
</MudIconButton>
</MudTooltip>
<MudTooltip Text="Reset Playout">
@ -256,8 +256,8 @@ @@ -256,8 +256,8 @@
private async Task PlayoutSelected(PlayoutNameViewModel playout)
{
// only show details for flood, block and template playouts
_selectedPlayoutId = playout.PlayoutType is ProgramSchedulePlayoutType.Flood or ProgramSchedulePlayoutType.Block or ProgramSchedulePlayoutType.Template
// only show details for flood, block and YAML playouts
_selectedPlayoutId = playout.PlayoutType is ProgramSchedulePlayoutType.Flood or ProgramSchedulePlayoutType.Block or ProgramSchedulePlayoutType.Yaml
? playout.PlayoutId
: null;
@ -286,16 +286,16 @@ @@ -286,16 +286,16 @@
}
}
private async Task EditTemplateFile(PlayoutNameViewModel playout)
private async Task EditYamlFile(PlayoutNameViewModel playout)
{
var parameters = new DialogParameters { { "TemplateFile", $"{playout.TemplateFile}" } };
var parameters = new DialogParameters { { "YamlFile", $"{playout.TemplateFile}" } };
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraLarge };
IDialogReference dialog = await Dialog.ShowAsync<EditTemplateFileDialog>("Edit Template File", parameters, options);
IDialogReference dialog = await Dialog.ShowAsync<EditYamlFileDialog>("Edit YAML File", parameters, options);
DialogResult result = await dialog.Result;
if (!result.Canceled)
{
await Mediator.Send(new UpdateTemplatePlayout(playout.PlayoutId, result.Data as string ?? playout.TemplateFile), _cts.Token);
await Mediator.Send(new UpdateYamlPlayout(playout.PlayoutId, result.Data as string ?? playout.TemplateFile), _cts.Token);
if (_table != null)
{
await _table.ReloadServerData();

2
ErsatzTV/PlayoutKind.cs

@ -3,6 +3,6 @@ namespace ErsatzTV; @@ -3,6 +3,6 @@ namespace ErsatzTV;
public static class PlayoutKind
{
public const string ExternalJson = "externaljson";
public const string Template = "template";
public const string Yaml = "yaml";
public const string Block = "block";
}

14
ErsatzTV/Shared/EditTemplateFileDialog.razor → ErsatzTV/Shared/EditYamlFileDialog.razor

@ -4,14 +4,14 @@ @@ -4,14 +4,14 @@
<DialogContent>
<MudContainer Class="mb-6">
<MudText>
Edit the playout's template file
Edit the playout's YAML file
</MudText>
</MudContainer>
<MudTextField Label="Template File" @bind-Value="_templateFile"/>
<MudTextField Label="YAML File" @bind-Value="_yamlFile"/>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel" ButtonType="ButtonType.Reset">Cancel</MudButton>
<MudButton Color="Color.Primary" Variant="Variant.Filled" Disabled="@(string.IsNullOrWhiteSpace(_templateFile))" OnClick="Submit">
<MudButton Color="Color.Primary" Variant="Variant.Filled" Disabled="@(string.IsNullOrWhiteSpace(_yamlFile))" OnClick="Submit">
Save Changes
</MudButton>
</DialogActions>
@ -21,12 +21,12 @@ @@ -21,12 +21,12 @@
private readonly CancellationTokenSource _cts = new();
[Parameter]
public string TemplateFile { get; set; }
public string YamlFile { get; set; }
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
private string _templateFile;
private string _yamlFile;
public void Dispose()
{
@ -34,9 +34,9 @@ @@ -34,9 +34,9 @@
_cts.Dispose();
}
protected override void OnParametersSet() => _templateFile = TemplateFile;
protected override void OnParametersSet() => _yamlFile = YamlFile;
private void Submit() => MudDialog.Close(DialogResult.Ok(_templateFile));
private void Submit() => MudDialog.Close(DialogResult.Ok(_yamlFile));
private void Cancel() => MudDialog.Cancel();
}

4
ErsatzTV/Startup.cs

@ -36,7 +36,7 @@ using ErsatzTV.Core.Metadata; @@ -36,7 +36,7 @@ using ErsatzTV.Core.Metadata;
using ErsatzTV.Core.Plex;
using ErsatzTV.Core.Scheduling;
using ErsatzTV.Core.Scheduling.BlockScheduling;
using ErsatzTV.Core.Scheduling.TemplateScheduling;
using ErsatzTV.Core.Scheduling.YamlScheduling;
using ErsatzTV.Core.Trakt;
using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Pipeline;
@ -656,7 +656,7 @@ public class Startup @@ -656,7 +656,7 @@ public class Startup
services.AddScoped<IBlockPlayoutBuilder, BlockPlayoutBuilder>();
services.AddScoped<IBlockPlayoutPreviewBuilder, BlockPlayoutPreviewBuilder>();
services.AddScoped<IBlockPlayoutFillerBuilder, BlockPlayoutFillerBuilder>();
services.AddScoped<ITemplatePlayoutBuilder, TemplatePlayoutBuilder>();
services.AddScoped<IYamlPlayoutBuilder, YamlPlayoutBuilder>();
services.AddScoped<IExternalJsonPlayoutBuilder, ExternalJsonPlayoutBuilder>();
services.AddScoped<IPlayoutTimeShifter, PlayoutTimeShifter>();
services.AddScoped<IImageCache, ImageCache>();

4
ErsatzTV/ViewModels/PlayoutEditViewModel.cs

@ -10,13 +10,13 @@ public class PlayoutEditViewModel @@ -10,13 +10,13 @@ public class PlayoutEditViewModel
public ChannelViewModel Channel { get; set; }
public ProgramScheduleViewModel ProgramSchedule { get; set; }
public string ExternalJsonFile { get; set; }
public string TemplateFile { get; set; }
public string YamlFile { get; set; }
public CreatePlayout ToCreate() =>
Kind switch
{
PlayoutKind.ExternalJson => new CreateExternalJsonPlayout(Channel.Id, ExternalJsonFile),
PlayoutKind.Template => new CreateTemplatePlayout(Channel.Id, TemplateFile),
PlayoutKind.Yaml => new CreateYamlPlayout(Channel.Id, YamlFile),
PlayoutKind.Block => new CreateBlockPlayout(Channel.Id),
_ => new CreateFloodPlayout(Channel.Id, ProgramSchedule.Id)
};

Loading…
Cancel
Save