api changes for ffmpeg profiles (#2520)

This commit is contained in:
Jason Dove
2025-10-13 06:44:32 -05:00
committed by GitHub
parent 48e7c85f7b
commit b851a7daba
18 changed files with 764 additions and 48 deletions
+13 -7
View File
@@ -47,19 +47,25 @@ internal static class Mapper
ffmpegProfile.Id,
ffmpegProfile.Name,
ffmpegProfile.ThreadCount,
(int)ffmpegProfile.HardwareAcceleration,
ffmpegProfile.HardwareAcceleration,
ffmpegProfile.VaapiDisplay,
(int)ffmpegProfile.VaapiDriver,
ffmpegProfile.VaapiDriver,
ffmpegProfile.VaapiDevice,
ffmpegProfile.ResolutionId,
(int)ffmpegProfile.VideoFormat,
ffmpegProfile.QsvExtraHardwareFrames,
ffmpegProfile.Resolution.Name,
ffmpegProfile.ScalingBehavior,
ffmpegProfile.VideoFormat,
ffmpegProfile.VideoProfile,
ffmpegProfile.VideoPreset,
ffmpegProfile.AllowBFrames,
ffmpegProfile.BitDepth,
ffmpegProfile.VideoBitrate,
ffmpegProfile.VideoBufferSize,
(int)ffmpegProfile.TonemapAlgorithm,
(int)ffmpegProfile.AudioFormat,
ffmpegProfile.TonemapAlgorithm,
ffmpegProfile.AudioFormat,
ffmpegProfile.AudioBitrate,
ffmpegProfile.AudioBufferSize,
(int)ffmpegProfile.NormalizeLoudnessMode,
ffmpegProfile.NormalizeLoudnessMode,
ffmpegProfile.AudioChannels,
ffmpegProfile.AudioSampleRate,
ffmpegProfile.NormalizeFramerate,
@@ -2,4 +2,4 @@
namespace ErsatzTV.Application.FFmpegProfiles;
public record GetAllFFmpegProfilesForApi : IRequest<List<FFmpegProfileResponseModel>>;
public record GetAllFFmpegProfilesForApi : IRequest<List<FFmpegFullProfileResponseModel>>;
@@ -6,23 +6,18 @@ using static ErsatzTV.Application.FFmpegProfiles.Mapper;
namespace ErsatzTV.Application.FFmpegProfiles;
public class
GetAllFFmpegProfilesForApiHandler : IRequestHandler<GetAllFFmpegProfilesForApi, List<FFmpegProfileResponseModel>>
public class GetAllFFmpegProfilesForApiHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetAllFFmpegProfilesForApi, List<FFmpegFullProfileResponseModel>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public GetAllFFmpegProfilesForApiHandler(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
public async Task<List<FFmpegProfileResponseModel>> Handle(
public async Task<List<FFmpegFullProfileResponseModel>> Handle(
GetAllFFmpegProfilesForApi request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
List<FFmpegProfile> ffmpegProfiles = await dbContext.FFmpegProfiles
.AsNoTracking()
.Include(p => p.Resolution)
.ToListAsync(cancellationToken);
return ffmpegProfiles.Map(ProjectToResponseModel).ToList();
return ffmpegProfiles.Map(ProjectToFullResponseModel).ToList();
}
}
@@ -0,0 +1,5 @@
using ErsatzTV.Core;
namespace ErsatzTV.Application.FFmpegProfiles;
public record GetResolutionByName(string Name) : IRequest<Option<int>>;
@@ -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);
}
}
@@ -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(
int Id,
string Name,
int ThreadCount,
int HardwareAcceleration,
HardwareAccelerationKind HardwareAcceleration,
string VaapiDisplay,
int VaapiDriver,
VaapiDriver VaapiDriver,
string VaapiDevice,
int ResolutionId,
int VideoFormat,
int? QsvExtraHardwareFrames,
string Resolution,
ScalingBehavior ScalingBehavior,
FFmpegProfileVideoFormat VideoFormat,
string VideoProfile,
string VideoPreset,
bool AllowBFrames,
FFmpegProfileBitDepth BitDepth,
int VideoBitrate,
int VideoBufferSize,
int TonemapAlgorithm,
int AudioFormat,
FFmpegProfileTonemapAlgorithm TonemapAlgorithm,
FFmpegProfileAudioFormat AudioFormat,
int AudioBitrate,
int AudioBufferSize,
int NormalizeLoudnessMode,
NormalizeLoudnessMode NormalizeLoudnessMode,
int AudioChannels,
int AudioSampleRate,
bool NormalizeFramerate,
@@ -1,5 +1,8 @@
namespace ErsatzTV.Core.Domain;
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileAudioFormat>))]
public enum FFmpegProfileAudioFormat
{
None = 0,
@@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileBitDepth>))]
public enum FFmpegProfileBitDepth
{
EightBit = 0,
@@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileTonemapAlgorithm>))]
public enum FFmpegProfileTonemapAlgorithm
{
Linear = 0,
@@ -1,5 +1,8 @@
namespace ErsatzTV.Core.Domain;
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<FFmpegProfileVideoFormat>))]
public enum FFmpegProfileVideoFormat
{
None = 0,
@@ -1,5 +1,8 @@
namespace ErsatzTV.Core.Domain;
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<HardwareAccelerationKind>))]
public enum HardwareAccelerationKind
{
None = 0,
@@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<NormalizeLoudnessMode>))]
public enum NormalizeLoudnessMode
{
Off = 0,
+3
View File
@@ -1,5 +1,8 @@
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.Domain;
[JsonConverter(typeof(JsonStringEnumConverter<ScalingBehavior>))]
public enum ScalingBehavior
{
ScaleAndPad = 0,
+2
View File
@@ -1,8 +1,10 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
namespace ErsatzTV.Core.FFmpeg;
[SuppressMessage("ReSharper", "InconsistentNaming")]
[JsonConverter(typeof(JsonStringEnumConverter<VaapiDriver>))]
public enum VaapiDriver
{
Default = 0,
@@ -2,34 +2,43 @@
using ErsatzTV.Application.FFmpegProfiles;
using ErsatzTV.Core;
using ErsatzTV.Core.Api.FFmpegProfiles;
using ErsatzTV.Filters;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace ErsatzTV.Controllers.Api;
[ApiController]
[V2ApiActionFilter]
public class FFmpegProfileController(IMediator mediator)
[EndpointGroupName("general")]
public class FFmpegProfileController(IMediator mediator) : ControllerBase
{
[HttpGet("/api/ffmpeg/profiles")]
public async Task<List<FFmpegProfileResponseModel>> GetAll() =>
await mediator.Send(new GetAllFFmpegProfilesForApi());
[HttpGet("/api/ffmpeg/profiles", Name="GetFFmpegProfiles")]
public async Task<List<FFmpegFullProfileResponseModel>> GetAll(CancellationToken cancellationToken) =>
await mediator.Send(new GetAllFFmpegProfilesForApi(), cancellationToken);
[HttpPost("/api/ffmpeg/profiles/new")]
public async Task<Either<BaseError, CreateFFmpegProfileResult>> AddOne(
[HttpPost("/api/ffmpeg/profiles/new", Name="CreateFFmpegProfile")]
public async Task<IActionResult> AddOne(
[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")]
public async Task<Either<BaseError, UpdateFFmpegProfileResult>> UpdateOne(
[HttpPut("/api/ffmpeg/profiles/update", Name="UpdateFFmpegProfile")]
public async Task<IActionResult> UpdateOne(
[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}")]
public async Task<Option<FFmpegFullProfileResponseModel>> GetOne(int id) =>
await mediator.Send(new GetFFmpegFullProfileByIdForApi(id));
[HttpDelete("/api/ffmpeg/delete/{id:int}")]
public async Task DeleteProfileAsync(int id) => await mediator.Send(new DeleteFFmpegProfile(id));
[HttpDelete("/api/ffmpeg/delete/{id:int}", Name="DeleteFFmpegProfile")]
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 => Problem(error.ToString()));
}
}
@@ -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());
}
}
@@ -11,7 +11,7 @@ public class VersionController
static VersionController() =>
Version = new CombinedVersion(
1,
2,
Assembly.GetEntryAssembly()?
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion ?? "unknown");
+633
View File
@@ -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": {
"post": {
"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": {
"get": {
"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": {
"required": [
"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": {
"required": [
"channelNumber",
@@ -508,6 +986,19 @@
}
}
},
"NormalizeLoudnessMode": {
"enum": [
"Off",
"LoudNorm"
]
},
"ScalingBehavior": {
"enum": [
"ScaleAndPad",
"Stretch",
"Crop"
]
},
"ScanShowRequest": {
"required": [
"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": {
"required": [
"id",
@@ -567,6 +1185,15 @@
"nullable": true
}
}
},
"VaapiDriver": {
"enum": [
"Default",
"iHD",
"i965",
"RadeonSI",
"Nouveau"
]
}
}
},
@@ -577,12 +1204,18 @@
{
"name": "Channels"
},
{
"name": "FFmpegProfile"
},
{
"name": "Libraries"
},
{
"name": "Maintenance"
},
{
"name": "Resolution"
},
{
"name": "Sessions"
},