mirror of https://github.com/ErsatzTV/ErsatzTV.git
6 changed files with 730 additions and 0 deletions
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
using ErsatzTV.Core; |
||||
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public record UpdateScriptedPlayout(int PlayoutId, string ScheduleFile) |
||||
: IRequest<Either<BaseError, PlayoutNameViewModel>>; |
||||
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
using System.Threading.Channels; |
||||
using ErsatzTV.Application.Channels; |
||||
using ErsatzTV.Core; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using ErsatzTV.Infrastructure.Extensions; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public class |
||||
UpdateScriptedPlayoutHandler( |
||||
IDbContextFactory<TvContext> dbContextFactory, |
||||
ChannelWriter<IBackgroundServiceRequest> workerChannel) |
||||
: IRequestHandler<UpdateScriptedPlayout, |
||||
Either<BaseError, PlayoutNameViewModel>> |
||||
{ |
||||
public async Task<Either<BaseError, PlayoutNameViewModel>> Handle( |
||||
UpdateScriptedPlayout request, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
Validation<BaseError, Playout> validation = await Validate(dbContext, request); |
||||
return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout)); |
||||
} |
||||
|
||||
private async Task<PlayoutNameViewModel> ApplyUpdateRequest( |
||||
TvContext dbContext, |
||||
UpdateScriptedPlayout request, |
||||
Playout playout) |
||||
{ |
||||
playout.ScheduleFile = request.ScheduleFile; |
||||
|
||||
if (await dbContext.SaveChangesAsync() > 0) |
||||
{ |
||||
await workerChannel.WriteAsync(new RefreshChannelData(playout.Channel.Number)); |
||||
} |
||||
|
||||
return new PlayoutNameViewModel( |
||||
playout.Id, |
||||
playout.ScheduleKind, |
||||
playout.Channel.Name, |
||||
playout.Channel.Number, |
||||
playout.Channel.PlayoutMode, |
||||
playout.ProgramSchedule?.Name ?? string.Empty, |
||||
playout.ScheduleFile, |
||||
playout.DailyRebuildTime); |
||||
} |
||||
|
||||
private static Task<Validation<BaseError, Playout>> Validate( |
||||
TvContext dbContext, |
||||
UpdateScriptedPlayout request) => |
||||
PlayoutMustExist(dbContext, request); |
||||
|
||||
private static Task<Validation<BaseError, Playout>> PlayoutMustExist( |
||||
TvContext dbContext, |
||||
UpdateScriptedPlayout updatePlayout) => |
||||
dbContext.Playouts |
||||
.Include(p => p.Channel) |
||||
.SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId) |
||||
.Map(o => o.ToValidation<BaseError>("Playout does not exist.")); |
||||
} |
||||
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
@page "/playouts/scripted/{Id:int}" |
||||
@using ErsatzTV.Application.Channels |
||||
@using ErsatzTV.Application.Playouts |
||||
@using ErsatzTV.Application.Scheduling |
||||
@implements IDisposable |
||||
@inject NavigationManager NavigationManager |
||||
@inject ISnackbar Snackbar |
||||
@inject IMediator Mediator |
||||
@inject IEntityLocker EntityLocker; |
||||
@inject ILogger<ScriptedPlayoutEditor> Logger |
||||
|
||||
<MudForm Style="max-height: 100%"> |
||||
<MudPaper Square="true" Style="display: flex; height: 64px; min-height: 64px; width: 100%; z-index: 100; align-items: center"> |
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Class="ml-6" OnClick="@(_ => SaveChanges())" StartIcon="@Icons.Material.Filled.Save"> |
||||
Save Scripted Schedule |
||||
</MudButton> |
||||
</MudPaper> |
||||
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto"> |
||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8"> |
||||
<MudText Typo="Typo.h5" Class="mb-2">@_channelName - Scripted Schedule</MudText> |
||||
<MudDivider Class="mb-6"/> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Scripted Schedule</MudText> |
||||
</div> |
||||
<MudTextField @bind-Value="@_playout.ScheduleFile" For="@(() => _playout.ScheduleFile)"/> |
||||
</MudStack> |
||||
<MudText Typo="Typo.h5" Class="mt-10 mb-2">Maintenance</MudText> |
||||
<MudDivider Class="mb-6"/> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Playout Items and History</MudText> |
||||
</div> |
||||
<MudButton Disabled="@EntityLocker.IsPlayoutLocked(Id)" Variant="Variant.Filled" Color="Color.Error" OnClick="@(_ => EraseItems(eraseHistory: true))" StartIcon="@Icons.Material.Filled.Delete"> |
||||
Erase Items and History |
||||
</MudButton> |
||||
</MudStack> |
||||
</MudContainer> |
||||
</div> |
||||
</MudForm> |
||||
|
||||
@code { |
||||
private readonly CancellationTokenSource _cts = new(); |
||||
private PlayoutNameViewModel _playout; |
||||
|
||||
[Parameter] |
||||
public int Id { get; set; } |
||||
|
||||
private string _channelName; |
||||
|
||||
public void Dispose() |
||||
{ |
||||
_cts.Cancel(); |
||||
_cts.Dispose(); |
||||
} |
||||
|
||||
protected override async Task OnParametersSetAsync() |
||||
{ |
||||
Option<string> maybeName = await Mediator.Send(new GetChannelNameByPlayoutId(Id), _cts.Token); |
||||
if (maybeName.IsNone) |
||||
{ |
||||
NavigationManager.NavigateTo("playouts"); |
||||
return; |
||||
} |
||||
|
||||
foreach (string name in maybeName) |
||||
{ |
||||
_channelName = name; |
||||
} |
||||
|
||||
Option<PlayoutNameViewModel> maybePlayout = await Mediator.Send(new GetPlayoutById(Id), _cts.Token); |
||||
foreach (PlayoutNameViewModel playout in maybePlayout) |
||||
{ |
||||
_playout = playout; |
||||
} |
||||
} |
||||
|
||||
private async Task EraseItems(bool eraseHistory) |
||||
{ |
||||
IRequest request = eraseHistory ? new ErasePlayoutHistory(Id) : new ErasePlayoutItems(Id); |
||||
await Mediator.Send(request, _cts.Token); |
||||
|
||||
string message = eraseHistory ? "Erased playout items and history" : "Erased playout items"; |
||||
Snackbar.Add(message, Severity.Info); |
||||
} |
||||
|
||||
private async Task SaveChanges() |
||||
{ |
||||
if (_playout is null) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
Either<BaseError, PlayoutNameViewModel> result = |
||||
await Mediator.Send(new UpdateScriptedPlayout(_playout.PlayoutId, _playout.ScheduleFile), _cts.Token); |
||||
|
||||
result.Match( |
||||
_ => { Snackbar.Add($"Saved scripted schedule for playout {_channelName}", Severity.Success); }, |
||||
error => |
||||
{ |
||||
Snackbar.Add($"Unexpected error saving scripted schedule: {error.Value}", Severity.Error); |
||||
Logger.LogError("Unexpected error saving scripted schedule: {Error}", error.Value); |
||||
}); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue