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