diff --git a/ErsatzTV.Application/ErsatzTV.Application.csproj b/ErsatzTV.Application/ErsatzTV.Application.csproj index 80a34e9d..78383436 100644 --- a/ErsatzTV.Application/ErsatzTV.Application.csproj +++ b/ErsatzTV.Application/ErsatzTV.Application.csproj @@ -13,8 +13,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj index 2e2a3e43..de758ffb 100644 --- a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj +++ b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj @@ -10,11 +10,11 @@ - - - - - + + + + + all diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj b/ErsatzTV.Core/ErsatzTV.Core.csproj index 7f1ccc8d..b7404ba4 100644 --- a/ErsatzTV.Core/ErsatzTV.Core.csproj +++ b/ErsatzTV.Core/ErsatzTV.Core.csproj @@ -15,16 +15,16 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj index 6fddd381..9594d19b 100644 --- a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj +++ b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj index 5f06017c..419a3e02 100644 --- a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj +++ b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj b/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj index 8d288a13..b0204f8b 100644 --- a/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj +++ b/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj @@ -16,7 +16,7 @@ - + diff --git a/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj b/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj index d016f619..f7a1eed0 100644 --- a/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj +++ b/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj index cf932ffe..8d38918a 100644 --- a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj +++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj @@ -18,12 +18,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj index 446c948c..e6200d33 100644 --- a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj +++ b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj @@ -24,9 +24,9 @@ - - - + + + diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 54abd0e6..c5af2d41 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -25,11 +25,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ErsatzTV/Pages/ChannelEditor.razor b/ErsatzTV/Pages/ChannelEditor.razor index 914b8dcd..5f835437 100644 --- a/ErsatzTV/Pages/ChannelEditor.razor +++ b/ErsatzTV/Pages/ChannelEditor.razor @@ -19,241 +19,241 @@ @inject IMediator Mediator - - @(IsEdit ? "Save Channel" : "Add Channel") - -
- - Channel - - -
- Number -
- -
- -
- Name -
- -
- -
- Group -
- -
- -
- Categories -
- -
- -
- Active Mode -
- - Active - Hidden - Inactive - -
- -
- Progress Mode -
- - Always - On Demand - -
- -
- Streaming Mode -
- - MPEG-TS - MPEG-TS (Legacy) - HLS Direct - HLS Segmenter - HLS Segmenter V2 - -
- -
- FFmpeg Profile -
- - @foreach (FFmpegProfileViewModel profile in _ffmpegProfiles) - { - @profile.Name - } - -
- -
- Stream Selector Mode -
- - Default - Custom - -
- @if (_model.StreamSelectorMode is ChannelStreamSelectorMode.Default) - { + + @(IsEdit ? "Save Channel" : "Add Channel") + +
+ + Channel +
- Preferred Audio Language + Number
- - (none) - @foreach (LanguageCodeViewModel culture in _availableCultures) - { - @culture.EnglishName - } + +
+ +
+ Name +
+ +
+ +
+ Group +
+ +
+ +
+ Categories +
+ +
+ +
+ Active Mode +
+ + Active + Hidden + Inactive
- Preferred Audio Title + Progress Mode
- + + Always + On Demand +
- Preferred Subtitle Language + Streaming Mode
- - (none) - @foreach (LanguageCodeViewModel culture in _availableCultures) + + MPEG-TS + MPEG-TS (Legacy) + HLS Direct + HLS Segmenter + HLS Segmenter V2 + +
+ +
+ FFmpeg Profile +
+ + @foreach (FFmpegProfileViewModel profile in _ffmpegProfiles) { - @culture.EnglishName + @profile.Name }
- Subtitle Mode + Stream Selector Mode
- - None - Forced - Default - Any + + Default + Custom + +
+ @if (_model.StreamSelectorMode is ChannelStreamSelectorMode.Default) + { + +
+ Preferred Audio Language +
+ + (none) + @foreach (LanguageCodeViewModel culture in _availableCultures) + { + @culture.EnglishName + } + +
+ +
+ Preferred Audio Title +
+ +
+ +
+ Preferred Subtitle Language +
+ + (none) + @foreach (LanguageCodeViewModel culture in _availableCultures) + { + @culture.EnglishName + } + +
+ +
+ Subtitle Mode +
+ + None + Forced + Default + Any + +
+ } + else + { + +
+ Stream Selector +
+ + (none) + @foreach (string selector in _streamSelectors) + { + @selector + } + +
+ } + +
+ Music Video Credits Mode +
+ + None + Generate Subtitles
- } - else - {
- Stream Selector + Music Video Credits Template
- + (none) - @foreach (string selector in _streamSelectors) + @foreach (string template in _musicVideoCreditsTemplates) { - @selector + @template }
- } - -
- Music Video Credits Mode -
- - None - Generate Subtitles - -
- -
- Music Video Credits Template -
- - (none) - @foreach (string template in _musicVideoCreditsTemplates) - { - @template - } - -
- -
- Song Video Mode -
- - Default - With Progress - -
- -
- Logo -
-
- -
- External Logo URL -
- -
- -
- Logo Preview -
- @if (!string.IsNullOrWhiteSpace(_model.Logo?.Path) || !string.IsNullOrWhiteSpace(_model.ExternalLogoUrl)) - { - - } -
- -
- Watermark -
- - (none) - @foreach (WatermarkViewModel watermark in _watermarks) - { - @watermark.Name - } - -
- -
- Fallback Filler -
- - (none) - @foreach (FillerPresetViewModel fillerPreset in _fillerPresets) + +
+ Song Video Mode +
+ + Default + With Progress + +
+ +
+ Logo +
+
+ +
+ External Logo URL +
+ +
+ +
+ Logo Preview +
+ @if (!string.IsNullOrWhiteSpace(_model.Logo?.Path) || !string.IsNullOrWhiteSpace(_model.ExternalLogoUrl)) { - @fillerPreset.Name + } -
-
-
-
+ + +
+ Watermark +
+ + (none) + @foreach (WatermarkViewModel watermark in _watermarks) + { + @watermark.Name + } + +
+ +
+ Fallback Filler +
+ + (none) + @foreach (FillerPresetViewModel fillerPreset in _fillerPresets) + { + @fillerPreset.Name + } + +
+
+
@code { diff --git a/ErsatzTV/Pages/FillerPresetEditor.razor b/ErsatzTV/Pages/FillerPresetEditor.razor index f46dc08c..b3cf4275 100644 --- a/ErsatzTV/Pages/FillerPresetEditor.razor +++ b/ErsatzTV/Pages/FillerPresetEditor.razor @@ -6,137 +6,181 @@ @using ErsatzTV.Application.MediaItems @using ErsatzTV.Application.Television @using ErsatzTV.Core.Domain.Filler +@using ErsatzTV.Validators +@using FluentValidation.Results @implements IDisposable @inject NavigationManager NavigationManager @inject ILogger Logger @inject ISnackbar Snackbar @inject IMediator Mediator - -
- @(IsEdit ? "Edit Filler Preset" : "Add Filler Preset") - - @if (_editContext is not null) - { - - - - - - - Pre-Roll - Mid-Roll - Post-Roll - Tail - Fallback - - - Duration - Count - Pad - Random Count - - - - - 5 (:00, :05, :10, :15, :20, etc) - 10 (:00, :10, :20, :30, :40, :50) - 15 (:00, :15, :30, :45) - 30 (:00, :30) - - - - Collection - Television Show - Television Season - Artist - Multi Collection - Smart Collection - - @if (_model.CollectionType == ProgramScheduleItemCollectionType.Collection) + + + @(IsEdit ? "Save Filler Preset" : "Add Filler Preset") + +
+ + Filler Preset + + +
+ Name +
+ +
+ +
+ Kind +
+ + Pre-Roll + Mid-Roll + Post-Roll + Tail + Fallback + +
+ +
+ Mode +
+ + Duration + Count + Pad + Random Count + +
+ +
+ Duration +
+ +
+ +
+ Count +
+ +
+ +
+ Pad To Nearest Minute +
+ + 5 (:00, :05, :10, :15, :20, etc) + 10 (:00, :10, :20, :30, :40, :50) + 15 (:00, :15, :30, :45) + 30 (:00, :30) + +
+ +
+ Allow Watermarks +
+ +
+ +
+ Collection Type +
+ + Collection + Television Show + Television Season + Artist + Multi Collection + Smart Collection + +
+ @if (_model.CollectionType == ProgramScheduleItemCollectionType.Collection) + { + +
+ Collection +
+ + @foreach (MediaCollectionViewModel collection in _mediaCollections) { - - @foreach (MediaCollectionViewModel collection in _mediaCollections) - { - @collection.Name - } - + @collection.Name } - @if (_model.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) + +
+ } + @if (_model.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) + { + +
+ Multi Collection +
+ + @foreach (MultiCollectionViewModel collection in _multiCollections) { - - @foreach (MultiCollectionViewModel collection in _multiCollections) - { - @collection.Name - } - + @collection.Name } - @if (_model.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) + +
+ } + @if (_model.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) + { + +
+ Smart Collection +
+ + @foreach (SmartCollectionViewModel collection in _smartCollections) { - - @foreach (SmartCollectionViewModel collection in _smartCollections) - { - @collection.Name - } - + @collection.Name } - @if (_model.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) + +
+ } + @if (_model.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) + { + +
+ Television Show +
+ + @foreach (NamedMediaItemViewModel show in _televisionShows) { - - @foreach (NamedMediaItemViewModel show in _televisionShows) - { - @show.Name - } - + @show.Name } - @if (_model.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) + +
+ } + @if (_model.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) + { + +
+ Television Season +
+ + @foreach (NamedMediaItemViewModel season in _televisionSeasons) { - - @foreach (NamedMediaItemViewModel season in _televisionSeasons) - { - @season.Name - } - + @season.Name } - @if (_model.CollectionType == ProgramScheduleItemCollectionType.Artist) + +
+ } + @if (_model.CollectionType == ProgramScheduleItemCollectionType.Artist) + { + +
+ Artist +
+ + @foreach (NamedMediaItemViewModel artist in _artists) { - - @foreach (NamedMediaItemViewModel artist in _artists) - { - @artist.Name - } - + @artist.Name } - - - - @(IsEdit ? "Save Changes" : "Add Filler Preset") - - - - - } + +
+ } +
- +
@code { private readonly CancellationTokenSource _cts = new(); @@ -145,15 +189,15 @@ public int Id { get; set; } private readonly FillerPresetEditViewModel _model = new(); - private EditContext _editContext; - private ValidationMessageStore _messageStore; + private readonly FillerPresetEditViewModelValidator _validator = new(); + private MudForm _form; - private List _mediaCollections = new(); - private List _multiCollections = new(); - private List _smartCollections = new(); - private List _televisionShows = new(); - private List _televisionSeasons = new(); - private List _artists = new(); + private List _mediaCollections = []; + private List _multiCollections = []; + private List _smartCollections = []; + private List _televisionShows = []; + private List _televisionSeasons = []; + private List _artists = []; public void Dispose() { @@ -212,23 +256,17 @@ } } - protected override void OnInitialized() - { - _editContext = new EditContext(_model); - _messageStore = new ValidationMessageStore(_editContext); - } - private bool IsEdit => Id != 0; private async Task HandleSubmitAsync() { - _messageStore.Clear(); - if (_editContext.Validate()) + await _form.Validate(); + ValidationResult result = await _validator.ValidateAsync(_model, _cts.Token); + if (result.IsValid) { IRequest> request = IsEdit ? _model.ToEdit() : _model.ToUpdate(); - Seq errorMessage = await Mediator.Send(request, _cts.Token) - .Map(result => result.LeftToSeq()); + Seq errorMessage = (await Mediator.Send(request, _cts.Token)).LeftToSeq(); errorMessage.HeadOrNone().Match( error => diff --git a/ErsatzTV/Pages/FillerPresets.razor b/ErsatzTV/Pages/FillerPresets.razor index b89fc5e8..b04e362c 100644 --- a/ErsatzTV/Pages/FillerPresets.razor +++ b/ErsatzTV/Pages/FillerPresets.razor @@ -6,66 +6,70 @@ @inject IDialogService Dialog @inject IMediator Mediator - -
- + + + Add Filler Preset + +
+ + Filler Presets + + + + + + + + + + + Name + Filler Kind + + + + @context.Name + + @( + context.FillerKind switch + { + FillerKind.PreRoll => "Pre-Roll", + FillerKind.MidRoll => "Mid-Roll", + FillerKind.PostRoll => "Post-Roll", + FillerKind.Fallback => "Fallback", + FillerKind.Tail => "Tail", + _ => "None" + } + ) + + +
+ + + + + + + + +
+
+
+ + + +
+
- - - Filler Presets - - - - - - - - Name - Filler Kind - - - - @context.Name - - @( - context.FillerKind switch - { - FillerKind.PreRoll => "Pre-Roll", - FillerKind.MidRoll => "Mid-Roll", - FillerKind.PostRoll => "Post-Roll", - FillerKind.Fallback => "Fallback", - FillerKind.Tail => "Tail", - _ => "None" - } - ) - - -
- - - - - - - - -
-
-
- - - -
- +
@code { private readonly CancellationTokenSource _cts = new(); @@ -90,7 +94,7 @@ IDialogReference dialog = await Dialog.ShowAsync("Delete Filler Preset", parameters, options); DialogResult result = await dialog.Result; - if (!result.Canceled) + if (result is { Canceled: false }) { await Mediator.Send(new DeleteFillerPreset(fillerPreset.Id), _cts.Token); if (_fillerPresetsTable != null) diff --git a/ErsatzTV/Pages/PlayoutEditor.razor b/ErsatzTV/Pages/PlayoutEditor.razor index 60115554..96f60a32 100644 --- a/ErsatzTV/Pages/PlayoutEditor.razor +++ b/ErsatzTV/Pages/PlayoutEditor.razor @@ -3,89 +3,99 @@ @using System.Globalization @using ErsatzTV.Application.Channels @using ErsatzTV.Application.ProgramSchedules +@using ErsatzTV.Validators +@using FluentValidation.Results @implements IDisposable @inject NavigationManager NavigationManager @inject ILogger Logger @inject ISnackbar Snackbar @inject IMediator Mediator - - - @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; - } - -
- - - - - - @foreach (ChannelViewModel channel in _channels) - { - - @($"{channel.Number} - {channel.Name}") - - } - - @switch (Kind) + + + + @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; + } + + +
+ + Playout + + +
+ Channel +
+ + @foreach (ChannelViewModel channel in _channels) { - case PlayoutKind.ExternalJson: - - break; - case PlayoutKind.Yaml: - - break; - case PlayoutKind.Block: - break; - default: - - @foreach (ProgramScheduleViewModel schedule in _programSchedules) - { - @schedule.Name - } - - break; + + @($"{channel.Number} - {channel.Name}") + } - - - - Add Playout - - - - + +
+ @switch (Kind) + { + case PlayoutKind.ExternalJson: + +
+ External Json File +
+ +
+ break; + case PlayoutKind.Yaml: + +
+ YAML File +
+ +
+ break; + case PlayoutKind.Block: + break; + default: + +
+ Schedule +
+ + @foreach (ProgramScheduleViewModel schedule in _programSchedules) + { + @schedule.Name + } + +
+ break; + } +
- +
@code { private readonly CancellationTokenSource _cts = new(); private readonly PlayoutEditViewModel _model = new(); + private readonly PlayoutEditViewModelValidator _validator = new(); + private MudForm _form; + private List _channels = []; private List _programSchedules = []; - private EditContext _editContext; - private ValidationMessageStore _messageStore; - [Parameter] public string Kind { get; set; } @@ -109,16 +119,11 @@ } } - protected override void OnInitialized() - { - _editContext = new EditContext(_model); - _messageStore = new ValidationMessageStore(_editContext); - } - private async Task HandleSubmitAsync() { - _messageStore.Clear(); - if (_editContext.Validate()) + await _form.Validate(); + ValidationResult result = await _validator.ValidateAsync(_model, _cts.Token); + if (result.IsValid) { Seq errorMessage = (await Mediator.Send(_model.ToCreate(), _cts.Token)).LeftToSeq(); diff --git a/ErsatzTV/Pages/Playouts.razor b/ErsatzTV/Pages/Playouts.razor index 0d6290d3..958d7741 100644 --- a/ErsatzTV/Pages/Playouts.razor +++ b/ErsatzTV/Pages/Playouts.razor @@ -12,208 +12,226 @@ @inject IEntityLocker EntityLocker; @inject ICourier Courier; - -
- - Add Playout - - - - Add Block Playout - - - - - Add YAML Playout - - - - - Add External Json Playout - - - - Reset All Playouts - -
- - - Playouts - - - - - - - - - - - Channel - - - - - Default Schedule - - - Playout Type - - - - @context.ChannelNumber - @context.ChannelName - @context.ScheduleName - - @switch (context.PlayoutType) - { - case ProgramSchedulePlayoutType.Block: - Block - break; - case ProgramSchedulePlayoutType.Yaml: - YAML - break; - case ProgramSchedulePlayoutType.ExternalJson: - External Json - break; - default: - - break; - } - - -
-
- @if (EntityLocker.IsPlayoutLocked(context.PlayoutId)) - { - - } -
- @if (context.PlayoutType == ProgramSchedulePlayoutType.Flood) - { - if (context.ProgressMode is ChannelProgressMode.OnDemand) + + +
+
+
+ + Add Playout + + + + Add Block Playout + + + + + Add YAML Playout + + + + + Add External Json Playout + + + + Reset All Playouts + +
+
+
+ + + + + + + +
+
+
+
+ + Playouts + + + + + + + + + + + + + + Channel + + + + + Default Schedule + + + Playout Type + + + + @context.ChannelNumber - @context.ChannelName + @context.ScheduleName + + @switch (context.PlayoutType) { - - - - + case ProgramSchedulePlayoutType.Block: + Block + break; + case ProgramSchedulePlayoutType.Yaml: + YAML + break; + case ProgramSchedulePlayoutType.ExternalJson: + External Json + break; + default: + + break; } - else - { - - + +
+
+ @if (EntityLocker.IsPlayoutLocked(context.PlayoutId)) + { + + } +
+ @if (context.PlayoutType == ProgramSchedulePlayoutType.Flood) + { + if (context.ProgressMode is ChannelProgressMode.OnDemand) + { + + + + + } + else + { + + + + + } + + + + + + + + + + } + else if (context.PlayoutType == ProgramSchedulePlayoutType.ExternalJson) + { + + + + +
+
+ } + else if (context.PlayoutType == ProgramSchedulePlayoutType.Yaml) + { + + + + + + + + +
+ } + else if (context.PlayoutType == ProgramSchedulePlayoutType.Block) + { + + + + + + + + +
+ } + + + OnClick="@(_ => DeletePlayout(context))"> - } - - - - - - - - - - } - else if (context.PlayoutType == ProgramSchedulePlayoutType.ExternalJson) - { - - - - -
-
- } - else if (context.PlayoutType == ProgramSchedulePlayoutType.Yaml) - { - - - - - - - - -
- } - else if (context.PlayoutType == ProgramSchedulePlayoutType.Block) - { - - - - - - - - -
- } - - - - -
-
-
- - - -
- - @if (_selectedPlayoutId != null) - { - - - Playout Detail - - - - Start - Finish - Media Item - Duration - - - @context.Start.ToString("G", _dtf) - @context.Finish.ToString("G", _dtf) - @context.Title - @context.Duration - - - - - - } -
+
+ + + + + + + @if (_selectedPlayoutId != null) + { + Playout Detail + + + + + + + Start + Finish + Media Item + Duration + + + @context.Start.ToString("G", _dtf) + @context.Finish.ToString("G", _dtf) + @context.Title + @context.Duration + + + + + + } + +
+ @code { private readonly CancellationTokenSource _cts = new(); diff --git a/ErsatzTV/Pages/Schedules.razor b/ErsatzTV/Pages/Schedules.razor index 18e2913d..56cb2bdf 100644 --- a/ErsatzTV/Pages/Schedules.razor +++ b/ErsatzTV/Pages/Schedules.razor @@ -7,91 +7,100 @@ @inject IMediator Mediator @inject NavigationManager NavigationManager - - - - Schedules - - - - - - - - - Name - - - - - - @context.Name - -
- - - - - - - - - - - - - - - - -
-
-
- - - -
- - Add Schedule - - - @if (_selectedSchedule != null) - { - - - @_selectedSchedule.Name Items - - - Start Time - Collection - Playout Mode - - - - @(context.StartType == StartType.Fixed ? context.StartTime == null ? string.Empty : DateTime.Today.Add(context.StartTime.Value).ToShortTimeString() : "Dynamic") - - @context.Name - @context.PlayoutMode - - - - - - } -
+ + + + Add Schedule + + +
+ + Schedules + + + + + + + + + + + + Name + + + + + + @context.Name + +
+ + + + + + + + + + + + + + + + +
+
+
+ + + +
+ @if (_selectedSchedule != null) + { + + + + @_selectedSchedule.Name Items + + + Start Time + Collection + Playout Mode + + + + @(context.StartType == StartType.Fixed ? context.StartTime == null ? string.Empty : DateTime.Today.Add(context.StartTime.Value).ToShortTimeString() : "Dynamic") + + @context.Name + @context.PlayoutMode + + + + + + + } +
+
+
@code { private readonly CancellationTokenSource _cts = new(); @@ -132,7 +141,7 @@ IDialogReference dialog = await Dialog.ShowAsync("Delete Schedule", parameters, options); DialogResult result = await dialog.Result; - if (!result.Canceled) + if (result is { Canceled: false }) { await Mediator.Send(new DeleteProgramSchedule(programSchedule.Id), _cts.Token); if (_table != null) @@ -154,7 +163,7 @@ IDialogReference dialog = await Dialog.ShowAsync("Copy Schedule", parameters, options); DialogResult dialogResult = await dialog.Result; - if (!dialogResult.Canceled && dialogResult.Data is ProgramScheduleViewModel data) + if (dialogResult is { Canceled: false, Data: ProgramScheduleViewModel data }) { NavigationManager.NavigateTo($"schedules/{data.Id}/items"); } diff --git a/ErsatzTV/Pages/TraktLists.razor b/ErsatzTV/Pages/TraktLists.razor index 277e0cd4..6252898a 100644 --- a/ErsatzTV/Pages/TraktLists.razor +++ b/ErsatzTV/Pages/TraktLists.razor @@ -7,68 +7,74 @@ @inject IEntityLocker Locker @inject ChannelWriter WorkerChannel - -
- + + Add Trakt List + +
+ + Trakt Lists + + + + + + + + + + + + Id + Name + Match Status + + + + @context.Slug + @context.Name + @context.MatchCount of @context.ItemCount + +
+ + + + + + + + + + + + +
+
+
+ + + +
+
- - - Trakt Lists - - - - - - - - - Id - Name - Match Status - - - - @context.Slug - @context.Name - @context.MatchCount of @context.ItemCount - -
- - - - - - - - - - - - -
-
-
- - - -
- + @code { private readonly CancellationTokenSource _cts = new(); @@ -115,7 +121,7 @@ IDialogReference dialog = await Dialog.ShowAsync("Delete Trakt List", parameters, options); DialogResult result = await dialog.Result; - if (!result.Canceled) + if (result is { Canceled: false }) { await WorkerChannel.WriteAsync(new DeleteTraktList(traktList.Id), _cts.Token); } diff --git a/ErsatzTV/Pages/Trash.razor b/ErsatzTV/Pages/Trash.razor index ee675224..2956c6fa 100644 --- a/ErsatzTV/Pages/Trash.razor +++ b/ErsatzTV/Pages/Trash.razor @@ -623,7 +623,7 @@ IDialogReference dialog = await Dialog.ShowAsync("Delete From Database", parameters, options); DialogResult result = await dialog.Result; - if (!result.Canceled) + if (result is { Canceled: false }) { var request = new DeleteItemsFromDatabase( movieIds.Append(showIds) diff --git a/ErsatzTV/Pages/YamlPlayoutEditor.razor b/ErsatzTV/Pages/YamlPlayoutEditor.razor index 4d5dbe68..8aede4e6 100644 --- a/ErsatzTV/Pages/YamlPlayoutEditor.razor +++ b/ErsatzTV/Pages/YamlPlayoutEditor.razor @@ -9,53 +9,25 @@ @inject IMediator Mediator @inject IEntityLocker EntityLocker; - - Edit YAML Playout - @_channelName - - -
- - - - YAML File - - - - - Edit YAML File - - - -
-
- -
- - - - Playout Items and History - - - - - -
- - Erase Items and History - -
-
-
-
-
-
-
+ + + Edit YAML Playout - @_channelName + +
+ + YAML File + + + Edit YAML File + + Playout Items and History + + + Erase Items and History + + +
+
@code { private readonly CancellationTokenSource _cts = new(); diff --git a/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor b/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor index aacb4363..73faf632 100644 --- a/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor +++ b/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor @@ -5,68 +5,78 @@ @inject ISnackbar Snackbar @inject IMediator Mediator - - - - @_source?.Name Path Replacements - - - - - - - - @Name Path - Local Path - - - - - - @context.RemotePath - - - - - @context.LocalPath - - - - - - - - - - - - Add Path Replacement - - - Save Changes - - - @if (_selectedItem is not null) - { -
- - - - - - - - - -
- } -
+ + + + Save Path Replacements + + +
+ + @_source?.Name + + + + + + + + + + + @Name Path + Local Path + + + + + + @context.RemotePath + + + + + @context.LocalPath + + + + + + + + + + + + Add Path Replacement + + Path Replacement + + @if (_selectedItem is not null) + { + +
+ @($"{Name} Path") +
+ +
+ +
+ Local Path +
+ +
+ } +
+
+
@code { private readonly CancellationTokenSource _cts = new(); diff --git a/ErsatzTV/Validators/FillerPresetEditViewModelValidator.cs b/ErsatzTV/Validators/FillerPresetEditViewModelValidator.cs index 890ff81b..86b2c9e5 100644 --- a/ErsatzTV/Validators/FillerPresetEditViewModelValidator.cs +++ b/ErsatzTV/Validators/FillerPresetEditViewModelValidator.cs @@ -2,6 +2,7 @@ using ErsatzTV.Core.Domain.Filler; using ErsatzTV.ViewModels; using FluentValidation; +using FluentValidation.Results; namespace ErsatzTV.Validators; @@ -39,4 +40,18 @@ public class FillerPresetEditViewModelValidator : AbstractValidator RuleFor(fp => fp.MediaItem).NotNull()); } + + public Func>> ValidateValue => async (model, propertyName) => + { + ValidationResult result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (FillerPresetEditViewModel)model, + x => x.IncludeProperties(propertyName))); + if (result.IsValid) + { + return []; + } + + return result.Errors.Select(e => e.ErrorMessage); + }; } diff --git a/ErsatzTV/Validators/PlayoutEditViewModelValidator.cs b/ErsatzTV/Validators/PlayoutEditViewModelValidator.cs index 59989c04..5e869006 100644 --- a/ErsatzTV/Validators/PlayoutEditViewModelValidator.cs +++ b/ErsatzTV/Validators/PlayoutEditViewModelValidator.cs @@ -1,5 +1,6 @@ using ErsatzTV.ViewModels; using FluentValidation; +using FluentValidation.Results; namespace ErsatzTV.Validators; @@ -11,4 +12,18 @@ public class PlayoutEditViewModelValidator : AbstractValidator p.ProgramSchedule).NotNull().When(p => string.IsNullOrWhiteSpace(p.Kind)); RuleFor(p => p.ExternalJsonFile).NotNull().When(p => p.Kind == PlayoutKind.ExternalJson); } + + public Func>> ValidateValue => async (model, propertyName) => + { + ValidationResult result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (PlayoutEditViewModel)model, + x => x.IncludeProperties(propertyName))); + if (result.IsValid) + { + return []; + } + + return result.Errors.Select(e => e.ErrorMessage); + }; }