rename yaml playout to sequential schedule (#2335)
* clarify some schedule and playout terms * more renaming
This commit is contained in:
@@ -78,6 +78,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Fix error when changing default (lowest priority) alternate schedule
|
||||
|
||||
### Changed
|
||||
- Rename some schedule and playout terms for clarity
|
||||
- Schedules are used to build playouts and are what actually differs
|
||||
- The playout is the end result, and is the same no matter what schedule kind is used
|
||||
- Supported schedule kinds:
|
||||
- `Classic Schedules`
|
||||
- `Block Schedules`
|
||||
- `Sequential Schedules` (formerly `YAML Schedules` or `YAML Playouts`)
|
||||
- `JSON (dizqueTV) Schedules` (formerly `External JSON Playouts`)
|
||||
- Allow multiple watermarks in playback troubleshooting
|
||||
- Classic schedules: allow selecting multiple watermarks on schedule items
|
||||
- Block schedules: allow selecting multiple watermarks on decos
|
||||
|
||||
@@ -190,10 +190,10 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
|
||||
foreach (Playout playout in playouts)
|
||||
{
|
||||
switch (playout.ProgramSchedulePlayoutType)
|
||||
switch (playout.ScheduleKind)
|
||||
{
|
||||
case ProgramSchedulePlayoutType.Classic:
|
||||
case ProgramSchedulePlayoutType.Yaml:
|
||||
case PlayoutScheduleKind.Classic:
|
||||
case PlayoutScheduleKind.Sequential:
|
||||
var floodSorted = playouts
|
||||
.Collect(p => p.Items)
|
||||
.OrderBy(pi => pi.Start)
|
||||
@@ -211,7 +211,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
minifier,
|
||||
xml);
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.Block:
|
||||
case PlayoutScheduleKind.Block:
|
||||
var blockSorted = playouts
|
||||
.Collect(p => p.Items)
|
||||
.OrderBy(pi => pi.Start)
|
||||
@@ -229,7 +229,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
minifier,
|
||||
xml);
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.ExternalJson:
|
||||
case PlayoutScheduleKind.ExternalJson:
|
||||
var externalJsonSorted = (await CollectExternalJsonItems(playout.ExternalJsonFile))
|
||||
.Filter(pi => pi.StartOffset <= finish)
|
||||
.ToList();
|
||||
|
||||
@@ -32,7 +32,7 @@ public class PreviewPlaylistPlayoutHandler(
|
||||
Name = "Playlist Preview"
|
||||
},
|
||||
Items = [],
|
||||
ProgramSchedulePlayoutType = ProgramSchedulePlayoutType.Classic,
|
||||
ScheduleKind = PlayoutScheduleKind.Classic,
|
||||
PlayoutHistory = [],
|
||||
ProgramSchedule = new ProgramSchedule
|
||||
{
|
||||
|
||||
@@ -108,14 +108,14 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
dbContext,
|
||||
playout.Id,
|
||||
playout.ProgramSchedulePlayoutType);
|
||||
playout.ScheduleKind);
|
||||
string channelNumber = referenceData.Channel.Number;
|
||||
channelName = referenceData.Channel.Name;
|
||||
PlayoutBuildResult result = PlayoutBuildResult.Empty;
|
||||
|
||||
switch (playout.ProgramSchedulePlayoutType)
|
||||
switch (playout.ScheduleKind)
|
||||
{
|
||||
case ProgramSchedulePlayoutType.Block:
|
||||
case PlayoutScheduleKind.Block:
|
||||
result = await _blockPlayoutBuilder.Build(playout, referenceData, request.Mode, cancellationToken);
|
||||
result = await _blockPlayoutFillerBuilder.Build(
|
||||
playout,
|
||||
@@ -124,7 +124,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
request.Mode,
|
||||
cancellationToken);
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.Yaml:
|
||||
case PlayoutScheduleKind.Sequential:
|
||||
result = await _yamlPlayoutBuilder.Build(
|
||||
request.Start,
|
||||
playout,
|
||||
@@ -132,11 +132,11 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
request.Mode,
|
||||
cancellationToken);
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.ExternalJson:
|
||||
case PlayoutScheduleKind.ExternalJson:
|
||||
await _externalJsonPlayoutBuilder.Build(playout, request.Mode, cancellationToken);
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.None:
|
||||
case ProgramSchedulePlayoutType.Classic:
|
||||
case PlayoutScheduleKind.None:
|
||||
case PlayoutScheduleKind.Classic:
|
||||
default:
|
||||
result = await _playoutBuilder.Build(playout, referenceData, request.Mode, cancellationToken);
|
||||
break;
|
||||
@@ -223,7 +223,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
|
||||
string fileName = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channelNumber}.xml");
|
||||
if (hasChanges || !File.Exists(fileName) ||
|
||||
playout.ProgramSchedulePlayoutType is ProgramSchedulePlayoutType.ExternalJson)
|
||||
playout.ScheduleKind is PlayoutScheduleKind.ExternalJson)
|
||||
{
|
||||
await _workerChannel.WriteAsync(new RefreshChannelData(channelNumber), cancellationToken);
|
||||
}
|
||||
@@ -276,9 +276,9 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
|
||||
foreach (Playout playout in maybePlayout)
|
||||
{
|
||||
switch (playout.ProgramSchedulePlayoutType)
|
||||
switch (playout.ScheduleKind)
|
||||
{
|
||||
case ProgramSchedulePlayoutType.Classic:
|
||||
case PlayoutScheduleKind.Classic:
|
||||
await dbContext.Entry(playout)
|
||||
.Collection(p => p.FillGroupIndices)
|
||||
.LoadAsync();
|
||||
@@ -311,7 +311,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
private static async Task<PlayoutReferenceData> GetReferenceData(
|
||||
TvContext dbContext,
|
||||
int playoutId,
|
||||
ProgramSchedulePlayoutType playoutType)
|
||||
PlayoutScheduleKind scheduleKind)
|
||||
{
|
||||
Channel channel = await dbContext.Channels
|
||||
.AsNoTracking()
|
||||
@@ -322,7 +322,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
List<PlayoutItem> existingItems = [];
|
||||
List<PlayoutTemplate> playoutTemplates = [];
|
||||
|
||||
if (playoutType is ProgramSchedulePlayoutType.Block)
|
||||
if (scheduleKind is PlayoutScheduleKind.Block)
|
||||
{
|
||||
deco = await dbContext.Decos
|
||||
.AsNoTracking()
|
||||
|
||||
@@ -41,11 +41,11 @@ public class CreateBlockPlayoutHandler(
|
||||
private static async Task<Validation<BaseError, Playout>> Validate(
|
||||
TvContext dbContext,
|
||||
CreateBlockPlayout request) =>
|
||||
(await ValidateChannel(dbContext, request), ValidatePlayoutType(request))
|
||||
.Apply((channel, playoutType) => new Playout
|
||||
(await ValidateChannel(dbContext, request), ValidateScheduleKind(request))
|
||||
.Apply((channel, scheduleKind) => new Playout
|
||||
{
|
||||
ChannelId = channel.Id,
|
||||
ProgramSchedulePlayoutType = playoutType,
|
||||
ScheduleKind = scheduleKind,
|
||||
Seed = new Random().Next()
|
||||
});
|
||||
|
||||
@@ -64,9 +64,9 @@ public class CreateBlockPlayoutHandler(
|
||||
.Map(_ => channel)
|
||||
.ToValidation<BaseError>("Channel already has one playout");
|
||||
|
||||
private static Validation<BaseError, ProgramSchedulePlayoutType> ValidatePlayoutType(
|
||||
private static Validation<BaseError, PlayoutScheduleKind> ValidateScheduleKind(
|
||||
CreateBlockPlayout createBlockPlayout) =>
|
||||
Optional(createBlockPlayout.ProgramSchedulePlayoutType)
|
||||
.Filter(playoutType => playoutType == ProgramSchedulePlayoutType.Block)
|
||||
.ToValidation<BaseError>("[ProgramSchedulePlayoutType] must be Block");
|
||||
Optional(createBlockPlayout.ScheduleKind)
|
||||
.Filter(scheduleKind => scheduleKind == PlayoutScheduleKind.Block)
|
||||
.ToValidation<BaseError>("[ScheduleKind] must be Block");
|
||||
}
|
||||
|
||||
+16
-16
@@ -10,12 +10,12 @@ using Channel = ErsatzTV.Core.Domain.Channel;
|
||||
|
||||
namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public class CreateFloodPlayoutHandler : IRequestHandler<CreateFloodPlayout, Either<BaseError, CreatePlayoutResponse>>
|
||||
public class CreateClassicPlayoutHandler : IRequestHandler<CreateClassicPlayout, Either<BaseError, CreatePlayoutResponse>>
|
||||
{
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public CreateFloodPlayoutHandler(
|
||||
public CreateClassicPlayoutHandler(
|
||||
ChannelWriter<IBackgroundServiceRequest> channel,
|
||||
IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
@@ -24,7 +24,7 @@ public class CreateFloodPlayoutHandler : IRequestHandler<CreateFloodPlayout, Eit
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, CreatePlayoutResponse>> Handle(
|
||||
CreateFloodPlayout request,
|
||||
CreateClassicPlayout request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
@@ -48,22 +48,22 @@ public class CreateFloodPlayoutHandler : IRequestHandler<CreateFloodPlayout, Eit
|
||||
|
||||
private static async Task<Validation<BaseError, Playout>> Validate(
|
||||
TvContext dbContext,
|
||||
CreateFloodPlayout request) =>
|
||||
CreateClassicPlayout request) =>
|
||||
(await ValidateChannel(dbContext, request), await ValidateProgramSchedule(dbContext, request),
|
||||
ValidatePlayoutType(request))
|
||||
.Apply((channel, programSchedule, playoutType) => new Playout
|
||||
ValidateScheduleKind(request))
|
||||
.Apply((channel, programSchedule, scheduleKind) => new Playout
|
||||
{
|
||||
ChannelId = channel.Id,
|
||||
ProgramScheduleId = programSchedule.Id,
|
||||
ProgramSchedulePlayoutType = playoutType
|
||||
ScheduleKind = scheduleKind
|
||||
});
|
||||
|
||||
private static Task<Validation<BaseError, Channel>> ValidateChannel(
|
||||
TvContext dbContext,
|
||||
CreateFloodPlayout createFloodPlayout) =>
|
||||
CreateClassicPlayout createClassicPlayout) =>
|
||||
dbContext.Channels
|
||||
.Include(c => c.Playouts)
|
||||
.SelectOneAsync(c => c.Id, c => c.Id == createFloodPlayout.ChannelId)
|
||||
.SelectOneAsync(c => c.Id, c => c.Id == createClassicPlayout.ChannelId)
|
||||
.Map(o => o.ToValidation<BaseError>("Channel does not exist"))
|
||||
.BindT(ChannelMustNotHavePlayouts);
|
||||
|
||||
@@ -75,10 +75,10 @@ public class CreateFloodPlayoutHandler : IRequestHandler<CreateFloodPlayout, Eit
|
||||
|
||||
private static Task<Validation<BaseError, ProgramSchedule>> ValidateProgramSchedule(
|
||||
TvContext dbContext,
|
||||
CreateFloodPlayout createFloodPlayout) =>
|
||||
CreateClassicPlayout createClassicPlayout) =>
|
||||
dbContext.ProgramSchedules
|
||||
.Include(ps => ps.Items)
|
||||
.SelectOneAsync(ps => ps.Id, ps => ps.Id == createFloodPlayout.ProgramScheduleId)
|
||||
.SelectOneAsync(ps => ps.Id, ps => ps.Id == createClassicPlayout.ProgramScheduleId)
|
||||
.Map(o => o.ToValidation<BaseError>("Program schedule does not exist"))
|
||||
.BindT(ProgramScheduleMustHaveItems);
|
||||
|
||||
@@ -88,9 +88,9 @@ public class CreateFloodPlayoutHandler : IRequestHandler<CreateFloodPlayout, Eit
|
||||
.Filter(ps => ps.Items.Count != 0)
|
||||
.ToValidation<BaseError>("Program schedule must have items");
|
||||
|
||||
private static Validation<BaseError, ProgramSchedulePlayoutType> ValidatePlayoutType(
|
||||
CreateFloodPlayout createFloodPlayout) =>
|
||||
Optional(createFloodPlayout.ProgramSchedulePlayoutType)
|
||||
.Filter(playoutType => playoutType != ProgramSchedulePlayoutType.None)
|
||||
.ToValidation<BaseError>("[ProgramSchedulePlayoutType] must not be None");
|
||||
private static Validation<BaseError, PlayoutScheduleKind> ValidateScheduleKind(
|
||||
CreateClassicPlayout createClassicPlayout) =>
|
||||
Optional(createClassicPlayout.ScheduleKind)
|
||||
.Filter(scheduleKind => scheduleKind == PlayoutScheduleKind.Classic)
|
||||
.ToValidation<BaseError>("[ScheduleKind] must be Classic");
|
||||
}
|
||||
@@ -49,12 +49,12 @@ public class CreateExternalJsonPlayoutHandler
|
||||
private async Task<Validation<BaseError, Playout>> Validate(
|
||||
TvContext dbContext,
|
||||
CreateExternalJsonPlayout request) =>
|
||||
(await ValidateChannel(dbContext, request), ValidateExternalJsonFile(request), ValidatePlayoutType(request))
|
||||
.Apply((channel, externalJsonFile, playoutType) => new Playout
|
||||
(await ValidateChannel(dbContext, request), ValidateExternalJsonFile(request), ValidateScheduleKind(request))
|
||||
.Apply((channel, externalJsonFile, scheduleKind) => new Playout
|
||||
{
|
||||
ChannelId = channel.Id,
|
||||
ExternalJsonFile = externalJsonFile,
|
||||
ProgramSchedulePlayoutType = playoutType
|
||||
ScheduleKind = scheduleKind
|
||||
});
|
||||
|
||||
private static Task<Validation<BaseError, Channel>> ValidateChannel(
|
||||
@@ -82,9 +82,9 @@ public class CreateExternalJsonPlayoutHandler
|
||||
return request.ExternalJsonFile;
|
||||
}
|
||||
|
||||
private static Validation<BaseError, ProgramSchedulePlayoutType> ValidatePlayoutType(
|
||||
private static Validation<BaseError, PlayoutScheduleKind> ValidateScheduleKind(
|
||||
CreateExternalJsonPlayout createExternalJsonPlayout) =>
|
||||
Optional(createExternalJsonPlayout.ProgramSchedulePlayoutType)
|
||||
.Filter(playoutType => playoutType == ProgramSchedulePlayoutType.ExternalJson)
|
||||
.ToValidation<BaseError>("[ProgramSchedulePlayoutType] must be ExternalJson");
|
||||
Optional(createExternalJsonPlayout.ScheduleKind)
|
||||
.Filter(scheduleKind => scheduleKind == PlayoutScheduleKind.ExternalJson)
|
||||
.ToValidation<BaseError>("[ScheduleKind] must be ExternalJson");
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@ using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public record CreatePlayout(int ChannelId, ProgramSchedulePlayoutType ProgramSchedulePlayoutType)
|
||||
public record CreatePlayout(int ChannelId, PlayoutScheduleKind ScheduleKind)
|
||||
: IRequest<Either<BaseError, CreatePlayoutResponse>>;
|
||||
|
||||
public record CreateFloodPlayout(int ChannelId, int ProgramScheduleId)
|
||||
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.Classic);
|
||||
public record CreateClassicPlayout(int ChannelId, int ProgramScheduleId)
|
||||
: CreatePlayout(ChannelId, PlayoutScheduleKind.Classic);
|
||||
|
||||
public record CreateBlockPlayout(int ChannelId)
|
||||
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.Block);
|
||||
: CreatePlayout(ChannelId, PlayoutScheduleKind.Block);
|
||||
|
||||
public record CreateYamlPlayout(int ChannelId, string TemplateFile)
|
||||
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.Yaml);
|
||||
public record CreateSequentialPlayout(int ChannelId, string TemplateFile)
|
||||
: CreatePlayout(ChannelId, PlayoutScheduleKind.Sequential);
|
||||
|
||||
public record CreateExternalJsonPlayout(int ChannelId, string ExternalJsonFile)
|
||||
: CreatePlayout(ChannelId, ProgramSchedulePlayoutType.ExternalJson);
|
||||
: CreatePlayout(ChannelId, PlayoutScheduleKind.ExternalJson);
|
||||
|
||||
+17
-17
@@ -11,14 +11,14 @@ using Channel = ErsatzTV.Core.Domain.Channel;
|
||||
|
||||
namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public class CreateYamlPlayoutHandler
|
||||
: IRequestHandler<CreateYamlPlayout, Either<BaseError, CreatePlayoutResponse>>
|
||||
public class CreateSequentialPlayoutHandler
|
||||
: IRequestHandler<CreateSequentialPlayout, Either<BaseError, CreatePlayoutResponse>>
|
||||
{
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
|
||||
public CreateYamlPlayoutHandler(
|
||||
public CreateSequentialPlayoutHandler(
|
||||
ILocalFileSystem localFileSystem,
|
||||
ChannelWriter<IBackgroundServiceRequest> channel,
|
||||
IDbContextFactory<TvContext> dbContextFactory)
|
||||
@@ -29,7 +29,7 @@ public class CreateYamlPlayoutHandler
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, CreatePlayoutResponse>> Handle(
|
||||
CreateYamlPlayout request,
|
||||
CreateSequentialPlayout request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
@@ -53,22 +53,22 @@ public class CreateYamlPlayoutHandler
|
||||
|
||||
private async Task<Validation<BaseError, Playout>> Validate(
|
||||
TvContext dbContext,
|
||||
CreateYamlPlayout request) =>
|
||||
(await ValidateChannel(dbContext, request), ValidateYamlFile(request), ValidatePlayoutType(request))
|
||||
.Apply((channel, externalJsonFile, playoutType) => new Playout
|
||||
CreateSequentialPlayout request) =>
|
||||
(await ValidateChannel(dbContext, request), ValidateYamlFile(request), ValidateScheduleKind(request))
|
||||
.Apply((channel, yamlFile, scheduleKind) => new Playout
|
||||
{
|
||||
ChannelId = channel.Id,
|
||||
TemplateFile = externalJsonFile,
|
||||
ProgramSchedulePlayoutType = playoutType,
|
||||
TemplateFile = yamlFile,
|
||||
ScheduleKind = scheduleKind,
|
||||
Seed = new Random().Next()
|
||||
});
|
||||
|
||||
private static Task<Validation<BaseError, Channel>> ValidateChannel(
|
||||
TvContext dbContext,
|
||||
CreateYamlPlayout createYamlPlayout) =>
|
||||
CreateSequentialPlayout createSequentialPlayout) =>
|
||||
dbContext.Channels
|
||||
.Include(c => c.Playouts)
|
||||
.SelectOneAsync(c => c.Id, c => c.Id == createYamlPlayout.ChannelId)
|
||||
.SelectOneAsync(c => c.Id, c => c.Id == createSequentialPlayout.ChannelId)
|
||||
.Map(o => o.ToValidation<BaseError>("Channel does not exist"))
|
||||
.BindT(ChannelMustNotHavePlayouts);
|
||||
|
||||
@@ -78,7 +78,7 @@ public class CreateYamlPlayoutHandler
|
||||
.Map(_ => channel)
|
||||
.ToValidation<BaseError>("Channel already has one playout");
|
||||
|
||||
private Validation<BaseError, string> ValidateYamlFile(CreateYamlPlayout request)
|
||||
private Validation<BaseError, string> ValidateYamlFile(CreateSequentialPlayout request)
|
||||
{
|
||||
if (!_localFileSystem.FileExists(request.TemplateFile))
|
||||
{
|
||||
@@ -88,9 +88,9 @@ public class CreateYamlPlayoutHandler
|
||||
return request.TemplateFile;
|
||||
}
|
||||
|
||||
private static Validation<BaseError, ProgramSchedulePlayoutType> ValidatePlayoutType(
|
||||
CreateYamlPlayout createYamlPlayout) =>
|
||||
Optional(createYamlPlayout.ProgramSchedulePlayoutType)
|
||||
.Filter(playoutType => playoutType == ProgramSchedulePlayoutType.Yaml)
|
||||
.ToValidation<BaseError>("[ProgramSchedulePlayoutType] must be YAML");
|
||||
private static Validation<BaseError, PlayoutScheduleKind> ValidateScheduleKind(
|
||||
CreateSequentialPlayout createSequentialPlayout) =>
|
||||
Optional(createSequentialPlayout.ScheduleKind)
|
||||
.Filter(scheduleKind => scheduleKind == PlayoutScheduleKind.Sequential)
|
||||
.ToValidation<BaseError>("[ScheduleKind] must be YAML");
|
||||
}
|
||||
@@ -19,11 +19,11 @@ public class ResetAllPlayoutsHandler(
|
||||
|
||||
foreach (Playout playout in await dbContext.Playouts.ToListAsync(cancellationToken))
|
||||
{
|
||||
switch (playout.ProgramSchedulePlayoutType)
|
||||
switch (playout.ScheduleKind)
|
||||
{
|
||||
case ProgramSchedulePlayoutType.Classic:
|
||||
case ProgramSchedulePlayoutType.Block:
|
||||
case ProgramSchedulePlayoutType.Yaml:
|
||||
case PlayoutScheduleKind.Classic:
|
||||
case PlayoutScheduleKind.Block:
|
||||
case PlayoutScheduleKind.Sequential:
|
||||
if (!locker.IsPlayoutLocked(playout.Id))
|
||||
{
|
||||
await channel.WriteAsync(
|
||||
@@ -32,8 +32,8 @@ public class ResetAllPlayoutsHandler(
|
||||
}
|
||||
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.ExternalJson:
|
||||
case ProgramSchedulePlayoutType.None:
|
||||
case PlayoutScheduleKind.ExternalJson:
|
||||
case PlayoutScheduleKind.None:
|
||||
default:
|
||||
// external json cannot be reset
|
||||
continue;
|
||||
|
||||
@@ -46,7 +46,7 @@ public class
|
||||
|
||||
return new PlayoutNameViewModel(
|
||||
playout.Id,
|
||||
playout.ProgramSchedulePlayoutType,
|
||||
playout.ScheduleKind,
|
||||
playout.Channel.Name,
|
||||
playout.Channel.Number,
|
||||
playout.Channel.PlayoutMode,
|
||||
|
||||
@@ -38,7 +38,7 @@ public class UpdatePlayoutHandler : IRequestHandler<UpdatePlayout, Either<BaseEr
|
||||
|
||||
return new PlayoutNameViewModel(
|
||||
playout.Id,
|
||||
playout.ProgramSchedulePlayoutType,
|
||||
playout.ScheduleKind,
|
||||
playout.Channel.Name,
|
||||
playout.Channel.Number,
|
||||
playout.Channel.PlayoutMode,
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@
|
||||
|
||||
namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public record UpdateYamlPlayout(int PlayoutId, string TemplateFile)
|
||||
public record UpdateSequentialPlayout(int PlayoutId, string TemplateFile)
|
||||
: IRequest<Either<BaseError, PlayoutNameViewModel>>;
|
||||
+7
-7
@@ -9,13 +9,13 @@ using Microsoft.EntityFrameworkCore;
|
||||
namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public class
|
||||
UpdateTemplatePlayoutHandler : IRequestHandler<UpdateYamlPlayout,
|
||||
UpdateSequentialPlayoutHandler : IRequestHandler<UpdateSequentialPlayout,
|
||||
Either<BaseError, PlayoutNameViewModel>>
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
|
||||
public UpdateTemplatePlayoutHandler(
|
||||
public UpdateSequentialPlayoutHandler(
|
||||
IDbContextFactory<TvContext> dbContextFactory,
|
||||
ChannelWriter<IBackgroundServiceRequest> workerChannel)
|
||||
{
|
||||
@@ -24,7 +24,7 @@ public class
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, PlayoutNameViewModel>> Handle(
|
||||
UpdateYamlPlayout request,
|
||||
UpdateSequentialPlayout request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
@@ -34,7 +34,7 @@ public class
|
||||
|
||||
private async Task<PlayoutNameViewModel> ApplyUpdateRequest(
|
||||
TvContext dbContext,
|
||||
UpdateYamlPlayout request,
|
||||
UpdateSequentialPlayout request,
|
||||
Playout playout)
|
||||
{
|
||||
playout.TemplateFile = request.TemplateFile;
|
||||
@@ -46,7 +46,7 @@ public class
|
||||
|
||||
return new PlayoutNameViewModel(
|
||||
playout.Id,
|
||||
playout.ProgramSchedulePlayoutType,
|
||||
playout.ScheduleKind,
|
||||
playout.Channel.Name,
|
||||
playout.Channel.Number,
|
||||
playout.Channel.PlayoutMode,
|
||||
@@ -58,12 +58,12 @@ public class
|
||||
|
||||
private static Task<Validation<BaseError, Playout>> Validate(
|
||||
TvContext dbContext,
|
||||
UpdateYamlPlayout request) =>
|
||||
UpdateSequentialPlayout request) =>
|
||||
PlayoutMustExist(dbContext, request);
|
||||
|
||||
private static Task<Validation<BaseError, Playout>> PlayoutMustExist(
|
||||
TvContext dbContext,
|
||||
UpdateYamlPlayout updatePlayout) =>
|
||||
UpdateSequentialPlayout updatePlayout) =>
|
||||
dbContext.Playouts
|
||||
.Include(p => p.Channel)
|
||||
.SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId)
|
||||
@@ -4,7 +4,7 @@ namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public record PlayoutNameViewModel(
|
||||
int PlayoutId,
|
||||
ProgramSchedulePlayoutType PlayoutType,
|
||||
PlayoutScheduleKind ScheduleKind,
|
||||
string ChannelName,
|
||||
string ChannelNumber,
|
||||
ChannelPlayoutMode PlayoutMode,
|
||||
|
||||
@@ -6,4 +6,4 @@ public record PlayoutViewModel(
|
||||
int Id,
|
||||
PlayoutChannelViewModel Channel,
|
||||
PlayoutProgramScheduleViewModel ProgramSchedule,
|
||||
ProgramSchedulePlayoutType ProgramSchedulePlayoutType);
|
||||
PlayoutScheduleKind ScheduleKind);
|
||||
|
||||
@@ -21,7 +21,7 @@ public class GetAllPlayoutsHandler : IRequestHandler<GetAllPlayouts, List<Playou
|
||||
.Filter(p => p.Channel != null)
|
||||
.Map(p => new PlayoutNameViewModel(
|
||||
p.Id,
|
||||
p.ProgramSchedulePlayoutType,
|
||||
p.ScheduleKind,
|
||||
p.Channel.Name,
|
||||
p.Channel.Number,
|
||||
p.Channel.PlayoutMode,
|
||||
|
||||
@@ -19,7 +19,7 @@ public class GetPlayoutByIdHandler(IDbContextFactory<TvContext> dbContextFactory
|
||||
.SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId)
|
||||
.MapT(p => new PlayoutNameViewModel(
|
||||
p.Id,
|
||||
p.ProgramSchedulePlayoutType,
|
||||
p.ScheduleKind,
|
||||
p.Channel.Name,
|
||||
p.Channel.Number,
|
||||
p.Channel.PlayoutMode,
|
||||
|
||||
@@ -13,8 +13,8 @@ public class ErasePlayoutHistoryHandler(IDbContextFactory<TvContext> dbContextFa
|
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
Option<Playout> maybePlayout = await dbContext.Playouts
|
||||
.Filter(p => p.ProgramSchedulePlayoutType == ProgramSchedulePlayoutType.Block ||
|
||||
p.ProgramSchedulePlayoutType == ProgramSchedulePlayoutType.Yaml)
|
||||
.Filter(p => p.ScheduleKind == PlayoutScheduleKind.Block ||
|
||||
p.ScheduleKind == PlayoutScheduleKind.Sequential)
|
||||
.SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId);
|
||||
|
||||
foreach (Playout playout in maybePlayout)
|
||||
|
||||
@@ -16,8 +16,8 @@ public class ErasePlayoutItemsHandler(IDbContextFactory<TvContext> dbContextFact
|
||||
Option<Playout> maybePlayout = await dbContext.Playouts
|
||||
.Include(p => p.Items)
|
||||
.Include(p => p.PlayoutHistory)
|
||||
.Filter(p => p.ProgramSchedulePlayoutType == ProgramSchedulePlayoutType.Block ||
|
||||
p.ProgramSchedulePlayoutType == ProgramSchedulePlayoutType.Yaml)
|
||||
.Filter(p => p.ScheduleKind == PlayoutScheduleKind.Block ||
|
||||
p.ScheduleKind == PlayoutScheduleKind.Sequential)
|
||||
.SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId);
|
||||
|
||||
foreach (Playout playout in maybePlayout)
|
||||
|
||||
@@ -42,7 +42,7 @@ public class PreviewBlockPlayoutHandler(
|
||||
Name = "Block Preview"
|
||||
},
|
||||
Items = [],
|
||||
ProgramSchedulePlayoutType = ProgramSchedulePlayoutType.Block,
|
||||
ScheduleKind = PlayoutScheduleKind.Block,
|
||||
PlayoutHistory = [],
|
||||
Templates =
|
||||
[
|
||||
|
||||
@@ -133,7 +133,7 @@ public class ScheduleIntegrationTests
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
PLAYOUT_ID,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutScheduleKind.Classic);
|
||||
|
||||
await builder.Build(
|
||||
playout,
|
||||
@@ -157,7 +157,7 @@ public class ScheduleIntegrationTests
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
PLAYOUT_ID,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutScheduleKind.Classic);
|
||||
|
||||
await builder.Build(
|
||||
playout,
|
||||
@@ -181,7 +181,7 @@ public class ScheduleIntegrationTests
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
PLAYOUT_ID,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutScheduleKind.Classic);
|
||||
|
||||
await builder.Build(
|
||||
playout,
|
||||
@@ -329,7 +329,7 @@ public class ScheduleIntegrationTests
|
||||
PlayoutReferenceData referenceData = await GetReferenceData(
|
||||
context,
|
||||
playoutId,
|
||||
ProgramSchedulePlayoutType.Classic);
|
||||
PlayoutScheduleKind.Classic);
|
||||
|
||||
await builder.Build(
|
||||
playout,
|
||||
@@ -397,7 +397,7 @@ public class ScheduleIntegrationTests
|
||||
private static async Task<PlayoutReferenceData> GetReferenceData(
|
||||
TvContext dbContext,
|
||||
int playoutId,
|
||||
ProgramSchedulePlayoutType playoutType)
|
||||
PlayoutScheduleKind scheduleKind)
|
||||
{
|
||||
Channel channel = await dbContext.Channels
|
||||
.AsNoTracking()
|
||||
@@ -407,7 +407,7 @@ public class ScheduleIntegrationTests
|
||||
List<PlayoutItem> existingItems = [];
|
||||
List<PlayoutTemplate> playoutTemplates = [];
|
||||
|
||||
if (playoutType is ProgramSchedulePlayoutType.Block)
|
||||
if (scheduleKind is PlayoutScheduleKind.Block)
|
||||
{
|
||||
existingItems = await dbContext.PlayoutItems
|
||||
.AsNoTracking()
|
||||
|
||||
@@ -12,7 +12,7 @@ public class Playout
|
||||
public string ExternalJsonFile { get; set; }
|
||||
public string TemplateFile { get; set; }
|
||||
public List<ProgramScheduleAlternate> ProgramScheduleAlternates { get; set; }
|
||||
public ProgramSchedulePlayoutType ProgramSchedulePlayoutType { get; set; }
|
||||
public PlayoutScheduleKind ScheduleKind { get; set; }
|
||||
public List<PlayoutItem> Items { get; set; }
|
||||
public PlayoutAnchor Anchor { get; set; }
|
||||
public List<PlayoutProgramScheduleAnchor> ProgramScheduleAnchors { get; set; }
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
namespace ErsatzTV.Core.Domain;
|
||||
|
||||
public enum ProgramSchedulePlayoutType
|
||||
public enum PlayoutScheduleKind
|
||||
{
|
||||
None = 0,
|
||||
Classic = 1,
|
||||
Block = 2,
|
||||
Yaml = 3,
|
||||
Sequential = 3,
|
||||
|
||||
ExternalJson = 20
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
namespace ErsatzTV.Core.Interfaces.Scheduling;
|
||||
|
||||
public interface IYamlScheduleValidator
|
||||
public interface ISequentialScheduleValidator
|
||||
{
|
||||
Task<bool> ValidateSchedule(string yaml, bool isImport);
|
||||
string ToJson(string yaml);
|
||||
@@ -67,11 +67,11 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
{
|
||||
PlayoutBuildResult result = PlayoutBuildResult.Empty;
|
||||
|
||||
if (playout.ProgramSchedulePlayoutType is not ProgramSchedulePlayoutType.Classic)
|
||||
if (playout.ScheduleKind is not PlayoutScheduleKind.Classic)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Skipping playout build with type {Type} on channel {Number} - {Name}",
|
||||
playout.ProgramSchedulePlayoutType,
|
||||
playout.ScheduleKind,
|
||||
referenceData.Channel.Number,
|
||||
referenceData.Channel.Name);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public class YamlPlayoutRepeatHandler : IYamlPlayoutHandler
|
||||
if (context.VisitedAll && _itemsSinceLastRepeat == context.AddedItems.Count)
|
||||
{
|
||||
logger.LogWarning("Repeat encountered without adding any playout items; aborting");
|
||||
throw new InvalidOperationException("YAML playout loop detected");
|
||||
throw new InvalidOperationException("Sequential playout loop detected");
|
||||
}
|
||||
|
||||
_itemsSinceLastRepeat = context.AddedItems.Count;
|
||||
|
||||
@@ -20,7 +20,7 @@ public class YamlPlayoutBuilder(
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
IChannelRepository channelRepository,
|
||||
IGraphicsElementRepository graphicsElementRepository,
|
||||
IYamlScheduleValidator yamlScheduleValidator,
|
||||
ISequentialScheduleValidator sequentialScheduleValidator,
|
||||
ILogger<YamlPlayoutBuilder> logger)
|
||||
: IYamlPlayoutBuilder
|
||||
{
|
||||
@@ -455,7 +455,7 @@ public class YamlPlayoutBuilder(
|
||||
try
|
||||
{
|
||||
string yaml = await File.ReadAllTextAsync(fileName, cancellationToken);
|
||||
if (!await yamlScheduleValidator.ValidateSchedule(yaml, isImport))
|
||||
if (!await sequentialScheduleValidator.ValidateSchedule(yaml, isImport))
|
||||
{
|
||||
return Option<YamlPlayoutDefinition>.None;
|
||||
}
|
||||
|
||||
+6331
File diff suppressed because it is too large
Load Diff
+28
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Rename_ProgramSchedulePlayoutType_ScheduleKind : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ProgramSchedulePlayoutType",
|
||||
table: "Playout",
|
||||
newName: "ScheduleKind");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ScheduleKind",
|
||||
table: "Playout",
|
||||
newName: "ProgramSchedulePlayoutType");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1831,7 +1831,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
|
||||
b.Property<int?>("ProgramScheduleId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ProgramSchedulePlayoutType")
|
||||
b.Property<int>("ScheduleKind")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Seed")
|
||||
|
||||
+6166
File diff suppressed because it is too large
Load Diff
+28
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Rename_ProgramSchedulePlayoutType_ScheduleKind : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ProgramSchedulePlayoutType",
|
||||
table: "Playout",
|
||||
newName: "ScheduleKind");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ScheduleKind",
|
||||
table: "Playout",
|
||||
newName: "ProgramSchedulePlayoutType");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1742,7 +1742,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
||||
b.Property<int?>("ProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramSchedulePlayoutType")
|
||||
b.Property<int>("ScheduleKind")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Seed")
|
||||
|
||||
+5
-5
@@ -11,7 +11,7 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Scheduling;
|
||||
|
||||
public class YamlScheduleValidator(ILogger<YamlScheduleValidator> logger) : IYamlScheduleValidator
|
||||
public class SequentialScheduleValidator(ILogger<SequentialScheduleValidator> logger) : ISequentialScheduleValidator
|
||||
{
|
||||
public async Task<bool> ValidateSchedule(string yaml, bool isImport)
|
||||
{
|
||||
@@ -19,7 +19,7 @@ public class YamlScheduleValidator(ILogger<YamlScheduleValidator> logger) : IYam
|
||||
{
|
||||
string schemaFileName = Path.Combine(
|
||||
FileSystemLayout.ResourcesCacheFolder,
|
||||
isImport ? "yaml-playout-import.schema.json" : "yaml-playout.schema.json");
|
||||
isImport ? "sequential-schedule-import.schema.json" : "sequential-schedule.schema.json");
|
||||
using StreamReader sr = File.OpenText(schemaFileName);
|
||||
await using var reader = new JsonTextReader(sr);
|
||||
var schema = JSchema.Load(reader);
|
||||
@@ -31,7 +31,7 @@ public class YamlScheduleValidator(ILogger<YamlScheduleValidator> logger) : IYam
|
||||
|
||||
if (!schedule.IsValid(schema, out IList<string> errorMessages))
|
||||
{
|
||||
logger.LogWarning("Failed to validate YAML schedule definition: {ErrorMessages}", errorMessages);
|
||||
logger.LogWarning("Failed to validate sequential schedule definition: {ErrorMessages}", errorMessages);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class YamlScheduleValidator(ILogger<YamlScheduleValidator> logger) : IYam
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Unexpected error while validating YAML schedule definition");
|
||||
logger.LogWarning(ex, "Unexpected error while validating sequential schedule definition");
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -62,7 +62,7 @@ public class YamlScheduleValidator(ILogger<YamlScheduleValidator> logger) : IYam
|
||||
{
|
||||
string schemaFileName = Path.Combine(
|
||||
FileSystemLayout.ResourcesCacheFolder,
|
||||
isImport ? "yaml-playout-import.schema.json" : "yaml-playout.schema.json");
|
||||
isImport ? "sequential-schedule-import.schema.json" : "sequential-schedule.schema.json");
|
||||
using StreamReader sr = File.OpenText(schemaFileName);
|
||||
await using var reader = new JsonTextReader(sr);
|
||||
var schema = JSchema.Load(reader);
|
||||
@@ -58,7 +58,7 @@ public class ExternalJsonPlayoutItemProvider : IExternalJsonPlayoutItemProvider
|
||||
foreach (Playout playout in maybePlayout)
|
||||
{
|
||||
// playout must be external json
|
||||
if (playout.ProgramSchedulePlayoutType == ProgramSchedulePlayoutType.ExternalJson)
|
||||
if (playout.ScheduleKind == PlayoutScheduleKind.ExternalJson)
|
||||
{
|
||||
// json file must exist
|
||||
if (_localFileSystem.FileExists(playout.ExternalJsonFile))
|
||||
|
||||
@@ -81,8 +81,8 @@
|
||||
<EmbeddedResource Include="Resources\Templates\_musicVideo.sbntxt" />
|
||||
<EmbeddedResource Include="Resources\Templates\_otherVideo.sbntxt" />
|
||||
<EmbeddedResource Include="Resources\Templates\_song.sbntxt" />
|
||||
<EmbeddedResource Include="Resources\yaml-playout-import.schema.json" />
|
||||
<EmbeddedResource Include="Resources\yaml-playout.schema.json" />
|
||||
<EmbeddedResource Include="Resources\sequential-schedule-import.schema.json" />
|
||||
<EmbeddedResource Include="Resources\sequential-schedule.schema.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -13,22 +13,8 @@
|
||||
|
||||
<MudForm @ref="_form" Validation="@(_validator.ValidateValue)" ValidationDelay="0" Style="max-height: 100%">
|
||||
<MudPaper Square="true" Style="display: flex; height: 64px; min-height: 64px; width: 100%; z-index: 100; align-items: center">
|
||||
<MudButton Class="ml-6" OnClick="HandleSubmitAsync" Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add">
|
||||
@switch (Kind)
|
||||
{
|
||||
case PlayoutKind.ExternalJson:
|
||||
@:Add External Json Playout
|
||||
break;
|
||||
case PlayoutKind.Yaml:
|
||||
@:Add YAML Playout
|
||||
break;
|
||||
case PlayoutKind.Block:
|
||||
@:Add Block Playout
|
||||
break;
|
||||
default:
|
||||
@:Add Playout
|
||||
break;
|
||||
}
|
||||
<MudButton Class="ml-6" OnClick="@HandleSubmitAsync" Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add">
|
||||
Add Playout
|
||||
</MudButton>
|
||||
</MudPaper>
|
||||
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto">
|
||||
@@ -53,25 +39,29 @@
|
||||
case PlayoutKind.ExternalJson:
|
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
|
||||
<div class="d-flex">
|
||||
<MudText>External Json File</MudText>
|
||||
<MudText>JSON (dizqueTV) Schedule</MudText>
|
||||
</div>
|
||||
<MudTextField @bind-Value="_model.ExternalJsonFile" For="@(() => _model.ExternalJsonFile)"/>
|
||||
</MudStack>
|
||||
break;
|
||||
case PlayoutKind.Yaml:
|
||||
case PlayoutKind.Sequential:
|
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
|
||||
<div class="d-flex">
|
||||
<MudText>YAML File</MudText>
|
||||
<MudText>Sequential Schedule</MudText>
|
||||
</div>
|
||||
<MudTextField @bind-Value="_model.YamlFile" For="@(() => _model.YamlFile)"/>
|
||||
<MudTextField @bind-Value="_model.SequentialSchedule" For="@(() => _model.SequentialSchedule)"/>
|
||||
</MudStack>
|
||||
break;
|
||||
case PlayoutKind.Block:
|
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
|
||||
<div class="d-flex"></div>
|
||||
<MudText Typo="Typo.caption" Style="font-weight: normal">Block templates are added later</MudText>
|
||||
</MudStack>
|
||||
break;
|
||||
default:
|
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
|
||||
<div class="d-flex">
|
||||
<MudText>Schedule</MudText>
|
||||
<MudText>Classic Schedule</MudText>
|
||||
</div>
|
||||
<MudSelect T="ProgramScheduleViewModel" @bind-value="_model.ProgramSchedule">
|
||||
@foreach (ProgramScheduleViewModel schedule in _programSchedules)
|
||||
|
||||
@@ -17,24 +17,17 @@
|
||||
<div style="display: flex; flex-direction: row; margin-bottom: auto; margin-top: auto; width: 100%; align-items: center" class="ml-6 mr-6">
|
||||
<div class="flex-grow-1"></div>
|
||||
<div style="margin-left: auto" class="d-none d-md-flex">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" Href="playouts/add">
|
||||
Add Playout
|
||||
</MudButton>
|
||||
<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.Block}")">
|
||||
Add Block Playout
|
||||
</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.Yaml}")">
|
||||
Add YAML Playout
|
||||
</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.ExternalJson}")">
|
||||
Add External Json Playout
|
||||
</MudButton>
|
||||
</MudTooltip>
|
||||
<MudMenu>
|
||||
<ActivatorContent>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled" Color="Color.Primary">Add Playout</MudButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Add" Label="Classic Schedule" Href="playouts/add"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Add" Label="Block Schedule" Href="@($"playouts/add/{PlayoutKind.Block}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Add" Label="Sequential Schedule" Href="@($"playouts/add/{PlayoutKind.Sequential}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Warning" Label="JSON (dizqueTV) Schedule" Href="@($"playouts/add/{PlayoutKind.ExternalJson}")"/>
|
||||
</ChildContent>
|
||||
</MudMenu>
|
||||
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Warning" StartIcon="@Icons.Material.Filled.Refresh" OnClick="@ResetAllPlayouts">
|
||||
Reset All Playouts
|
||||
</MudButton>
|
||||
@@ -42,11 +35,11 @@
|
||||
<div style="align-items: center; display: flex; margin-left: auto;" class="d-md-none">
|
||||
<div class="flex-grow-1"></div>
|
||||
<MudMenu Icon="@Icons.Material.Filled.MoreVert">
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Add" Label="Add Playout" Href="playouts/add"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Warning" Label="Add Block Playout" Href="@($"playouts/add/{PlayoutKind.Block}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Warning" Label="Add YAML Playout" Href="@($"playouts/add/{PlayoutKind.Yaml}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Warning" Label="Add External Json Playout" Href="@($"playouts/add/{PlayoutKind.ExternalJson}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Refresh" Label="Reset All Playouts" OnClick="ResetAllPlayouts"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Add" Label="Add Classic Playout" Href="playouts/add"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Add" Label="Add Block Playout" Href="@($"playouts/add/{PlayoutKind.Block}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Add" Label="Add Sequential Playout" Href="@($"playouts/add/{PlayoutKind.Sequential}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Warning" Label="Add JSON (dizqueTV) Playout" Href="@($"playouts/add/{PlayoutKind.ExternalJson}")"/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Refresh" Label="Reset All Playouts" OnClick="@ResetAllPlayouts"/>
|
||||
</MudMenu>
|
||||
</div>
|
||||
</div>
|
||||
@@ -80,23 +73,23 @@
|
||||
Default Schedule
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh Class="d-none d-md-table-cell">Playout Type</MudTh>
|
||||
<MudTh Class="d-none d-md-table-cell">Schedule Kind</MudTh>
|
||||
<MudTh/>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.ChannelNumber - @context.ChannelName</MudTd>
|
||||
<MudTd Class="d-none d-md-table-cell">@context.ScheduleName</MudTd>
|
||||
<MudTd Class="d-none d-md-table-cell">
|
||||
@switch (context.PlayoutType)
|
||||
@switch (context.ScheduleKind)
|
||||
{
|
||||
case ProgramSchedulePlayoutType.Block:
|
||||
case PlayoutScheduleKind.Block:
|
||||
<span>Block</span>
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.Yaml:
|
||||
<span>YAML</span>
|
||||
case PlayoutScheduleKind.Sequential:
|
||||
<span>Sequential</span>
|
||||
break;
|
||||
case ProgramSchedulePlayoutType.ExternalJson:
|
||||
<span>External Json</span>
|
||||
case PlayoutScheduleKind.ExternalJson:
|
||||
<span>JSON (dizqueTV)</span>
|
||||
break;
|
||||
default:
|
||||
<span></span>
|
||||
@@ -111,7 +104,7 @@
|
||||
<MudProgressCircular Color="Color.Primary" Size="Size.Small" Indeterminate="true"/>
|
||||
}
|
||||
</div>
|
||||
@if (context.PlayoutType == ProgramSchedulePlayoutType.Classic)
|
||||
@if (context.ScheduleKind == PlayoutScheduleKind.Classic)
|
||||
{
|
||||
if (context.PlayoutMode is ChannelPlayoutMode.OnDemand)
|
||||
{
|
||||
@@ -144,7 +137,7 @@
|
||||
</MudIconButton>
|
||||
</MudTooltip>
|
||||
}
|
||||
else if (context.PlayoutType == ProgramSchedulePlayoutType.ExternalJson)
|
||||
else if (context.ScheduleKind == PlayoutScheduleKind.ExternalJson)
|
||||
{
|
||||
<MudTooltip Text="Edit External Json File">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||
@@ -155,7 +148,7 @@
|
||||
<div style="width: 48px"></div>
|
||||
<div style="width: 48px"></div>
|
||||
}
|
||||
else if (context.PlayoutType == ProgramSchedulePlayoutType.Yaml)
|
||||
else if (context.ScheduleKind == PlayoutScheduleKind.Sequential)
|
||||
{
|
||||
<MudTooltip Text="Edit Playout">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||
@@ -171,7 +164,7 @@
|
||||
</MudTooltip>
|
||||
<div style="width: 48px"></div>
|
||||
}
|
||||
else if (context.PlayoutType == ProgramSchedulePlayoutType.Block)
|
||||
else if (context.ScheduleKind == PlayoutScheduleKind.Block)
|
||||
{
|
||||
<MudTooltip Text="Edit Playout">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||
@@ -297,8 +290,8 @@
|
||||
|
||||
private async Task PlayoutSelected(PlayoutNameViewModel playout)
|
||||
{
|
||||
// only show details for flood, block and YAML playouts
|
||||
_selectedPlayoutId = playout.PlayoutType is ProgramSchedulePlayoutType.Classic or ProgramSchedulePlayoutType.Block or ProgramSchedulePlayoutType.Yaml
|
||||
// only show details for flood, block and sequential playouts
|
||||
_selectedPlayoutId = playout.ScheduleKind is PlayoutScheduleKind.Classic or PlayoutScheduleKind.Block or PlayoutScheduleKind.Sequential
|
||||
? playout.PlayoutId
|
||||
: null;
|
||||
|
||||
|
||||
+8
-8
@@ -7,21 +7,21 @@
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IMediator Mediator
|
||||
@inject IEntityLocker EntityLocker;
|
||||
@inject ILogger<YamlPlayoutEditor> Logger
|
||||
@inject ILogger<SequentialPlayoutEditor> Logger
|
||||
|
||||
<MudForm Style="max-height: 100%">
|
||||
<MudPaper Square="true" Style="display: flex; height: 64px; min-height: 64px; width: 100%; z-index: 100; align-items: center">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Class="ml-6" OnClick="@(_ => SaveChanges())" StartIcon="@Icons.Material.Filled.Save">
|
||||
Save YAML File
|
||||
Save Sequential Schedule
|
||||
</MudButton>
|
||||
</MudPaper>
|
||||
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto">
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
|
||||
<MudText Typo="Typo.h5" Class="mb-2">@_channelName - YAML Playout</MudText>
|
||||
<MudText Typo="Typo.h5" Class="mb-2">@_channelName - Sequential Schedule</MudText>
|
||||
<MudDivider Class="mb-6"/>
|
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
|
||||
<div class="d-flex">
|
||||
<MudText>YAML File</MudText>
|
||||
<MudText>Sequential Schedule</MudText>
|
||||
</div>
|
||||
<MudTextField @bind-Value="@_playout.TemplateFile" For="@(() => _playout.TemplateFile)"/>
|
||||
</MudStack>
|
||||
@@ -92,14 +92,14 @@
|
||||
}
|
||||
|
||||
Either<BaseError, PlayoutNameViewModel> result =
|
||||
await Mediator.Send(new UpdateYamlPlayout(_playout.PlayoutId, _playout.TemplateFile), _cts.Token);
|
||||
await Mediator.Send(new UpdateSequentialPlayout(_playout.PlayoutId, _playout.TemplateFile), _cts.Token);
|
||||
|
||||
result.Match(
|
||||
_ => { Snackbar.Add($"Saved YAML file for playout {_channelName}", Severity.Success); },
|
||||
_ => { Snackbar.Add($"Saved sequential schedule for playout {_channelName}", Severity.Success); },
|
||||
error =>
|
||||
{
|
||||
Snackbar.Add($"Unexpected error saving YAML file: {error.Value}", Severity.Error);
|
||||
Logger.LogError("Unexpected error saving YAML file: {Error}", error.Value);
|
||||
Snackbar.Add($"Unexpected error saving sequential schedule: {error.Value}", Severity.Error);
|
||||
Logger.LogError("Unexpected error saving sequential schedule: {Error}", error.Value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/system/troubleshooting/yaml"
|
||||
@using ErsatzTV.Core.Interfaces.Scheduling
|
||||
@implements IDisposable
|
||||
@inject IYamlScheduleValidator YamlScheduleValidator
|
||||
@inject ISequentialScheduleValidator SequentialScheduleValidator
|
||||
|
||||
<MudForm Style="max-height: 100%">
|
||||
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto">
|
||||
@@ -99,8 +99,8 @@
|
||||
_yamlText = await File.ReadAllTextAsync(_yamlFile);
|
||||
try
|
||||
{
|
||||
_jsonText = YamlScheduleValidator.ToJson(_yamlText);
|
||||
IList<string> messages = await YamlScheduleValidator.GetValidationMessages(_yamlText, _isImport);
|
||||
_jsonText = SequentialScheduleValidator.ToJson(_yamlText);
|
||||
IList<string> messages = await SequentialScheduleValidator.GetValidationMessages(_yamlText, _isImport);
|
||||
_messagesCount = messages.Count;
|
||||
_messages = string.Join("\n", messages);
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@ namespace ErsatzTV;
|
||||
public static class PlayoutKind
|
||||
{
|
||||
public const string ExternalJson = "externaljson";
|
||||
public const string Yaml = "yaml";
|
||||
public const string Sequential = "sequential";
|
||||
public const string Block = "block";
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://ersatztv.org/yaml-playout.schema.json",
|
||||
"title": "YAML Playout Import",
|
||||
"description": "An ErsatzTV YAML playout import definition",
|
||||
"$id": "https://ersatztv.org/sequential-schedule.schema.json",
|
||||
"title": "Sequential Schedule Import",
|
||||
"description": "An ErsatzTV sequential schedule import definition",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://ersatztv.org/yaml-playout.schema.json",
|
||||
"title": "YAML Playout",
|
||||
"description": "An ErsatzTV YAML playout definition",
|
||||
"$id": "https://ersatztv.org/sequential-schedule.schema.json",
|
||||
"title": "Sequential Schedule",
|
||||
"description": "An ErsatzTV sequential schedule definition",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"import": {
|
||||
@@ -24,8 +24,8 @@ public class ResourceExtractorService : BackgroundService
|
||||
await ExtractResource(assembly, "song_progress_overlay.png", stoppingToken);
|
||||
await ExtractResource(assembly, "song_progress_overlay_43.png", stoppingToken);
|
||||
await ExtractResource(assembly, "ErsatzTV.png", stoppingToken);
|
||||
await ExtractResource(assembly, "yaml-playout.schema.json", stoppingToken);
|
||||
await ExtractResource(assembly, "yaml-playout-import.schema.json", stoppingToken);
|
||||
await ExtractResource(assembly, "sequential-schedule.schema.json", stoppingToken);
|
||||
await ExtractResource(assembly, "sequential-schedule-import.schema.json", stoppingToken);
|
||||
|
||||
await ExtractFontResource(assembly, "Sen.ttf", stoppingToken);
|
||||
await ExtractFontResource(assembly, "Roboto-Regular.ttf", stoppingToken);
|
||||
|
||||
+1
-1
@@ -745,7 +745,7 @@ public class Startup
|
||||
services.AddScoped<IJellyfinSecretStore, JellyfinSecretStore>();
|
||||
services.AddScoped<IEmbySecretStore, EmbySecretStore>();
|
||||
services.AddScoped<IScriptEngine, ScriptEngine>();
|
||||
services.AddScoped<IYamlScheduleValidator, YamlScheduleValidator>();
|
||||
services.AddScoped<ISequentialScheduleValidator, SequentialScheduleValidator>();
|
||||
|
||||
services.AddScoped<PlexEtag>();
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ public class PlayoutEditViewModel
|
||||
public ChannelViewModel Channel { get; set; }
|
||||
public ProgramScheduleViewModel ProgramSchedule { get; set; }
|
||||
public string ExternalJsonFile { get; set; }
|
||||
public string YamlFile { get; set; }
|
||||
public string SequentialSchedule { get; set; }
|
||||
|
||||
public CreatePlayout ToCreate() =>
|
||||
Kind switch
|
||||
{
|
||||
PlayoutKind.ExternalJson => new CreateExternalJsonPlayout(Channel.Id, ExternalJsonFile),
|
||||
PlayoutKind.Yaml => new CreateYamlPlayout(Channel.Id, YamlFile),
|
||||
PlayoutKind.Sequential => new CreateSequentialPlayout(Channel.Id, SequentialSchedule),
|
||||
PlayoutKind.Block => new CreateBlockPlayout(Channel.Id),
|
||||
_ => new CreateFloodPlayout(Channel.Id, ProgramSchedule.Id)
|
||||
_ => new CreateClassicPlayout(Channel.Id, ProgramSchedule.Id)
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user