From 198e595bc6b406f98bdd07c3933692a7810adc27 Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sat, 1 May 2021 07:45:33 -0500 Subject: [PATCH] add button to copy/clone ffmpeg profile (#183) --- .../Commands/CopyFFmpegProfile.cs | 9 +++ .../Commands/CopyFFmpegProfileHandler.cs | 37 ++++++++++ .../Repositories/IFFmpegProfileRepository.cs | 1 + .../Repositories/FFmpegProfileRepository.cs | 27 ++++++- ErsatzTV/Pages/FFmpeg.razor | 39 ++++++++--- ErsatzTV/Shared/CopyFFmpegProfileDialog.razor | 70 +++++++++++++++++++ 6 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfile.cs create mode 100644 ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfileHandler.cs create mode 100644 ErsatzTV/Shared/CopyFFmpegProfileDialog.razor diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfile.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfile.cs new file mode 100644 index 000000000..c22736727 --- /dev/null +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfile.cs @@ -0,0 +1,9 @@ +using ErsatzTV.Core; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.FFmpegProfiles.Commands +{ + public record CopyFFmpegProfile + (int FFmpegProfileId, string Name) : IRequest>; +} diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfileHandler.cs new file mode 100644 index 000000000..9f702d652 --- /dev/null +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfileHandler.cs @@ -0,0 +1,37 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.FFmpegProfiles.Mapper; + +namespace ErsatzTV.Application.FFmpegProfiles.Commands +{ + public class + CopyFFmpegProfileHandler : IRequestHandler> + { + private readonly IFFmpegProfileRepository _ffmpegProfileRepository; + + public CopyFFmpegProfileHandler(IFFmpegProfileRepository ffmpegProfileRepository) => + _ffmpegProfileRepository = ffmpegProfileRepository; + + public Task> Handle( + CopyFFmpegProfile request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(PerformCopy) + .Bind(v => v.ToEitherAsync()); + + private Task PerformCopy(CopyFFmpegProfile request) => + _ffmpegProfileRepository.Copy(request.FFmpegProfileId, request.Name) + .Map(ProjectToViewModel); + + private Task> Validate(CopyFFmpegProfile request) => + ValidateName(request).AsTask().MapT(_ => request); + + private Validation ValidateName(CopyFFmpegProfile request) => + request.NotEmpty(x => x.Name) + .Bind(_ => request.NotLongerThan(50)(x => x.Name)); + } +} diff --git a/ErsatzTV.Core/Interfaces/Repositories/IFFmpegProfileRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IFFmpegProfileRepository.cs index c08ef8469..b78cfe708 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IFFmpegProfileRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IFFmpegProfileRepository.cs @@ -12,5 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task> GetAll(); Task Update(FFmpegProfile ffmpegProfile); Task Delete(int ffmpegProfileId); + Task Copy(int ffmpegProfileId, string name); } } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs index e71c7c684..9b04cc04d 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs @@ -5,14 +5,20 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; namespace ErsatzTV.Infrastructure.Data.Repositories { public class FFmpegProfileRepository : IFFmpegProfileRepository { private readonly TvContext _dbContext; + private readonly IDbContextFactory _dbContextFactory; - public FFmpegProfileRepository(TvContext dbContext) => _dbContext = dbContext; + public FFmpegProfileRepository(IDbContextFactory dbContextFactory, TvContext dbContext) + { + _dbContextFactory = dbContextFactory; + _dbContext = dbContext; + } public async Task Add(FFmpegProfile ffmpegProfile) { @@ -44,5 +50,24 @@ namespace ErsatzTV.Infrastructure.Data.Repositories _dbContext.FFmpegProfiles.Remove(ffmpegProfile); await _dbContext.SaveChangesAsync(); } + + public async Task Copy(int ffmpegProfileId, string name) + { + await using TvContext dbContext = _dbContextFactory.CreateDbContext(); + FFmpegProfile ffmpegProfile = await dbContext.FFmpegProfiles.FindAsync(ffmpegProfileId); + + PropertyValues values = dbContext.Entry(ffmpegProfile).CurrentValues.Clone(); + values["Id"] = 0; + + var clone = new FFmpegProfile(); + await dbContext.AddAsync(clone); + dbContext.Entry(clone).CurrentValues.SetValues(values); + clone.Name = name; + + await dbContext.SaveChangesAsync(); + await dbContext.Entry(clone).Reference(f => f.Resolution).LoadAsync(); + + return clone; + } } } diff --git a/ErsatzTV/Pages/FFmpeg.razor b/ErsatzTV/Pages/FFmpeg.razor index e0fdf9a13..80bda071e 100644 --- a/ErsatzTV/Pages/FFmpeg.razor +++ b/ErsatzTV/Pages/FFmpeg.razor @@ -2,10 +2,11 @@ @using ErsatzTV.Application.FFmpegProfiles @using ErsatzTV.Application.FFmpegProfiles.Commands @using ErsatzTV.Application.FFmpegProfiles.Queries -@inject IDialogService Dialog -@inject IMediator Mediator -@inject ILogger Logger -@inject ISnackbar Snackbar +@inject IDialogService _dialog +@inject IMediator _mediator +@inject ILogger _logger +@inject ISnackbar _snackbar +@inject NavigationManager _navigationManager @@ -20,7 +21,7 @@ - + Name @@ -52,12 +53,17 @@
- + - + + + + + @@ -77,20 +83,33 @@ protected override async Task OnParametersSetAsync() => await LoadFFmpegProfilesAsync(); private async Task LoadFFmpegProfilesAsync() => - _ffmpegProfiles = await Mediator.Send(new GetAllFFmpegProfiles()); + _ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles()); private async Task DeleteProfileAsync(FFmpegProfileViewModel ffmpegProfile) { var parameters = new DialogParameters { { "EntityType", "ffmpeg profile" }, { "EntityName", ffmpegProfile.Name } }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; - IDialogReference dialog = Dialog.Show("Delete FFmpeg Profile", parameters, options); + IDialogReference dialog = _dialog.Show("Delete FFmpeg Profile", parameters, options); DialogResult result = await dialog.Result; if (!result.Cancelled) { - await Mediator.Send(new DeleteFFmpegProfile(ffmpegProfile.Id)); + await _mediator.Send(new DeleteFFmpegProfile(ffmpegProfile.Id)); await LoadFFmpegProfilesAsync(); } } + private async Task CopyProfileAsync(FFmpegProfileViewModel ffmpegProfile) + { + var parameters = new DialogParameters { { "FFmpegProfileId", ffmpegProfile.Id } }; + var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; + + IDialogReference dialog = _dialog.Show("Copy FFmpeg Profile", parameters, options); + DialogResult dialogResult = await dialog.Result; + if (!dialogResult.Cancelled && dialogResult.Data is FFmpegProfileViewModel data) + { + _navigationManager.NavigateTo($"/ffmpeg/{data.Id}"); + } + } + } \ No newline at end of file diff --git a/ErsatzTV/Shared/CopyFFmpegProfileDialog.razor b/ErsatzTV/Shared/CopyFFmpegProfileDialog.razor new file mode 100644 index 000000000..82894a18f --- /dev/null +++ b/ErsatzTV/Shared/CopyFFmpegProfileDialog.razor @@ -0,0 +1,70 @@ +@using ErsatzTV.Application.FFmpegProfiles +@using ErsatzTV.Application.FFmpegProfiles.Commands +@inject IMediator _mediator +@inject ISnackbar _snackbar +@inject ILogger _logger + + + + + + + Enter a name for the new FFmpeg Profile + + + + + + + + Cancel + + Copy Profile + + + + +@code { + [CascadingParameter] + MudDialogInstance MudDialog { get; set; } + + [Parameter] + public int FFmpegProfileId { get; set; } + + private record DummyModel; + private readonly DummyModel _dummyModel = new(); + + private string _newFFmpegProfileName; + + private bool CanSubmit() => !string.IsNullOrWhiteSpace(_newFFmpegProfileName); + + private async Task Submit() + { + if (!CanSubmit()) + { + return; + } + + Either maybeResult = + await _mediator.Send(new CopyFFmpegProfile(FFmpegProfileId, _newFFmpegProfileName)); + + maybeResult.Match( + ffmpegProfile => + { + MudDialog.Close(DialogResult.Ok(ffmpegProfile)); + }, + error => + { + _snackbar.Add(error.Value, Severity.Error); + _logger.LogError("Error copying FFmpeg Profile: {Error}", error.Value); + MudDialog.Close(DialogResult.Cancel()); + }); + } + + private void Cancel(MouseEventArgs e) + { + MudDialog.Cancel(); + } +} \ No newline at end of file