Browse Source

api changes for ffmpeg profiles (#2520)

pull/2521/head
Jason Dove 10 months ago committed by GitHub
parent
commit
b851a7daba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 20
      ErsatzTV.Application/FFmpegProfiles/Mapper.cs
  2. 2
      ErsatzTV.Application/FFmpegProfiles/Queries/GetAllFFmpegProfilesForApi.cs
  3. 15
      ErsatzTV.Application/FFmpegProfiles/Queries/GetAllFFmpegProfilesForApiHandler.cs
  4. 5
      ErsatzTV.Application/FFmpegProfiles/Queries/GetResolutionByName.cs
  5. 18
      ErsatzTV.Application/FFmpegProfiles/Queries/GetResolutionByNameHandler.cs
  6. 25
      ErsatzTV.Core/Api/FFmpegProfiles/FFmpegFullProfileResponseModel.cs
  7. 5
      ErsatzTV.Core/Domain/FFmpegProfileAudioFormat.cs
  8. 3
      ErsatzTV.Core/Domain/FFmpegProfileBitDepth.cs
  9. 3
      ErsatzTV.Core/Domain/FFmpegProfileTonemapAlgorithm.cs
  10. 5
      ErsatzTV.Core/Domain/FFmpegProfileVideoFormat.cs
  11. 5
      ErsatzTV.Core/Domain/HardwareAccelerationKind.cs
  12. 3
      ErsatzTV.Core/Domain/NormalizeLoudnessMode.cs
  13. 3
      ErsatzTV.Core/Domain/ScalingBehavior.cs
  14. 2
      ErsatzTV.Core/FFmpeg/VaapiDriver.cs
  15. 45
      ErsatzTV/Controllers/Api/FFmpegProfileController.cs
  16. 18
      ErsatzTV/Controllers/Api/ResolutionController.cs
  17. 2
      ErsatzTV/Controllers/Api/VersionController.cs
  18. 633
      ErsatzTV/wwwroot/openapi/v1.json

20
ErsatzTV.Application/FFmpegProfiles/Mapper.cs

@ -47,19 +47,25 @@ internal static class Mapper
ffmpegProfile.Id, ffmpegProfile.Id,
ffmpegProfile.Name, ffmpegProfile.Name,
ffmpegProfile.ThreadCount, ffmpegProfile.ThreadCount,
(int)ffmpegProfile.HardwareAcceleration, ffmpegProfile.HardwareAcceleration,
ffmpegProfile.VaapiDisplay, ffmpegProfile.VaapiDisplay,
(int)ffmpegProfile.VaapiDriver, ffmpegProfile.VaapiDriver,
ffmpegProfile.VaapiDevice, ffmpegProfile.VaapiDevice,
ffmpegProfile.ResolutionId, ffmpegProfile.QsvExtraHardwareFrames,
(int)ffmpegProfile.VideoFormat, ffmpegProfile.Resolution.Name,
ffmpegProfile.ScalingBehavior,
ffmpegProfile.VideoFormat,
ffmpegProfile.VideoProfile,
ffmpegProfile.VideoPreset,
ffmpegProfile.AllowBFrames,
ffmpegProfile.BitDepth,
ffmpegProfile.VideoBitrate, ffmpegProfile.VideoBitrate,
ffmpegProfile.VideoBufferSize, ffmpegProfile.VideoBufferSize,
(int)ffmpegProfile.TonemapAlgorithm, ffmpegProfile.TonemapAlgorithm,
(int)ffmpegProfile.AudioFormat, ffmpegProfile.AudioFormat,
ffmpegProfile.AudioBitrate, ffmpegProfile.AudioBitrate,
ffmpegProfile.AudioBufferSize, ffmpegProfile.AudioBufferSize,
(int)ffmpegProfile.NormalizeLoudnessMode, ffmpegProfile.NormalizeLoudnessMode,
ffmpegProfile.AudioChannels, ffmpegProfile.AudioChannels,
ffmpegProfile.AudioSampleRate, ffmpegProfile.AudioSampleRate,
ffmpegProfile.NormalizeFramerate, ffmpegProfile.NormalizeFramerate,

2
ErsatzTV.Application/FFmpegProfiles/Queries/GetAllFFmpegProfilesForApi.cs

@ -2,4 +2,4 @@
namespace ErsatzTV.Application.FFmpegProfiles; namespace ErsatzTV.Application.FFmpegProfiles;
public record GetAllFFmpegProfilesForApi : IRequest<List<FFmpegProfileResponseModel>>; public record GetAllFFmpegProfilesForApi : IRequest<List<FFmpegFullProfileResponseModel>>;

15
ErsatzTV.Application/FFmpegProfiles/Queries/GetAllFFmpegProfilesForApiHandler.cs

@ -6,23 +6,18 @@ using static ErsatzTV.Application.FFmpegProfiles.Mapper;
namespace ErsatzTV.Application.FFmpegProfiles; namespace ErsatzTV.Application.FFmpegProfiles;
public class public class GetAllFFmpegProfilesForApiHandler(IDbContextFactory<TvContext> dbContextFactory)
GetAllFFmpegProfilesForApiHandler : IRequestHandler<GetAllFFmpegProfilesForApi, List<FFmpegProfileResponseModel>> : IRequestHandler<GetAllFFmpegProfilesForApi, List<FFmpegFullProfileResponseModel>>
{ {
private readonly IDbContextFactory<TvContext> _dbContextFactory; public async Task<List<FFmpegFullProfileResponseModel>> Handle(
public GetAllFFmpegProfilesForApiHandler(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
public async Task<List<FFmpegProfileResponseModel>> Handle(
GetAllFFmpegProfilesForApi request, GetAllFFmpegProfilesForApi request,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
List<FFmpegProfile> ffmpegProfiles = await dbContext.FFmpegProfiles List<FFmpegProfile> ffmpegProfiles = await dbContext.FFmpegProfiles
.AsNoTracking() .AsNoTracking()
.Include(p => p.Resolution) .Include(p => p.Resolution)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
return ffmpegProfiles.Map(ProjectToResponseModel).ToList(); return ffmpegProfiles.Map(ProjectToFullResponseModel).ToList();
} }
} }

5
ErsatzTV.Application/FFmpegProfiles/Queries/GetResolutionByName.cs

@ -0,0 +1,5 @@
using ErsatzTV.Core;
namespace ErsatzTV.Application.FFmpegProfiles;
public record GetResolutionByName(string Name) : IRequest<Option<int>>;

18
ErsatzTV.Application/FFmpegProfiles/Queries/GetResolutionByNameHandler.cs

@ -0,0 +1,18 @@
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.FFmpegProfiles;
public class GetResolutionByNameHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetResolutionByName, Option<int>>
{
public async Task<Option<int>> Handle(GetResolutionByName request, CancellationToken cancellationToken)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
return await dbContext.Resolutions
.AsNoTracking()
.SelectOneAsync(r => r.Name, r => r.Name == request.Name, cancellationToken)
.MapT(r => r.Id);
}
}

25
ErsatzTV.Core/Api/FFmpegProfiles/FFmpegFullProfileResponseModel.cs

@ -1,22 +1,31 @@
namespace ErsatzTV.Core.Api.FFmpegProfiles; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
namespace ErsatzTV.Core.Api.FFmpegProfiles;
public record FFmpegFullProfileResponseModel( public record FFmpegFullProfileResponseModel(
int Id, int Id,
string Name, string Name,
int ThreadCount, int ThreadCount,
int HardwareAcceleration, HardwareAccelerationKind HardwareAcceleration,
string VaapiDisplay, string VaapiDisplay,
int VaapiDriver, VaapiDriver VaapiDriver,
string VaapiDevice, string VaapiDevice,
int ResolutionId, int? QsvExtraHardwareFrames,
int VideoFormat, string Resolution,
ScalingBehavior ScalingBehavior,
FFmpegProfileVideoFormat VideoFormat,
string VideoProfile,
string VideoPreset,
bool AllowBFrames,
FFmpegProfileBitDepth BitDepth,
int VideoBitrate, int VideoBitrate,
int VideoBufferSize, int VideoBufferSize,
int TonemapAlgorithm, FFmpegProfileTonemapAlgorithm TonemapAlgorithm,
int AudioFormat, FFmpegProfileAudioFormat AudioFormat,
int AudioBitrate, int AudioBitrate,
int AudioBufferSize, int AudioBufferSize,
int NormalizeLoudnessMode, NormalizeLoudnessMode NormalizeLoudnessMode,
int AudioChannels, int AudioChannels,
int AudioSampleRate, int AudioSampleRate,
bool NormalizeFramerate, bool NormalizeFramerate,

5
ErsatzTV.Core/Domain/FFmpegProfileAudioFormat.cs

@ -1,5 +1,8 @@
namespace ErsatzTV.Core.Domain; using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileAudioFormat>))]
public enum FFmpegProfileAudioFormat public enum FFmpegProfileAudioFormat
{ {
None = 0, None = 0,

3
ErsatzTV.Core/Domain/FFmpegProfileBitDepth.cs

@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain; namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileBitDepth>))]
public enum FFmpegProfileBitDepth public enum FFmpegProfileBitDepth
{ {
EightBit = 0, EightBit = 0,

3
ErsatzTV.Core/Domain/FFmpegProfileTonemapAlgorithm.cs

@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain; namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileTonemapAlgorithm>))]
public enum FFmpegProfileTonemapAlgorithm public enum FFmpegProfileTonemapAlgorithm
{ {
Linear = 0, Linear = 0,

5
ErsatzTV.Core/Domain/FFmpegProfileVideoFormat.cs

@ -1,5 +1,8 @@
namespace ErsatzTV.Core.Domain; using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileVideoFormat>))]
public enum FFmpegProfileVideoFormat public enum FFmpegProfileVideoFormat
{ {
None = 0, None = 0,

5
ErsatzTV.Core/Domain/HardwareAccelerationKind.cs

@ -1,5 +1,8 @@
namespace ErsatzTV.Core.Domain; using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<HardwareAccelerationKind>))]
public enum HardwareAccelerationKind public enum HardwareAccelerationKind
{ {
None = 0, None = 0,

3
ErsatzTV.Core/Domain/NormalizeLoudnessMode.cs

@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain; namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<NormalizeLoudnessMode>))]
public enum NormalizeLoudnessMode public enum NormalizeLoudnessMode
{ {
Off = 0, Off = 0,

3
ErsatzTV.Core/Domain/ScalingBehavior.cs

@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain; namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<ScalingBehavior>))]
public enum ScalingBehavior public enum ScalingBehavior
{ {
ScaleAndPad = 0, ScaleAndPad = 0,

2
ErsatzTV.Core/FFmpeg/VaapiDriver.cs

@ -1,8 +1,10 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.FFmpeg; namespace ErsatzTV.Core.FFmpeg;
[SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "InconsistentNaming")]
[JsonConverter(typeof(JsonStringEnumConverter<VaapiDriver>))]
public enum VaapiDriver public enum VaapiDriver
{ {
Default = 0, Default = 0,

45
ErsatzTV/Controllers/Api/FFmpegProfileController.cs

@ -2,34 +2,43 @@
using ErsatzTV.Application.FFmpegProfiles; using ErsatzTV.Application.FFmpegProfiles;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Api.FFmpegProfiles; using ErsatzTV.Core.Api.FFmpegProfiles;
using ErsatzTV.Filters;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace ErsatzTV.Controllers.Api; namespace ErsatzTV.Controllers.Api;
[ApiController] [ApiController]
[V2ApiActionFilter] [EndpointGroupName("general")]
public class FFmpegProfileController(IMediator mediator) public class FFmpegProfileController(IMediator mediator) : ControllerBase
{ {
[HttpGet("/api/ffmpeg/profiles")] [HttpGet("/api/ffmpeg/profiles", Name="GetFFmpegProfiles")]
public async Task<List<FFmpegProfileResponseModel>> GetAll() => public async Task<List<FFmpegFullProfileResponseModel>> GetAll(CancellationToken cancellationToken) =>
await mediator.Send(new GetAllFFmpegProfilesForApi()); await mediator.Send(new GetAllFFmpegProfilesForApi(), cancellationToken);
[HttpPost("/api/ffmpeg/profiles/new")] [HttpPost("/api/ffmpeg/profiles/new", Name="CreateFFmpegProfile")]
public async Task<Either<BaseError, CreateFFmpegProfileResult>> AddOne( public async Task<IActionResult> AddOne(
[Required] [FromBody] [Required] [FromBody]
CreateFFmpegProfile request) => await mediator.Send(request); CreateFFmpegProfile request,
CancellationToken cancellationToken)
{
Either<BaseError, CreateFFmpegProfileResult> result = await mediator.Send(request, cancellationToken);
return result.Match<IActionResult>(Ok, error => Problem(error.ToString()));
}
[HttpPut("/api/ffmpeg/profiles/update")] [HttpPut("/api/ffmpeg/profiles/update", Name="UpdateFFmpegProfile")]
public async Task<Either<BaseError, UpdateFFmpegProfileResult>> UpdateOne( public async Task<IActionResult> UpdateOne(
[Required] [FromBody] [Required] [FromBody]
UpdateFFmpegProfile request) => await mediator.Send(request); UpdateFFmpegProfile request,
CancellationToken cancellationToken)
{
Either<BaseError, UpdateFFmpegProfileResult> result = await mediator.Send(request, cancellationToken);
return result.Match<IActionResult>(Ok, error => Problem(error.ToString()));
}
[HttpGet("/api/ffmpeg/profiles/{id:int}")] [HttpDelete("/api/ffmpeg/delete/{id:int}", Name="DeleteFFmpegProfile")]
public async Task<Option<FFmpegFullProfileResponseModel>> GetOne(int id) => public async Task<IActionResult> DeleteProfileAsync(int id, CancellationToken cancellationToken)
await mediator.Send(new GetFFmpegFullProfileByIdForApi(id)); {
Either<BaseError, Unit> result = await mediator.Send(new DeleteFFmpegProfile(id), cancellationToken);
[HttpDelete("/api/ffmpeg/delete/{id:int}")] return result.Match<IActionResult>(_ => Ok(), error => Problem(error.ToString()));
public async Task DeleteProfileAsync(int id) => await mediator.Send(new DeleteFFmpegProfile(id)); }
} }

18
ErsatzTV/Controllers/Api/ResolutionController.cs

@ -0,0 +1,18 @@
using ErsatzTV.Application.FFmpegProfiles;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace ErsatzTV.Controllers.Api;
[ApiController]
[EndpointGroupName("general")]
public class ResolutionController(IMediator mediator) : ControllerBase
{
[HttpGet("/api/ffmpeg/resolution/by-name/{name}", Name="GetResolutionByName")]
public async Task<ActionResult<int>> GetResolutionByName(string name, CancellationToken cancellationToken)
{
Option<int> result = await mediator.Send(new GetResolutionByName(name), cancellationToken);
return result.Match<ActionResult<int>>(i => Ok(i), () => NotFound());
}
}

2
ErsatzTV/Controllers/Api/VersionController.cs

@ -11,7 +11,7 @@ public class VersionController
static VersionController() => static VersionController() =>
Version = new CombinedVersion( Version = new CombinedVersion(
1, 2,
Assembly.GetEntryAssembly()? Assembly.GetEntryAssembly()?
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()? .GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion ?? "unknown"); .InformationalVersion ?? "unknown");

633
ErsatzTV/wwwroot/openapi/v1.json

@ -66,6 +66,145 @@
} }
} }
}, },
"/api/ffmpeg/profiles": {
"get": {
"tags": [
"FFmpegProfile"
],
"operationId": "GetFFmpegProfiles",
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FFmpegFullProfileResponseModel"
}
}
},
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FFmpegFullProfileResponseModel"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FFmpegFullProfileResponseModel"
}
}
}
}
}
}
}
},
"/api/ffmpeg/profiles/new": {
"post": {
"tags": [
"FFmpegProfile"
],
"operationId": "CreateFFmpegProfile",
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/CreateFFmpegProfile"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateFFmpegProfile"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/CreateFFmpegProfile"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/CreateFFmpegProfile"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/ffmpeg/profiles/update": {
"put": {
"tags": [
"FFmpegProfile"
],
"operationId": "UpdateFFmpegProfile",
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/UpdateFFmpegProfile"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateFFmpegProfile"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/UpdateFFmpegProfile"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/UpdateFFmpegProfile"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/ffmpeg/delete/{id}": {
"delete": {
"tags": [
"FFmpegProfile"
],
"operationId": "DeleteFFmpegProfile",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/libraries/{id}/scan": { "/api/libraries/{id}/scan": {
"post": { "post": {
"tags": [ "tags": [
@ -175,6 +314,49 @@
} }
} }
}, },
"/api/ffmpeg/resolution/by-name/{name}": {
"get": {
"tags": [
"Resolution"
],
"operationId": "GetResolutionByName",
"parameters": [
{
"name": "name",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "integer",
"format": "int32"
}
},
"application/json": {
"schema": {
"type": "integer",
"format": "int32"
}
},
"text/json": {
"schema": {
"type": "integer",
"format": "int32"
}
}
}
}
}
}
},
"/api/sessions": { "/api/sessions": {
"get": { "get": {
"tags": [ "tags": [
@ -464,6 +646,128 @@
} }
} }
}, },
"CreateFFmpegProfile": {
"required": [
"name",
"threadCount",
"hardwareAcceleration",
"vaapiDisplay",
"vaapiDriver",
"vaapiDevice",
"qsvExtraHardwareFrames",
"resolutionId",
"scalingBehavior",
"videoFormat",
"videoProfile",
"videoPreset",
"allowBFrames",
"bitDepth",
"videoBitrate",
"videoBufferSize",
"tonemapAlgorithm",
"audioFormat",
"audioBitrate",
"audioBufferSize",
"normalizeLoudnessMode",
"audioChannels",
"audioSampleRate",
"normalizeFramerate",
"deinterlaceVideo"
],
"type": "object",
"properties": {
"name": {
"type": "string",
"nullable": true
},
"threadCount": {
"type": "integer",
"format": "int32"
},
"hardwareAcceleration": {
"$ref": "#/components/schemas/HardwareAccelerationKind"
},
"vaapiDisplay": {
"type": "string",
"nullable": true
},
"vaapiDriver": {
"$ref": "#/components/schemas/VaapiDriver"
},
"vaapiDevice": {
"type": "string",
"nullable": true
},
"qsvExtraHardwareFrames": {
"type": "integer",
"format": "int32",
"nullable": true
},
"resolutionId": {
"type": "integer",
"format": "int32"
},
"scalingBehavior": {
"$ref": "#/components/schemas/ScalingBehavior"
},
"videoFormat": {
"$ref": "#/components/schemas/FFmpegProfileVideoFormat"
},
"videoProfile": {
"type": "string",
"nullable": true
},
"videoPreset": {
"type": "string",
"nullable": true
},
"allowBFrames": {
"type": "boolean"
},
"bitDepth": {
"$ref": "#/components/schemas/FFmpegProfileBitDepth"
},
"videoBitrate": {
"type": "integer",
"format": "int32"
},
"videoBufferSize": {
"type": "integer",
"format": "int32"
},
"tonemapAlgorithm": {
"$ref": "#/components/schemas/FFmpegProfileTonemapAlgorithm"
},
"audioFormat": {
"$ref": "#/components/schemas/FFmpegProfileAudioFormat"
},
"audioBitrate": {
"type": "integer",
"format": "int32"
},
"audioBufferSize": {
"type": "integer",
"format": "int32"
},
"normalizeLoudnessMode": {
"$ref": "#/components/schemas/NormalizeLoudnessMode"
},
"audioChannels": {
"type": "integer",
"format": "int32"
},
"audioSampleRate": {
"type": "integer",
"format": "int32"
},
"normalizeFramerate": {
"type": "boolean"
},
"deinterlaceVideo": {
"type": "boolean"
}
}
},
"CreateSmartCollection": { "CreateSmartCollection": {
"required": [ "required": [
"query", "query",
@ -481,6 +785,180 @@
} }
} }
}, },
"FFmpegFullProfileResponseModel": {
"required": [
"id",
"name",
"threadCount",
"hardwareAcceleration",
"vaapiDisplay",
"vaapiDriver",
"vaapiDevice",
"qsvExtraHardwareFrames",
"resolution",
"scalingBehavior",
"videoFormat",
"videoProfile",
"videoPreset",
"allowBFrames",
"bitDepth",
"videoBitrate",
"videoBufferSize",
"tonemapAlgorithm",
"audioFormat",
"audioBitrate",
"audioBufferSize",
"normalizeLoudnessMode",
"audioChannels",
"audioSampleRate",
"normalizeFramerate",
"deinterlaceVideo"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string",
"nullable": true
},
"threadCount": {
"type": "integer",
"format": "int32"
},
"hardwareAcceleration": {
"$ref": "#/components/schemas/HardwareAccelerationKind"
},
"vaapiDisplay": {
"type": "string",
"nullable": true
},
"vaapiDriver": {
"$ref": "#/components/schemas/VaapiDriver"
},
"vaapiDevice": {
"type": "string",
"nullable": true
},
"qsvExtraHardwareFrames": {
"type": "integer",
"format": "int32",
"nullable": true
},
"resolution": {
"type": "string",
"nullable": true
},
"scalingBehavior": {
"$ref": "#/components/schemas/ScalingBehavior"
},
"videoFormat": {
"$ref": "#/components/schemas/FFmpegProfileVideoFormat"
},
"videoProfile": {
"type": "string",
"nullable": true
},
"videoPreset": {
"type": "string",
"nullable": true
},
"allowBFrames": {
"type": "boolean"
},
"bitDepth": {
"$ref": "#/components/schemas/FFmpegProfileBitDepth"
},
"videoBitrate": {
"type": "integer",
"format": "int32"
},
"videoBufferSize": {
"type": "integer",
"format": "int32"
},
"tonemapAlgorithm": {
"$ref": "#/components/schemas/FFmpegProfileTonemapAlgorithm"
},
"audioFormat": {
"$ref": "#/components/schemas/FFmpegProfileAudioFormat"
},
"audioBitrate": {
"type": "integer",
"format": "int32"
},
"audioBufferSize": {
"type": "integer",
"format": "int32"
},
"normalizeLoudnessMode": {
"$ref": "#/components/schemas/NormalizeLoudnessMode"
},
"audioChannels": {
"type": "integer",
"format": "int32"
},
"audioSampleRate": {
"type": "integer",
"format": "int32"
},
"normalizeFramerate": {
"type": "boolean"
},
"deinterlaceVideo": {
"type": "boolean",
"nullable": true
}
}
},
"FFmpegProfileAudioFormat": {
"enum": [
"None",
"Aac",
"Ac3",
"Copy"
]
},
"FFmpegProfileBitDepth": {
"enum": [
"EightBit",
"TenBit"
]
},
"FFmpegProfileTonemapAlgorithm": {
"enum": [
"Linear",
"Clip",
"Gamma",
"Reinhard",
"Mobius",
"Hable"
]
},
"FFmpegProfileVideoFormat": {
"enum": [
"None",
"H264",
"Hevc",
"Mpeg2Video",
"Av1",
"Copy"
]
},
"HardwareAccelerationKind": {
"enum": [
"None",
"Qsv",
"Nvenc",
"Vaapi",
"VideoToolbox",
"Amf",
"V4l2m2m",
"Rkmpp"
]
},
"HlsSessionModel": { "HlsSessionModel": {
"required": [ "required": [
"channelNumber", "channelNumber",
@ -508,6 +986,19 @@
} }
} }
}, },
"NormalizeLoudnessMode": {
"enum": [
"Off",
"LoudNorm"
]
},
"ScalingBehavior": {
"enum": [
"ScaleAndPad",
"Stretch",
"Crop"
]
},
"ScanShowRequest": { "ScanShowRequest": {
"required": [ "required": [
"showTitle" "showTitle"
@ -546,6 +1037,133 @@
} }
} }
}, },
"UpdateFFmpegProfile": {
"required": [
"fFmpegProfileId",
"name",
"threadCount",
"hardwareAcceleration",
"vaapiDisplay",
"vaapiDriver",
"vaapiDevice",
"qsvExtraHardwareFrames",
"resolutionId",
"scalingBehavior",
"videoFormat",
"videoProfile",
"videoPreset",
"allowBFrames",
"bitDepth",
"videoBitrate",
"videoBufferSize",
"tonemapAlgorithm",
"audioFormat",
"audioBitrate",
"audioBufferSize",
"normalizeLoudnessMode",
"audioChannels",
"audioSampleRate",
"normalizeFramerate",
"deinterlaceVideo"
],
"type": "object",
"properties": {
"fFmpegProfileId": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string",
"nullable": true
},
"threadCount": {
"type": "integer",
"format": "int32"
},
"hardwareAcceleration": {
"$ref": "#/components/schemas/HardwareAccelerationKind"
},
"vaapiDisplay": {
"type": "string",
"nullable": true
},
"vaapiDriver": {
"$ref": "#/components/schemas/VaapiDriver"
},
"vaapiDevice": {
"type": "string",
"nullable": true
},
"qsvExtraHardwareFrames": {
"type": "integer",
"format": "int32",
"nullable": true
},
"resolutionId": {
"type": "integer",
"format": "int32"
},
"scalingBehavior": {
"$ref": "#/components/schemas/ScalingBehavior"
},
"videoFormat": {
"$ref": "#/components/schemas/FFmpegProfileVideoFormat"
},
"videoProfile": {
"type": "string",
"nullable": true
},
"videoPreset": {
"type": "string",
"nullable": true
},
"allowBFrames": {
"type": "boolean"
},
"bitDepth": {
"$ref": "#/components/schemas/FFmpegProfileBitDepth"
},
"videoBitrate": {
"type": "integer",
"format": "int32"
},
"videoBufferSize": {
"type": "integer",
"format": "int32"
},
"tonemapAlgorithm": {
"$ref": "#/components/schemas/FFmpegProfileTonemapAlgorithm"
},
"audioFormat": {
"$ref": "#/components/schemas/FFmpegProfileAudioFormat"
},
"audioBitrate": {
"type": "integer",
"format": "int32"
},
"audioBufferSize": {
"type": "integer",
"format": "int32"
},
"normalizeLoudnessMode": {
"$ref": "#/components/schemas/NormalizeLoudnessMode"
},
"audioChannels": {
"type": "integer",
"format": "int32"
},
"audioSampleRate": {
"type": "integer",
"format": "int32"
},
"normalizeFramerate": {
"type": "boolean"
},
"deinterlaceVideo": {
"type": "boolean"
}
}
},
"UpdateSmartCollection": { "UpdateSmartCollection": {
"required": [ "required": [
"id", "id",
@ -567,6 +1185,15 @@
"nullable": true "nullable": true
} }
} }
},
"VaapiDriver": {
"enum": [
"Default",
"iHD",
"i965",
"RadeonSI",
"Nouveau"
]
} }
} }
}, },
@ -577,12 +1204,18 @@
{ {
"name": "Channels" "name": "Channels"
}, },
{
"name": "FFmpegProfile"
},
{ {
"name": "Libraries" "name": "Libraries"
}, },
{ {
"name": "Maintenance" "name": "Maintenance"
}, },
{
"name": "Resolution"
},
{ {
"name": "Sessions" "name": "Sessions"
}, },

Loading…
Cancel
Save