@@ -1,44 +1,106 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using ErsatzTV.Application.FFmpegProfiles;
|
||||
using ErsatzTV.Controllers.Api.Requests;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Api.FFmpegProfiles;
|
||||
using ErsatzTV.Extensions;
|
||||
using ErsatzTV.Filters;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ErsatzTV.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
[EndpointGroupName("general")]
|
||||
[ServiceFilter(typeof(ApiKeyAuthorizationFilter))]
|
||||
public class FFmpegProfileController(IMediator mediator) : ControllerBase
|
||||
{
|
||||
[HttpGet("/api/ffmpeg/profiles", Name="GetFFmpegProfiles")]
|
||||
[HttpGet("/api/ffmpeg/profiles", Name = "GetFFmpegProfiles")]
|
||||
[Tags("FFmpeg Profiles")]
|
||||
[EndpointSummary("Get all FFmpeg profiles")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(List<FFmpegFullProfileResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<List<FFmpegFullProfileResponseModel>> GetAll(CancellationToken cancellationToken) =>
|
||||
await mediator.Send(new GetAllFFmpegProfilesForApi(), cancellationToken);
|
||||
|
||||
[HttpPost("/api/ffmpeg/profiles/new", Name="CreateFFmpegProfile")]
|
||||
[HttpGet("/api/ffmpeg/profiles/{id:int}", Name = "GetFFmpegProfileById")]
|
||||
[Tags("FFmpeg Profiles")]
|
||||
[EndpointSummary("Get an FFmpeg profile by id")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(FFmpegFullProfileResponseModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
Option<FFmpegFullProfileResponseModel> result =
|
||||
await mediator.Send(new GetFFmpegFullProfileByIdForApi(id), cancellationToken);
|
||||
return result.ToGetResult();
|
||||
}
|
||||
|
||||
[HttpPost("/api/ffmpeg/profiles", Name = "CreateFFmpegProfile")]
|
||||
[Tags("FFmpeg Profiles")]
|
||||
[EndpointSummary("Create an FFmpeg profile")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(FFmpegFullProfileResponseModel), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
|
||||
public async Task<IActionResult> AddOne(
|
||||
[Required] [FromBody]
|
||||
CreateFFmpegProfile request,
|
||||
CreateFFmpegProfileRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Either<BaseError, CreateFFmpegProfileResult> result = await mediator.Send(request, cancellationToken);
|
||||
return result.Match<IActionResult>(Ok, error => Problem(error.ToString()));
|
||||
Either<BaseError, CreateFFmpegProfileResult> result = await mediator.Send(request.ToCommand(), cancellationToken);
|
||||
return await result.Match(
|
||||
Left: error => Task.FromResult(error.ToErrorResult()),
|
||||
Right: async created =>
|
||||
{
|
||||
Option<FFmpegFullProfileResponseModel> profile =
|
||||
await mediator.Send(new GetFFmpegFullProfileByIdForApi(created.FFmpegProfileId), cancellationToken);
|
||||
return profile.Match(
|
||||
Some: vm => (IActionResult)new CreatedResult($"/api/ffmpeg/profiles/{vm.Id}", vm),
|
||||
None: () => ApiResults.NotFoundProblem());
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPut("/api/ffmpeg/profiles/update", Name="UpdateFFmpegProfile")]
|
||||
[HttpPut("/api/ffmpeg/profiles/{id:int}", Name = "UpdateFFmpegProfile")]
|
||||
[Tags("FFmpeg Profiles")]
|
||||
[EndpointSummary("Update an FFmpeg profile")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(FFmpegFullProfileResponseModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
|
||||
public async Task<IActionResult> UpdateOne(
|
||||
int id,
|
||||
[Required] [FromBody]
|
||||
UpdateFFmpegProfile request,
|
||||
UpdateFFmpegProfileRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Either<BaseError, UpdateFFmpegProfileResult> result = await mediator.Send(request, cancellationToken);
|
||||
return result.Match<IActionResult>(Ok, error => Problem(error.ToString()));
|
||||
Either<BaseError, UpdateFFmpegProfileResult> result =
|
||||
await mediator.Send(request.ToCommand(id), cancellationToken);
|
||||
return await result.Match(
|
||||
Left: error => Task.FromResult(error.ToErrorResult()),
|
||||
Right: async updated =>
|
||||
{
|
||||
Option<FFmpegFullProfileResponseModel> profile =
|
||||
await mediator.Send(new GetFFmpegFullProfileByIdForApi(updated.FFmpegProfileId), cancellationToken);
|
||||
return profile.Match(
|
||||
Some: vm => (IActionResult)new OkObjectResult(vm),
|
||||
None: () => ApiResults.NotFoundProblem());
|
||||
});
|
||||
}
|
||||
|
||||
[HttpDelete("/api/ffmpeg/delete/{id:int}", Name="DeleteFFmpegProfile")]
|
||||
[HttpDelete("/api/ffmpeg/profiles/{id:int}", Name = "DeleteFFmpegProfile")]
|
||||
[Tags("FFmpeg Profiles")]
|
||||
[EndpointSummary("Delete an FFmpeg profile")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
|
||||
public async Task<IActionResult> DeleteProfileAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
Either<BaseError, Unit> result = await mediator.Send(new DeleteFFmpegProfile(id), cancellationToken);
|
||||
return result.Match<IActionResult>(_ => Ok(), error => Conflict(error.ToString()));
|
||||
return result.ToDeletedResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ErsatzTV.Application.Libraries;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Filters;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -7,6 +8,7 @@ namespace ErsatzTV.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
[EndpointGroupName("general")]
|
||||
[ServiceFilter(typeof(ApiKeyAuthorizationFilter))]
|
||||
public class LibrariesController(ITelevisionRepository televisionRepository, IMediator mediator)
|
||||
{
|
||||
[HttpPost("/api/libraries/{id:int}/scan")]
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Threading.Channels;
|
||||
using ErsatzTV.Application;
|
||||
using ErsatzTV.Application.Maintenance;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Filters;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -9,6 +10,7 @@ namespace ErsatzTV.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
[EndpointGroupName("general")]
|
||||
[ServiceFilter(typeof(ApiKeyAuthorizationFilter))]
|
||||
public class MaintenanceController(IMediator mediator, ChannelWriter<IBackgroundServiceRequest> workerChannel)
|
||||
{
|
||||
[HttpGet("/api/maintenance/gc")]
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using ErsatzTV.Application.FFmpegProfiles;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.FFmpeg;
|
||||
|
||||
namespace ErsatzTV.Controllers.Api.Requests;
|
||||
|
||||
public record CreateFFmpegProfileRequest(
|
||||
string Name,
|
||||
int ThreadCount,
|
||||
bool NormalizeAudio,
|
||||
bool NormalizeVideo,
|
||||
HardwareAccelerationKind HardwareAcceleration,
|
||||
string VaapiDisplay,
|
||||
VaapiDriver VaapiDriver,
|
||||
string VaapiDevice,
|
||||
int? QsvExtraHardwareFrames,
|
||||
int ResolutionId,
|
||||
ScalingBehavior ScalingBehavior,
|
||||
FilterMode PadMode,
|
||||
FFmpegProfileVideoFormat VideoFormat,
|
||||
string VideoProfile,
|
||||
string VideoPreset,
|
||||
bool AllowBFrames,
|
||||
FFmpegProfileBitDepth BitDepth,
|
||||
int VideoBitrate,
|
||||
int VideoBufferSize,
|
||||
FFmpegProfileTonemapAlgorithm TonemapAlgorithm,
|
||||
FFmpegProfileAudioFormat AudioFormat,
|
||||
int AudioBitrate,
|
||||
int AudioBufferSize,
|
||||
NormalizeLoudnessMode NormalizeLoudnessMode,
|
||||
double? TargetLoudness,
|
||||
int AudioChannels,
|
||||
int AudioSampleRate,
|
||||
bool NormalizeFramerate,
|
||||
bool NormalizeColors,
|
||||
bool DeinterlaceVideo)
|
||||
{
|
||||
public CreateFFmpegProfile ToCommand() =>
|
||||
new(
|
||||
Name,
|
||||
ThreadCount,
|
||||
NormalizeAudio,
|
||||
NormalizeVideo,
|
||||
HardwareAcceleration,
|
||||
VaapiDisplay,
|
||||
VaapiDriver,
|
||||
VaapiDevice,
|
||||
QsvExtraHardwareFrames,
|
||||
ResolutionId,
|
||||
ScalingBehavior,
|
||||
PadMode,
|
||||
VideoFormat,
|
||||
VideoProfile,
|
||||
VideoPreset,
|
||||
AllowBFrames,
|
||||
BitDepth,
|
||||
VideoBitrate,
|
||||
VideoBufferSize,
|
||||
TonemapAlgorithm,
|
||||
AudioFormat,
|
||||
AudioBitrate,
|
||||
AudioBufferSize,
|
||||
NormalizeLoudnessMode,
|
||||
TargetLoudness,
|
||||
AudioChannels,
|
||||
AudioSampleRate,
|
||||
NormalizeFramerate,
|
||||
NormalizeColors,
|
||||
DeinterlaceVideo);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using ErsatzTV.Application.FFmpegProfiles;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.FFmpeg;
|
||||
|
||||
namespace ErsatzTV.Controllers.Api.Requests;
|
||||
|
||||
public record UpdateFFmpegProfileRequest(
|
||||
string Name,
|
||||
int ThreadCount,
|
||||
bool NormalizeAudio,
|
||||
bool NormalizeVideo,
|
||||
HardwareAccelerationKind HardwareAcceleration,
|
||||
string VaapiDisplay,
|
||||
VaapiDriver VaapiDriver,
|
||||
string VaapiDevice,
|
||||
int? QsvExtraHardwareFrames,
|
||||
int ResolutionId,
|
||||
ScalingBehavior ScalingBehavior,
|
||||
FilterMode PadMode,
|
||||
FFmpegProfileVideoFormat VideoFormat,
|
||||
string VideoProfile,
|
||||
string VideoPreset,
|
||||
bool AllowBFrames,
|
||||
FFmpegProfileBitDepth BitDepth,
|
||||
int VideoBitrate,
|
||||
int VideoBufferSize,
|
||||
FFmpegProfileTonemapAlgorithm TonemapAlgorithm,
|
||||
FFmpegProfileAudioFormat AudioFormat,
|
||||
int AudioBitrate,
|
||||
int AudioBufferSize,
|
||||
NormalizeLoudnessMode NormalizeLoudnessMode,
|
||||
double? TargetLoudness,
|
||||
int AudioChannels,
|
||||
int AudioSampleRate,
|
||||
bool NormalizeFramerate,
|
||||
bool NormalizeColors,
|
||||
bool DeinterlaceVideo)
|
||||
{
|
||||
public UpdateFFmpegProfile ToCommand(int id) =>
|
||||
new(
|
||||
id,
|
||||
Name,
|
||||
ThreadCount,
|
||||
NormalizeAudio,
|
||||
NormalizeVideo,
|
||||
HardwareAcceleration,
|
||||
VaapiDisplay,
|
||||
VaapiDriver,
|
||||
VaapiDevice,
|
||||
QsvExtraHardwareFrames,
|
||||
ResolutionId,
|
||||
ScalingBehavior,
|
||||
PadMode,
|
||||
VideoFormat,
|
||||
VideoProfile,
|
||||
VideoPreset,
|
||||
AllowBFrames,
|
||||
BitDepth,
|
||||
VideoBitrate,
|
||||
VideoBufferSize,
|
||||
TonemapAlgorithm,
|
||||
AudioFormat,
|
||||
AudioBitrate,
|
||||
AudioBufferSize,
|
||||
NormalizeLoudnessMode,
|
||||
TargetLoudness,
|
||||
AudioChannels,
|
||||
AudioSampleRate,
|
||||
NormalizeFramerate,
|
||||
NormalizeColors,
|
||||
DeinterlaceVideo);
|
||||
}
|
||||
Reference in New Issue
Block a user