156 lines
5.7 KiB
C#
156 lines
5.7 KiB
C#
using ErsatzTV.Core;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Domain.Filler;
|
|
using ErsatzTV.Core.Errors;
|
|
using ErsatzTV.Core.Scheduling;
|
|
using ErsatzTV.Infrastructure.Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace ErsatzTV.Application.ChannelTemplates;
|
|
|
|
public abstract record ChannelTemplateCommandBase(
|
|
string Name,
|
|
string Description,
|
|
int FFmpegProfileId,
|
|
int? WatermarkId,
|
|
int? FallbackFillerId,
|
|
int? PreRollFillerId,
|
|
int? MidRollFillerId,
|
|
int? PostRollFillerId,
|
|
ChannelStreamSelectorMode StreamSelectorMode,
|
|
string StreamSelector,
|
|
string PreferredAudioLanguageCode,
|
|
string PreferredAudioTitle,
|
|
ChannelPlayoutSource PlayoutSource,
|
|
ChannelPlayoutMode PlayoutMode,
|
|
StreamingMode StreamingMode,
|
|
string PreferredSubtitleLanguageCode,
|
|
ChannelSubtitleMode SubtitleMode,
|
|
ChannelMusicVideoCreditsMode MusicVideoCreditsMode,
|
|
string MusicVideoCreditsTemplate,
|
|
ChannelSongVideoMode SongVideoMode,
|
|
ChannelTranscodeMode TranscodeMode,
|
|
ChannelIdleBehavior IdleBehavior,
|
|
bool ShuffleScheduleItems,
|
|
bool RandomStartPoint,
|
|
FixedStartTimeBehavior FixedStartTimeBehavior)
|
|
{
|
|
internal static async Task<Option<BaseError>> ValidateCommon(
|
|
TvContext dbContext,
|
|
ChannelTemplateCommandBase request,
|
|
int? existingTemplateId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
string name = NormalizeName(request.Name);
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
return BaseError.New("Name is required.");
|
|
}
|
|
|
|
if (name.Length > 50)
|
|
{
|
|
return BaseError.New("Name must be 50 characters or less.");
|
|
}
|
|
|
|
if (request.Description?.Length > 500)
|
|
{
|
|
return BaseError.New("Description must be 500 characters or less.");
|
|
}
|
|
|
|
bool duplicateName = await dbContext.ChannelTemplates
|
|
.AnyAsync(t => t.Id != existingTemplateId && t.Name == name, cancellationToken);
|
|
if (duplicateName)
|
|
{
|
|
return BaseError.New("Channel template name must be unique.");
|
|
}
|
|
|
|
bool ffmpegProfileExists = await dbContext.FFmpegProfiles
|
|
.AnyAsync(p => p.Id == request.FFmpegProfileId, cancellationToken);
|
|
if (!ffmpegProfileExists)
|
|
{
|
|
return new NotFoundError($"FFmpegProfile {request.FFmpegProfileId} does not exist.");
|
|
}
|
|
|
|
foreach (int watermarkId in Optional(request.WatermarkId))
|
|
{
|
|
bool watermarkExists = await dbContext.ChannelWatermarks
|
|
.AnyAsync(w => w.Id == watermarkId, cancellationToken);
|
|
if (!watermarkExists)
|
|
{
|
|
return new NotFoundError($"Watermark {watermarkId} does not exist.");
|
|
}
|
|
}
|
|
|
|
Option<BaseError> maybeFillerError =
|
|
await FillerMustExist(dbContext, request.FallbackFillerId, FillerKind.Fallback, cancellationToken);
|
|
if (maybeFillerError.IsSome)
|
|
{
|
|
return maybeFillerError;
|
|
}
|
|
|
|
maybeFillerError = await FillerMustExist(dbContext, request.PreRollFillerId, FillerKind.PreRoll, cancellationToken);
|
|
if (maybeFillerError.IsSome)
|
|
{
|
|
return maybeFillerError;
|
|
}
|
|
|
|
maybeFillerError = await FillerMustExist(dbContext, request.MidRollFillerId, FillerKind.MidRoll, cancellationToken);
|
|
if (maybeFillerError.IsSome)
|
|
{
|
|
return maybeFillerError;
|
|
}
|
|
|
|
return await FillerMustExist(dbContext, request.PostRollFillerId, FillerKind.PostRoll, cancellationToken);
|
|
}
|
|
|
|
internal void ApplyTo(ChannelTemplate template)
|
|
{
|
|
template.Name = NormalizeName(Name);
|
|
template.Description = Description ?? string.Empty;
|
|
template.FFmpegProfileId = FFmpegProfileId;
|
|
template.WatermarkId = WatermarkId;
|
|
template.FallbackFillerId = FallbackFillerId;
|
|
template.PreRollFillerId = PreRollFillerId;
|
|
template.MidRollFillerId = MidRollFillerId;
|
|
template.PostRollFillerId = PostRollFillerId;
|
|
template.StreamSelectorMode = StreamSelectorMode;
|
|
template.StreamSelector = StreamSelector ?? string.Empty;
|
|
template.PreferredAudioLanguageCode = PreferredAudioLanguageCode ?? string.Empty;
|
|
template.PreferredAudioTitle = PreferredAudioTitle ?? string.Empty;
|
|
template.PlayoutSource = PlayoutSource;
|
|
template.PlayoutMode = PlayoutMode;
|
|
template.StreamingMode = StreamingMode;
|
|
template.PreferredSubtitleLanguageCode = PreferredSubtitleLanguageCode ?? string.Empty;
|
|
template.SubtitleMode = SubtitleMode;
|
|
template.MusicVideoCreditsMode = MusicVideoCreditsMode;
|
|
template.MusicVideoCreditsTemplate = MusicVideoCreditsTemplate ?? string.Empty;
|
|
template.SongVideoMode = SongVideoMode;
|
|
template.TranscodeMode = TranscodeMode;
|
|
template.IdleBehavior = IdleBehavior;
|
|
template.ShuffleScheduleItems = ShuffleScheduleItems;
|
|
template.RandomStartPoint = RandomStartPoint;
|
|
template.FixedStartTimeBehavior = FixedStartTimeBehavior;
|
|
}
|
|
|
|
internal static string NormalizeName(string name) => (name ?? string.Empty).Trim();
|
|
|
|
private static async Task<Option<BaseError>> FillerMustExist(
|
|
TvContext dbContext,
|
|
int? fillerPresetId,
|
|
FillerKind fillerKind,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
foreach (int id in Optional(fillerPresetId))
|
|
{
|
|
bool exists = await dbContext.FillerPresets
|
|
.AnyAsync(f => f.Id == id && f.FillerKind == fillerKind, cancellationToken);
|
|
if (!exists)
|
|
{
|
|
return new NotFoundError($"{fillerKind} filler {id} does not exist.");
|
|
}
|
|
}
|
|
|
|
return Option<BaseError>.None;
|
|
}
|
|
}
|