mirror of https://github.com/ErsatzTV/ErsatzTV.git
18 changed files with 229 additions and 83 deletions
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
using ErsatzTV.Core; |
||||
using LanguageExt; |
||||
|
||||
namespace ErsatzTV.Application.HDHR.Commands |
||||
{ |
||||
public record UpdateHDHRTunerCount(int TunerCount) : MediatR.IRequest<Either<BaseError, Unit>>; |
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Interfaces.Repositories; |
||||
using LanguageExt; |
||||
using static LanguageExt.Prelude; |
||||
|
||||
namespace ErsatzTV.Application.HDHR.Commands |
||||
{ |
||||
public class UpdateHDHRTunerCountHandler : MediatR.IRequestHandler<UpdateHDHRTunerCount, Either<BaseError, Unit>> |
||||
{ |
||||
private readonly IConfigElementRepository _configElementRepository; |
||||
|
||||
public UpdateHDHRTunerCountHandler(IConfigElementRepository configElementRepository) => |
||||
_configElementRepository = configElementRepository; |
||||
|
||||
public Task<Either<BaseError, Unit>> Handle( |
||||
UpdateHDHRTunerCount request, |
||||
CancellationToken cancellationToken) => |
||||
Validate(request) |
||||
.MapT(_ => Upsert(ConfigElementKey.HDHRTunerCount, request.TunerCount.ToString())) |
||||
.Bind(v => v.ToEitherAsync()); |
||||
|
||||
private Task<Validation<BaseError, Unit>> Validate(UpdateHDHRTunerCount request) => |
||||
Optional(request.TunerCount) |
||||
.Filter(tc => tc > 0) |
||||
.Map(_ => Unit.Default) |
||||
.ToValidation<BaseError>("Tuner count must be greater than zero") |
||||
.AsTask(); |
||||
|
||||
private Task<Unit> Upsert(ConfigElementKey key, string value) => |
||||
_configElementRepository.Get(key).Match( |
||||
ce => |
||||
{ |
||||
ce.Value = value; |
||||
return _configElementRepository.Update(ce); |
||||
}, |
||||
() => |
||||
{ |
||||
var ce = new ConfigElement { Key = key.Key, Value = value }; |
||||
return _configElementRepository.Add(ce); |
||||
}).ToUnit(); |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
using MediatR; |
||||
|
||||
namespace ErsatzTV.Application.HDHR.Queries |
||||
{ |
||||
public record GetHDHRTunerCount : IRequest<int>; |
||||
} |
||||
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Interfaces.Repositories; |
||||
using LanguageExt; |
||||
using MediatR; |
||||
|
||||
namespace ErsatzTV.Application.HDHR.Queries |
||||
{ |
||||
public class GetHDHRTunerCountHandler : IRequestHandler<GetHDHRTunerCount, int> |
||||
{ |
||||
private readonly IConfigElementRepository _configElementRepository; |
||||
|
||||
public GetHDHRTunerCountHandler(IConfigElementRepository configElementRepository) => |
||||
_configElementRepository = configElementRepository; |
||||
|
||||
public Task<int> Handle(GetHDHRTunerCount request, CancellationToken cancellationToken) => |
||||
_configElementRepository.GetValue<int>(ConfigElementKey.HDHRTunerCount) |
||||
.Map(result => result.IfNone(2)); |
||||
} |
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
@page "/settings" |
||||
@using ErsatzTV.Application.FFmpegProfiles |
||||
@using ErsatzTV.Application.FFmpegProfiles.Commands |
||||
@using ErsatzTV.Application.FFmpegProfiles.Queries |
||||
@using ErsatzTV.Application.HDHR.Commands |
||||
@using ErsatzTV.Application.HDHR.Queries |
||||
@using ErsatzTV.Application.MediaItems.Queries |
||||
@using System.Globalization |
||||
@using Unit = LanguageExt.Unit |
||||
@inject IMediator Mediator |
||||
@inject ISnackbar Snackbar |
||||
@inject ILogger<Settings> Logger |
||||
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8" Style="display: flex; flex-direction: row"> |
||||
<MudCard Class="mr-6" Style="max-width: 400px"> |
||||
<MudCardHeader> |
||||
<CardHeaderContent> |
||||
<MudText Typo="Typo.h6">FFmpeg Settings</MudText> |
||||
</CardHeaderContent> |
||||
</MudCardHeader> |
||||
<MudCardContent> |
||||
<MudForm @bind-IsValid="@_success"> |
||||
<MudTextField T="string" Label="FFmpeg Path" @bind-Value="_ffmpegSettings.FFmpegPath" Validation="@(new Func<string, string>(ValidatePathExists))" Required="true" RequiredError="FFmpeg path is required!"/> |
||||
<MudElement HtmlTag="div" Class="mt-3"> |
||||
<MudTextField T="string" Label="FFprobe Path" @bind-Value="_ffmpegSettings.FFprobePath" Validation="@(new Func<string, string>(ValidatePathExists))" Required="true" RequiredError="FFprobe path is required!"/> |
||||
</MudElement> |
||||
<MudElement HtmlTag="div" Class="mt-3"> |
||||
<MudSelect Label="Default Profile" @bind-Value="_ffmpegSettings.DefaultFFmpegProfileId" For="@(() => _ffmpegSettings.DefaultFFmpegProfileId)"> |
||||
@foreach (FFmpegProfileViewModel profile in _ffmpegProfiles) |
||||
{ |
||||
<MudSelectItem Value="@profile.Id">@profile.Name</MudSelectItem> |
||||
} |
||||
</MudSelect> |
||||
</MudElement> |
||||
<MudSelect Class="mt-3" Label="Preferred Language" @bind-Value="_ffmpegSettings.PreferredLanguageCode" For="@(() => _ffmpegSettings.PreferredLanguageCode)" Required="true" RequiredError="Preferred Language Code is required!"> |
||||
@foreach (CultureInfo culture in _availableCultures) |
||||
{ |
||||
<MudSelectItem Value="@culture.ThreeLetterISOLanguageName">@culture.EnglishName</MudSelectItem> |
||||
} |
||||
</MudSelect> |
||||
<MudElement HtmlTag="div" Class="mt-3"> |
||||
<MudSwitch T="bool" |
||||
Label="Save troubleshooting reports to disk" |
||||
Color="Color.Primary" |
||||
@bind-Checked="@_ffmpegSettings.SaveReports"/> |
||||
</MudElement> |
||||
</MudForm> |
||||
</MudCardContent> |
||||
<MudCardActions> |
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_success)" OnClick="@(_ => SaveFFmpegSettings())">Save Settings</MudButton> |
||||
</MudCardActions> |
||||
</MudCard> |
||||
<MudCard Style="width: 350px"> |
||||
<MudCardHeader> |
||||
<CardHeaderContent> |
||||
<MudText Typo="Typo.h6">HDHomeRun Settings</MudText> |
||||
</CardHeaderContent> |
||||
</MudCardHeader> |
||||
<MudCardContent> |
||||
<MudForm @bind-IsValid="@_hdhrSuccess"> |
||||
<MudTextField T="int" Label="Tuner Count" @bind-Value="_tunerCount" Validation="@(new Func<int, string>(ValidateTunerCount))" Required="true" RequiredError="Tuner count is required!"/> |
||||
</MudForm> |
||||
</MudCardContent> |
||||
<MudCardActions> |
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_hdhrSuccess)" OnClick="@(_ => SaveHDHRSettings())">Save Settings</MudButton> |
||||
</MudCardActions> |
||||
</MudCard> |
||||
</MudContainer> |
||||
|
||||
@code { |
||||
private bool _success; |
||||
private bool _hdhrSuccess; |
||||
private List<FFmpegProfileViewModel> _ffmpegProfiles; |
||||
private FFmpegSettingsViewModel _ffmpegSettings; |
||||
private List<CultureInfo> _availableCultures; |
||||
private int _tunerCount; |
||||
|
||||
protected override async Task OnParametersSetAsync() |
||||
{ |
||||
await LoadFFmpegProfilesAsync(); |
||||
|
||||
_ffmpegSettings = await Mediator.Send(new GetFFmpegSettings()); |
||||
_success = File.Exists(_ffmpegSettings.FFmpegPath) && File.Exists(_ffmpegSettings.FFprobePath); |
||||
_availableCultures = await Mediator.Send(new GetAllLanguageCodes()); |
||||
_tunerCount = await Mediator.Send(new GetHDHRTunerCount()); |
||||
_hdhrSuccess = string.IsNullOrWhiteSpace(ValidateTunerCount(_tunerCount)); |
||||
} |
||||
|
||||
private static string ValidatePathExists(string path) => !File.Exists(path) ? "Path does not exist" : null; |
||||
|
||||
private static string ValidateTunerCount(int tunerCount) => tunerCount <= 0 ? "Tuner count must be greater than zero" : null; |
||||
|
||||
private async Task LoadFFmpegProfilesAsync() => |
||||
_ffmpegProfiles = await Mediator.Send(new GetAllFFmpegProfiles()); |
||||
|
||||
private async Task SaveFFmpegSettings() |
||||
{ |
||||
Either<BaseError, Unit> result = await Mediator.Send(new UpdateFFmpegSettings(_ffmpegSettings)); |
||||
result.Match( |
||||
Left: error => |
||||
{ |
||||
Snackbar.Add(error.Value, Severity.Error); |
||||
Logger.LogError("Unexpected error saving FFmpeg settings: {Error}", error.Value); |
||||
}, |
||||
Right: _ => Snackbar.Add("Successfully saved FFmpeg settings", Severity.Success)); |
||||
} |
||||
|
||||
private async Task SaveHDHRSettings() |
||||
{ |
||||
Either<BaseError, Unit> result = await Mediator.Send(new UpdateHDHRTunerCount(_tunerCount)); |
||||
result.Match( |
||||
Left: error => |
||||
{ |
||||
Snackbar.Add(error.Value, Severity.Error); |
||||
Logger.LogError("Unexpected error saving HDHomeRun settings: {Error}", error.Value); |
||||
}, |
||||
Right: _ => Snackbar.Add("Successfully saved HDHomeRun settings", Severity.Success)); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue