mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge remote-tracking branch 'upstream/master' into mad-stylez
This commit is contained in:
commit
becd3b1542
@ -14,7 +14,7 @@ COPY . .
|
|||||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
# because of changes in docker and systemd we need to not build in parallel at the moment
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
FROM debian:buster-slim
|
FROM debian:buster-slim
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|||||||
# Discard objs - may cause failures if exists
|
# Discard objs - may cause failures if exists
|
||||||
RUN find . -type d -name obj | xargs -r rm -r
|
RUN find . -type d -name obj | xargs -r rm -r
|
||||||
# Build
|
# Build
|
||||||
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
||||||
|
@ -21,7 +21,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|||||||
# Discard objs - may cause failures if exists
|
# Discard objs - may cause failures if exists
|
||||||
RUN find . -type d -name obj | xargs -r rm -r
|
RUN find . -type d -name obj | xargs -r rm -r
|
||||||
# Build
|
# Build
|
||||||
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
||||||
FROM arm64v8/debian:buster-slim
|
FROM arm64v8/debian:buster-slim
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
|
using MediaBrowser.Common.Json;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Jellyfin.Api
|
namespace Jellyfin.Api
|
||||||
@ -8,7 +9,10 @@ namespace Jellyfin.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
[Produces(MediaTypeNames.Application.Json)]
|
[Produces(
|
||||||
|
MediaTypeNames.Application.Json,
|
||||||
|
JsonDefaults.CamelCaseMediaType,
|
||||||
|
JsonDefaults.PascalCaseMediaType)]
|
||||||
public class BaseJellyfinApiController : ControllerBase
|
public class BaseJellyfinApiController : ControllerBase
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Mime;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Emby.Dlna;
|
using Emby.Dlna;
|
||||||
using Emby.Dlna.Main;
|
using Emby.Dlna.Main;
|
||||||
@ -17,8 +18,6 @@ namespace Jellyfin.Api.Controllers
|
|||||||
[Route("Dlna")]
|
[Route("Dlna")]
|
||||||
public class DlnaServerController : BaseJellyfinApiController
|
public class DlnaServerController : BaseJellyfinApiController
|
||||||
{
|
{
|
||||||
private const string XMLContentType = "text/xml; charset=UTF-8";
|
|
||||||
|
|
||||||
private readonly IDlnaManager _dlnaManager;
|
private readonly IDlnaManager _dlnaManager;
|
||||||
private readonly IContentDirectory _contentDirectory;
|
private readonly IContentDirectory _contentDirectory;
|
||||||
private readonly IConnectionManager _connectionManager;
|
private readonly IConnectionManager _connectionManager;
|
||||||
@ -44,7 +43,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
/// <returns>An <see cref="OkResult"/> containing the description xml.</returns>
|
/// <returns>An <see cref="OkResult"/> containing the description xml.</returns>
|
||||||
[HttpGet("{serverId}/description")]
|
[HttpGet("{serverId}/description")]
|
||||||
[HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")]
|
[HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")]
|
||||||
[Produces(XMLContentType)]
|
[Produces(MediaTypeNames.Text.Xml)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public ActionResult GetDescriptionXml([FromRoute] string serverId)
|
public ActionResult GetDescriptionXml([FromRoute] string serverId)
|
||||||
{
|
{
|
||||||
@ -63,7 +62,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
[HttpGet("{serverId}/ContentDirectory")]
|
[HttpGet("{serverId}/ContentDirectory")]
|
||||||
[HttpGet("{serverId}/ContentDirectory/ContentDirectory", Name = "GetContentDirectory_2")]
|
[HttpGet("{serverId}/ContentDirectory/ContentDirectory", Name = "GetContentDirectory_2")]
|
||||||
[HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_3")]
|
[HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_3")]
|
||||||
[Produces(XMLContentType)]
|
[Produces(MediaTypeNames.Text.Xml)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
||||||
public ActionResult GetContentDirectory([FromRoute] string serverId)
|
public ActionResult GetContentDirectory([FromRoute] string serverId)
|
||||||
@ -79,7 +78,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
[HttpGet("{serverId}/MediaReceiverRegistrar")]
|
[HttpGet("{serverId}/MediaReceiverRegistrar")]
|
||||||
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar", Name = "GetMediaReceiverRegistrar_2")]
|
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar", Name = "GetMediaReceiverRegistrar_2")]
|
||||||
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_3")]
|
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_3")]
|
||||||
[Produces(XMLContentType)]
|
[Produces(MediaTypeNames.Text.Xml)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
||||||
public ActionResult GetMediaReceiverRegistrar([FromRoute] string serverId)
|
public ActionResult GetMediaReceiverRegistrar([FromRoute] string serverId)
|
||||||
@ -95,7 +94,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
[HttpGet("{serverId}/ConnectionManager")]
|
[HttpGet("{serverId}/ConnectionManager")]
|
||||||
[HttpGet("{serverId}/ConnectionManager/ConnectionManager", Name = "GetConnectionManager_2")]
|
[HttpGet("{serverId}/ConnectionManager/ConnectionManager", Name = "GetConnectionManager_2")]
|
||||||
[HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_3")]
|
[HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_3")]
|
||||||
[Produces(XMLContentType)]
|
[Produces(MediaTypeNames.Text.Xml)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
||||||
public ActionResult GetConnectionManager([FromRoute] string serverId)
|
public ActionResult GetConnectionManager([FromRoute] string serverId)
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
|
||||||
|
|
||||||
namespace Jellyfin.Api
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Route prefixing for ASP.NET MVC.
|
|
||||||
/// </summary>
|
|
||||||
public static class MvcRoutePrefix
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Adds route prefixes to the MVC conventions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="opts">The MVC options.</param>
|
|
||||||
/// <param name="prefixes">The list of prefixes.</param>
|
|
||||||
public static void UseGeneralRoutePrefix(this MvcOptions opts, params string[] prefixes)
|
|
||||||
{
|
|
||||||
opts.Conventions.Insert(0, new RoutePrefixConvention(prefixes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RoutePrefixConvention : IApplicationModelConvention
|
|
||||||
{
|
|
||||||
private readonly AttributeRouteModel[] _routePrefixes;
|
|
||||||
|
|
||||||
public RoutePrefixConvention(IEnumerable<string> prefixes)
|
|
||||||
{
|
|
||||||
_routePrefixes = prefixes.Select(p => new AttributeRouteModel(new RouteAttribute(p))).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Apply(ApplicationModel application)
|
|
||||||
{
|
|
||||||
foreach (var controller in application.Controllers)
|
|
||||||
{
|
|
||||||
if (controller.Selectors == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newSelectors = new List<SelectorModel>();
|
|
||||||
foreach (var selector in controller.Selectors)
|
|
||||||
{
|
|
||||||
newSelectors.AddRange(_routePrefixes.Select(routePrefix => new SelectorModel(selector)
|
|
||||||
{
|
|
||||||
AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(routePrefix, selector.AttributeRouteModel)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.Selectors.Clear();
|
|
||||||
newSelectors.ForEach(selector => controller.Selectors.Add(selector));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Jellyfin.Api;
|
|
||||||
using Jellyfin.Api.Auth;
|
using Jellyfin.Api.Auth;
|
||||||
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
|
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
|
||||||
using Jellyfin.Api.Auth.DownloadPolicy;
|
using Jellyfin.Api.Auth.DownloadPolicy;
|
||||||
@ -18,7 +17,6 @@ using Jellyfin.Api.Constants;
|
|||||||
using Jellyfin.Api.Controllers;
|
using Jellyfin.Api.Controllers;
|
||||||
using Jellyfin.Server.Formatters;
|
using Jellyfin.Server.Formatters;
|
||||||
using Jellyfin.Server.Models;
|
using Jellyfin.Server.Models;
|
||||||
using MediaBrowser.Common;
|
|
||||||
using MediaBrowser.Common.Json;
|
using MediaBrowser.Common.Json;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
@ -150,6 +148,9 @@ namespace Jellyfin.Server.Extensions
|
|||||||
})
|
})
|
||||||
.AddMvc(opts =>
|
.AddMvc(opts =>
|
||||||
{
|
{
|
||||||
|
// Allow requester to change between camelCase and PascalCase
|
||||||
|
opts.RespectBrowserAcceptHeader = true;
|
||||||
|
|
||||||
opts.OutputFormatters.Insert(0, new CamelCaseJsonProfileFormatter());
|
opts.OutputFormatters.Insert(0, new CamelCaseJsonProfileFormatter());
|
||||||
opts.OutputFormatters.Insert(0, new PascalCaseJsonProfileFormatter());
|
opts.OutputFormatters.Insert(0, new PascalCaseJsonProfileFormatter());
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ namespace Jellyfin.Server.Formatters
|
|||||||
public CamelCaseJsonProfileFormatter() : base(JsonDefaults.GetCamelCaseOptions())
|
public CamelCaseJsonProfileFormatter() : base(JsonDefaults.GetCamelCaseOptions())
|
||||||
{
|
{
|
||||||
SupportedMediaTypes.Clear();
|
SupportedMediaTypes.Clear();
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json;profile=\"CamelCase\""));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(JsonDefaults.CamelCaseMediaType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Net.Mime;
|
||||||
using MediaBrowser.Common.Json;
|
using MediaBrowser.Common.Json;
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
@ -16,8 +17,8 @@ namespace Jellyfin.Server.Formatters
|
|||||||
{
|
{
|
||||||
SupportedMediaTypes.Clear();
|
SupportedMediaTypes.Clear();
|
||||||
// Add application/json for default formatter
|
// Add application/json for default formatter
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json));
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json;profile=\"PascalCase\""));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(JsonDefaults.PascalCaseMediaType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,9 @@ namespace Jellyfin.Server.Formatters
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public XmlOutputFormatter()
|
public XmlOutputFormatter()
|
||||||
{
|
{
|
||||||
|
SupportedMediaTypes.Clear();
|
||||||
SupportedMediaTypes.Add(MediaTypeNames.Text.Xml);
|
SupportedMediaTypes.Add(MediaTypeNames.Text.Xml);
|
||||||
SupportedMediaTypes.Add("text/xml;charset=UTF-8");
|
|
||||||
SupportedEncodings.Add(Encoding.UTF8);
|
SupportedEncodings.Add(Encoding.UTF8);
|
||||||
SupportedEncodings.Add(Encoding.Unicode);
|
SupportedEncodings.Add(Encoding.Unicode);
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Jellyfin.Server.Implementations;
|
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
||||||
|
|
||||||
namespace Jellyfin.Server.HealthChecks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Checks connectivity to the database.
|
|
||||||
/// </summary>
|
|
||||||
public class JellyfinDbHealthCheck : IHealthCheck
|
|
||||||
{
|
|
||||||
private readonly JellyfinDbProvider _dbProvider;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="JellyfinDbHealthCheck"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dbProvider">The jellyfin db provider.</param>
|
|
||||||
public JellyfinDbHealthCheck(JellyfinDbProvider dbProvider)
|
|
||||||
{
|
|
||||||
_dbProvider = dbProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
await using var jellyfinDb = _dbProvider.CreateContext();
|
|
||||||
if (await jellyfinDb.Database.CanConnectAsync(cancellationToken).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
return HealthCheckResult.Healthy("Database connection successful.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return HealthCheckResult.Unhealthy("Unable to connect to the database.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -44,6 +44,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.7" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.7" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.7" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.7" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.7" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.7" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="3.1.7" />
|
||||||
<PackageReference Include="prometheus-net" Version="3.6.0" />
|
<PackageReference Include="prometheus-net" Version="3.6.0" />
|
||||||
<PackageReference Include="prometheus-net.AspNetCore" Version="3.6.0" />
|
<PackageReference Include="prometheus-net.AspNetCore" Version="3.6.0" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
||||||
|
@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using Jellyfin.Api.TypeConverters;
|
using Jellyfin.Api.TypeConverters;
|
||||||
using Jellyfin.Server.Extensions;
|
using Jellyfin.Server.Extensions;
|
||||||
using Jellyfin.Server.HealthChecks;
|
using Jellyfin.Server.Implementations;
|
||||||
using Jellyfin.Server.Middleware;
|
using Jellyfin.Server.Middleware;
|
||||||
using Jellyfin.Server.Models;
|
using Jellyfin.Server.Models;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
@ -80,7 +80,7 @@ namespace Jellyfin.Server
|
|||||||
.ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
|
.ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
|
||||||
|
|
||||||
services.AddHealthChecks()
|
services.AddHealthChecks()
|
||||||
.AddCheck<JellyfinDbHealthCheck>("JellyfinDb");
|
.AddDbContextCheck<JellyfinDb>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -9,6 +9,16 @@ namespace MediaBrowser.Common.Json
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class JsonDefaults
|
public static class JsonDefaults
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pascal case json profile media type.
|
||||||
|
/// </summary>
|
||||||
|
public const string PascalCaseMediaType = "application/json; profile=\"PascalCase\"";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Camel case json profile media type.
|
||||||
|
/// </summary>
|
||||||
|
public const string CamelCaseMediaType = "application/json; profile=\"CamelCase\"";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the default <see cref="JsonSerializerOptions" /> options.
|
/// Gets the default <see cref="JsonSerializerOptions" /> options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -60,8 +60,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
protected BaseItem()
|
protected BaseItem()
|
||||||
{
|
{
|
||||||
ThemeSongIds = Array.Empty<Guid>();
|
|
||||||
ThemeVideoIds = Array.Empty<Guid>();
|
|
||||||
Tags = Array.Empty<string>();
|
Tags = Array.Empty<string>();
|
||||||
Genres = Array.Empty<string>();
|
Genres = Array.Empty<string>();
|
||||||
Studios = Array.Empty<string>();
|
Studios = Array.Empty<string>();
|
||||||
@ -100,12 +98,52 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
};
|
};
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Guid[] ThemeSongIds { get; set; }
|
public Guid[] ThemeSongIds
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_themeSongIds == null)
|
||||||
|
{
|
||||||
|
_themeSongIds = GetExtras()
|
||||||
|
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong)
|
||||||
|
.Select(song => song.Id)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _themeSongIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_themeSongIds = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Guid[] ThemeVideoIds { get; set; }
|
public Guid[] ThemeVideoIds
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_themeVideoIds == null)
|
||||||
|
{
|
||||||
|
_themeVideoIds = GetExtras()
|
||||||
|
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo)
|
||||||
|
.Select(song => song.Id)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _themeVideoIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_themeVideoIds = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string PreferredMetadataCountryCode { get; set; }
|
public string PreferredMetadataCountryCode { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string PreferredMetadataLanguage { get; set; }
|
public string PreferredMetadataLanguage { get; set; }
|
||||||
|
|
||||||
@ -635,6 +673,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
private string _sortName;
|
private string _sortName;
|
||||||
|
private Guid[] _themeSongIds;
|
||||||
|
private Guid[] _themeVideoIds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the sort.
|
/// Gets the name of the sort.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1582,7 +1623,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
|
|
||||||
item.ThemeVideoIds = newThemeVideoIds;
|
// They are expected to be sorted by SortName
|
||||||
|
item.ThemeVideoIds = newThemeVideos.OrderBy(i => i.SortName).Select(i => i.Id).ToArray();
|
||||||
|
|
||||||
return themeVideosChanged;
|
return themeVideosChanged;
|
||||||
}
|
}
|
||||||
@ -1619,7 +1661,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
|
|
||||||
item.ThemeSongIds = newThemeSongIds;
|
// They are expected to be sorted by SortName
|
||||||
|
item.ThemeSongIds = newThemeSongs.OrderBy(i => i.SortName).Select(i => i.Id).ToArray();
|
||||||
|
|
||||||
return themeSongsChanged;
|
return themeSongsChanged;
|
||||||
}
|
}
|
||||||
@ -2910,12 +2953,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public IEnumerable<BaseItem> GetThemeSongs()
|
public IEnumerable<BaseItem> GetThemeSongs()
|
||||||
{
|
{
|
||||||
return ThemeVideoIds.Select(LibraryManager.GetItemById).Where(i => i.ExtraType.Equals(Model.Entities.ExtraType.ThemeSong)).OrderBy(i => i.SortName);
|
return ThemeSongIds.Select(LibraryManager.GetItemById);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<BaseItem> GetThemeVideos()
|
public IEnumerable<BaseItem> GetThemeVideos()
|
||||||
{
|
{
|
||||||
return ThemeVideoIds.Select(LibraryManager.GetItemById).Where(i => i.ExtraType.Equals(Model.Entities.ExtraType.ThemeVideo)).OrderBy(i => i.SortName);
|
return ThemeVideoIds.Select(LibraryManager.GetItemById);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -456,6 +456,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
|
var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
var isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
|
var isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
|
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
|
var isNvencHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||||
var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||||
@ -515,6 +516,24 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.IsVideoRequest
|
||||||
|
&& string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var isColorDepth10 = IsColorDepth10(state);
|
||||||
|
|
||||||
|
if (isNvencHevcDecoder && isColorDepth10
|
||||||
|
&& _mediaEncoder.SupportsHwaccel("opencl")
|
||||||
|
&& encodingOptions.EnableTonemapping
|
||||||
|
&& !string.IsNullOrEmpty(state.VideoStream.VideoRange)
|
||||||
|
&& state.VideoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
arg.Append("-init_hw_device opencl=ocl:")
|
||||||
|
.Append(encodingOptions.OpenclDevice)
|
||||||
|
.Append(' ')
|
||||||
|
.Append("-filter_hw_device ocl ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state.IsVideoRequest
|
if (state.IsVideoRequest
|
||||||
&& string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
|
&& string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@ -1003,11 +1022,33 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)
|
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
&& !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
&& !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
|
&& !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
param = "-pix_fmt yuv420p " + param;
|
param = "-pix_fmt yuv420p " + param;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
|
||||||
|
var videoStream = state.VideoStream;
|
||||||
|
var isColorDepth10 = IsColorDepth10(state);
|
||||||
|
|
||||||
|
if (videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1
|
||||||
|
&& isColorDepth10
|
||||||
|
&& _mediaEncoder.SupportsHwaccel("opencl")
|
||||||
|
&& encodingOptions.EnableTonemapping
|
||||||
|
&& !string.IsNullOrEmpty(videoStream.VideoRange)
|
||||||
|
&& videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
param = "-pix_fmt nv12 " + param;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
param = "-pix_fmt yuv420p " + param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
param = "-pix_fmt nv21 " + param;
|
param = "-pix_fmt nv21 " + param;
|
||||||
@ -1611,64 +1652,45 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
var outputSizeParam = ReadOnlySpan<char>.Empty;
|
var outputSizeParam = ReadOnlySpan<char>.Empty;
|
||||||
var request = state.BaseRequest;
|
var request = state.BaseRequest;
|
||||||
|
|
||||||
// Add resolution params, if specified
|
outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"');
|
||||||
if (request.Width.HasValue
|
|
||||||
|| request.Height.HasValue
|
// All possible beginning of video filters
|
||||||
|| request.MaxHeight.HasValue
|
// Don't break the order
|
||||||
|| request.MaxWidth.HasValue)
|
string[] beginOfOutputSizeParam = new[]
|
||||||
{
|
{
|
||||||
outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"');
|
// for tonemap_opencl
|
||||||
|
"hwupload,tonemap_opencl",
|
||||||
|
|
||||||
// hwupload=extra_hw_frames=64,vpp_qsv (for overlay_qsv on linux)
|
// hwupload=extra_hw_frames=64,vpp_qsv (for overlay_qsv on linux)
|
||||||
var index = outputSizeParam.IndexOf("hwupload=extra_hw_frames", StringComparison.OrdinalIgnoreCase);
|
"hwupload=extra_hw_frames",
|
||||||
|
|
||||||
|
// vpp_qsv
|
||||||
|
"vpp",
|
||||||
|
|
||||||
|
// hwdownload,format=p010le (hardware decode + software encode for vaapi)
|
||||||
|
"hwdownload",
|
||||||
|
|
||||||
|
// format=nv12|vaapi,hwupload,scale_vaapi
|
||||||
|
"format",
|
||||||
|
|
||||||
|
// bwdif,scale=expr
|
||||||
|
"bwdif",
|
||||||
|
|
||||||
|
// yadif,scale=expr
|
||||||
|
"yadif",
|
||||||
|
|
||||||
|
// scale=expr
|
||||||
|
"scale"
|
||||||
|
};
|
||||||
|
|
||||||
|
var index = -1;
|
||||||
|
foreach (var param in beginOfOutputSizeParam)
|
||||||
|
{
|
||||||
|
index = outputSizeParam.IndexOf(param, StringComparison.OrdinalIgnoreCase);
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
outputSizeParam = outputSizeParam.Slice(index);
|
outputSizeParam = outputSizeParam.Slice(index);
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
|
||||||
// vpp_qsv
|
|
||||||
index = outputSizeParam.IndexOf("vpp", StringComparison.OrdinalIgnoreCase);
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
outputSizeParam = outputSizeParam.Slice(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// hwdownload,format=p010le (hardware decode + software encode for vaapi)
|
|
||||||
index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase);
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
outputSizeParam = outputSizeParam.Slice(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// format=nv12|vaapi,hwupload,scale_vaapi
|
|
||||||
index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
outputSizeParam = outputSizeParam.Slice(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// yadif,scale=expr
|
|
||||||
index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase);
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
outputSizeParam = outputSizeParam.Slice(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// scale=expr
|
|
||||||
index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
outputSizeParam = outputSizeParam.Slice(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1747,9 +1769,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
*/
|
*/
|
||||||
if (isLinux)
|
if (isLinux)
|
||||||
{
|
{
|
||||||
retStr = !outputSizeParam.IsEmpty ?
|
retStr = !outputSizeParam.IsEmpty
|
||||||
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" :
|
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\""
|
||||||
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
|
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2084,8 +2106,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
|
var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1;
|
var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
var isNvdecH264Decoder = videoDecoder.IndexOf("h264_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
|
var isNvdecH264Decoder = videoDecoder.IndexOf("h264_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
|
var isNvdecHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1;
|
var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||||
|
var isColorDepth10 = IsColorDepth10(state);
|
||||||
|
|
||||||
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||||
@ -2093,6 +2117,50 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
// If double rate deinterlacing is enabled and the input framerate is 30fps or below, otherwise the output framerate will be too high for many devices
|
// If double rate deinterlacing is enabled and the input framerate is 30fps or below, otherwise the output framerate will be too high for many devices
|
||||||
var doubleRateDeinterlace = options.DeinterlaceDoubleRate && (videoStream?.RealFrameRate ?? 60) <= 30;
|
var doubleRateDeinterlace = options.DeinterlaceDoubleRate && (videoStream?.RealFrameRate ?? 60) <= 30;
|
||||||
|
|
||||||
|
// Currently only with the use of NVENC decoder can we get a decent performance.
|
||||||
|
// Currently only the HEVC/H265 format is supported.
|
||||||
|
// NVIDIA Pascal and Turing or higher are recommended.
|
||||||
|
if (isNvdecHevcDecoder && isColorDepth10
|
||||||
|
&& _mediaEncoder.SupportsHwaccel("opencl")
|
||||||
|
&& options.EnableTonemapping
|
||||||
|
&& !string.IsNullOrEmpty(videoStream.VideoRange)
|
||||||
|
&& videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
|
||||||
|
|
||||||
|
if (options.TonemappingParam != 0)
|
||||||
|
{
|
||||||
|
parameters += ":param={4}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
parameters += ":range={5}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload the HDR10 or HLG data to the OpenCL device,
|
||||||
|
// use tonemap_opencl filter for tone mapping,
|
||||||
|
// and then download the SDR data to memory.
|
||||||
|
filters.Add("hwupload");
|
||||||
|
filters.Add(
|
||||||
|
string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
parameters,
|
||||||
|
options.TonemappingAlgorithm,
|
||||||
|
options.TonemappingDesat,
|
||||||
|
options.TonemappingThreshold,
|
||||||
|
options.TonemappingPeak,
|
||||||
|
options.TonemappingParam,
|
||||||
|
options.TonemappingRange));
|
||||||
|
filters.Add("hwdownload");
|
||||||
|
|
||||||
|
if (hasGraphicalSubs || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)
|
||||||
|
|| string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
filters.Add("format=nv12");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When the input may or may not be hardware VAAPI decodable
|
// When the input may or may not be hardware VAAPI decodable
|
||||||
if (isVaapiH264Encoder)
|
if (isVaapiH264Encoder)
|
||||||
{
|
{
|
||||||
@ -2110,7 +2178,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
else if (IsVaapiSupported(state) && isVaapiDecoder && isLibX264Encoder)
|
else if (IsVaapiSupported(state) && isVaapiDecoder && isLibX264Encoder)
|
||||||
{
|
{
|
||||||
var codec = videoStream.Codec.ToLowerInvariant();
|
var codec = videoStream.Codec.ToLowerInvariant();
|
||||||
var isColorDepth10 = IsColorDepth10(state);
|
|
||||||
|
|
||||||
// Assert 10-bit hardware VAAPI decodable
|
// Assert 10-bit hardware VAAPI decodable
|
||||||
if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||||
|
@ -282,6 +282,20 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
[JsonPropertyName("disposition")]
|
[JsonPropertyName("disposition")]
|
||||||
public IReadOnlyDictionary<string, int> Disposition { get; set; }
|
public IReadOnlyDictionary<string, int> Disposition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color range.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The color range.</value>
|
||||||
|
[JsonPropertyName("color_range")]
|
||||||
|
public string ColorRange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color space.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The color space.</value>
|
||||||
|
[JsonPropertyName("color_space")]
|
||||||
|
public string ColorSpace { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the color transfer.
|
/// Gets or sets the color transfer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -714,6 +714,16 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
stream.RefFrames = streamInfo.Refs;
|
stream.RefFrames = streamInfo.Refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(streamInfo.ColorRange))
|
||||||
|
{
|
||||||
|
stream.ColorRange = streamInfo.ColorRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(streamInfo.ColorSpace))
|
||||||
|
{
|
||||||
|
stream.ColorSpace = streamInfo.ColorSpace;
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(streamInfo.ColorTransfer))
|
if (!string.IsNullOrEmpty(streamInfo.ColorTransfer))
|
||||||
{
|
{
|
||||||
stream.ColorTransfer = streamInfo.ColorTransfer;
|
stream.ColorTransfer = streamInfo.ColorTransfer;
|
||||||
|
@ -31,6 +31,22 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
|
|
||||||
public string VaapiDevice { get; set; }
|
public string VaapiDevice { get; set; }
|
||||||
|
|
||||||
|
public string OpenclDevice { get; set; }
|
||||||
|
|
||||||
|
public bool EnableTonemapping { get; set; }
|
||||||
|
|
||||||
|
public string TonemappingAlgorithm { get; set; }
|
||||||
|
|
||||||
|
public string TonemappingRange { get; set; }
|
||||||
|
|
||||||
|
public double TonemappingDesat { get; set; }
|
||||||
|
|
||||||
|
public double TonemappingThreshold { get; set; }
|
||||||
|
|
||||||
|
public double TonemappingPeak { get; set; }
|
||||||
|
|
||||||
|
public double TonemappingParam { get; set; }
|
||||||
|
|
||||||
public int H264Crf { get; set; }
|
public int H264Crf { get; set; }
|
||||||
|
|
||||||
public int H265Crf { get; set; }
|
public int H265Crf { get; set; }
|
||||||
@ -58,8 +74,19 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
EnableThrottling = false;
|
EnableThrottling = false;
|
||||||
ThrottleDelaySeconds = 180;
|
ThrottleDelaySeconds = 180;
|
||||||
EncodingThreadCount = -1;
|
EncodingThreadCount = -1;
|
||||||
// This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything
|
// This is a DRM device that is almost guaranteed to be there on every intel platform,
|
||||||
|
// plus it's the default one in ffmpeg if you don't specify anything
|
||||||
VaapiDevice = "/dev/dri/renderD128";
|
VaapiDevice = "/dev/dri/renderD128";
|
||||||
|
// This is the OpenCL device that is used for tonemapping.
|
||||||
|
// The left side of the dot is the platform number, and the right side is the device number on the platform.
|
||||||
|
OpenclDevice = "0.0";
|
||||||
|
EnableTonemapping = false;
|
||||||
|
TonemappingAlgorithm = "reinhard";
|
||||||
|
TonemappingRange = "auto";
|
||||||
|
TonemappingDesat = 0;
|
||||||
|
TonemappingThreshold = 0.8;
|
||||||
|
TonemappingPeak = 0;
|
||||||
|
TonemappingParam = 0;
|
||||||
H264Crf = 23;
|
H264Crf = 23;
|
||||||
H265Crf = 28;
|
H265Crf = 28;
|
||||||
DeinterlaceDoubleRate = false;
|
DeinterlaceDoubleRate = false;
|
||||||
|
@ -35,6 +35,18 @@ namespace MediaBrowser.Model.Entities
|
|||||||
/// <value>The language.</value>
|
/// <value>The language.</value>
|
||||||
public string Language { get; set; }
|
public string Language { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color range.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The color range.</value>
|
||||||
|
public string ColorRange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color space.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The color space.</value>
|
||||||
|
public string ColorSpace { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the color transfer.
|
/// Gets or sets the color transfer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -47,12 +59,6 @@ namespace MediaBrowser.Model.Entities
|
|||||||
/// <value>The color primaries.</value>
|
/// <value>The color primaries.</value>
|
||||||
public string ColorPrimaries { get; set; }
|
public string ColorPrimaries { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the color space.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The color space.</value>
|
|
||||||
public string ColorSpace { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the comment.
|
/// Gets or sets the comment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
2
debian/rules
vendored
2
debian/rules
vendored
@ -40,7 +40,7 @@ override_dh_clistrip:
|
|||||||
|
|
||||||
override_dh_auto_build:
|
override_dh_auto_build:
|
||||||
dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \
|
dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \
|
||||||
"-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none" Jellyfin.Server
|
"-p:DebugSymbols=false;DebugType=none" Jellyfin.Server
|
||||||
|
|
||||||
override_dh_auto_clean:
|
override_dh_auto_clean:
|
||||||
dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true
|
dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true
|
||||||
|
@ -12,4 +12,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|||||||
|
|
||||||
# because of changes in docker and systemd we need to not build in parallel at the moment
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
@ -12,4 +12,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|||||||
|
|
||||||
# because of changes in docker and systemd we need to not build in parallel at the moment
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
@ -12,4 +12,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|||||||
|
|
||||||
# because of changes in docker and systemd we need to not build in parallel at the moment
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
@ -16,7 +16,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Build archives
|
# Build archives
|
||||||
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
|
||||||
tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_${version}
|
tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_${version}
|
||||||
rm -rf dist/jellyfin-server_${version}
|
rm -rf dist/jellyfin-server_${version}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Build archives
|
# Build archives
|
||||||
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
|
||||||
tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_${version}
|
tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_${version}
|
||||||
rm -rf dist/jellyfin-server_${version}
|
rm -rf dist/jellyfin-server_${version}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Build archives
|
# Build archives
|
||||||
dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
|
||||||
tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version}
|
tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version}
|
||||||
rm -rf dist/jellyfin-server_${version}
|
rm -rf dist/jellyfin-server_${version}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ fi
|
|||||||
output_dir="dist/jellyfin-server_${version}"
|
output_dir="dist/jellyfin-server_${version}"
|
||||||
|
|
||||||
# Build binary
|
# Build binary
|
||||||
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
|
||||||
|
|
||||||
# Prepare addins
|
# Prepare addins
|
||||||
addin_build_dir="$( mktemp -d )"
|
addin_build_dir="$( mktemp -d )"
|
||||||
|
@ -54,7 +54,7 @@ The Jellyfin media server backend.
|
|||||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
|
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
|
||||||
dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \
|
dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \
|
||||||
"-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none" Jellyfin.Server
|
"-p:DebugSymbols=false;DebugType=none" Jellyfin.Server
|
||||||
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE
|
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE
|
||||||
%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf
|
%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf
|
||||||
%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json
|
%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json
|
||||||
|
Loading…
x
Reference in New Issue
Block a user