diff --git a/.ci/azure-pipelines-api-client.yml b/.ci/azure-pipelines-api-client.yml
index fc89b90d4e..de6bbf04ce 100644
--- a/.ci/azure-pipelines-api-client.yml
+++ b/.ci/azure-pipelines-api-client.yml
@@ -28,6 +28,12 @@ jobs:
inputs:
script: "wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/${{ parameters.GeneratorVersion }}/openapi-generator-cli-${{ parameters.GeneratorVersion }}.jar -O openapi-generator-cli.jar"
+## Authenticate with npm registry
+ - task: npmAuthenticate@0
+ inputs:
+ workingFile: ./.npmrc
+ customEndpoint: 'jellyfin-bot for NPM'
+
## Generate npm api client
# Unstable
- task: CmdLine@2
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000000..b7a317000b
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,3 @@
+registry=https://registry.npmjs.org/
+@jellyfin:registry=https://pkgs.dev.azure.com/jellyfin-project/jellyfin/_packaging/unstable/npm/registry/
+always-auth=true
\ No newline at end of file
diff --git a/Emby.Naming/AudioBook/AudioBookResolver.cs b/Emby.Naming/AudioBook/AudioBookResolver.cs
index ed53bd04fa..5807d4688c 100644
--- a/Emby.Naming/AudioBook/AudioBookResolver.cs
+++ b/Emby.Naming/AudioBook/AudioBookResolver.cs
@@ -1,3 +1,4 @@
+#nullable enable
#pragma warning disable CS1591
using System;
@@ -16,21 +17,11 @@ namespace Emby.Naming.AudioBook
_options = options;
}
- public AudioBookFileInfo ParseFile(string path)
+ public AudioBookFileInfo? Resolve(string path, bool isDirectory = false)
{
- return Resolve(path, false);
- }
-
- public AudioBookFileInfo ParseDirectory(string path)
- {
- return Resolve(path, true);
- }
-
- public AudioBookFileInfo Resolve(string path, bool isDirectory = false)
- {
- if (string.IsNullOrEmpty(path))
+ if (path.Length == 0)
{
- throw new ArgumentNullException(nameof(path));
+ throw new ArgumentException("String can't be empty.", nameof(path));
}
// TODO
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 7cb0196837..69f8521d08 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -128,7 +128,6 @@ namespace Emby.Server.Implementations
private IMediaEncoder _mediaEncoder;
private ISessionManager _sessionManager;
private IHttpClientFactory _httpClientFactory;
- private IWebSocketManager _webSocketManager;
private string[] _urlPrefixes;
///
@@ -653,7 +652,6 @@ namespace Emby.Server.Implementations
_mediaEncoder = Resolve();
_sessionManager = Resolve();
_httpClientFactory = Resolve();
- _webSocketManager = Resolve();
((AuthenticationRepository)Resolve()).Initialize();
@@ -788,7 +786,6 @@ namespace Emby.Server.Implementations
}
_urlPrefixes = GetUrlPrefixes().ToArray();
- _webSocketManager.Init(GetExports());
Resolve().AddParts(
GetExports(),
@@ -1068,7 +1065,7 @@ namespace Emby.Server.Implementations
}
else
{
- // Un-versioned folder - Add it under the path name and version 0.0.0.1.
+ // Un-versioned folder - Add it under the path name and version 0.0.0.1.
versions.Add((new Version(0, 0, 0, 1), metafile, dir));
}
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 9ed3cca99c..c762aa0b84 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -32,10 +32,10 @@
-
-
-
-
+
+
+
+
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
index 89c1b7ea08..71ece80a75 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Jellyfin.Data.Events;
@@ -14,16 +13,18 @@ namespace Emby.Server.Implementations.HttpServer
{
public class WebSocketManager : IWebSocketManager
{
+ private readonly Lazy> _webSocketListeners;
private readonly ILogger _logger;
private readonly ILoggerFactory _loggerFactory;
- private IWebSocketListener[] _webSocketListeners = Array.Empty();
private bool _disposed = false;
public WebSocketManager(
+ Lazy> webSocketListeners,
ILogger logger,
ILoggerFactory loggerFactory)
{
+ _webSocketListeners = webSocketListeners;
_logger = logger;
_loggerFactory = loggerFactory;
}
@@ -68,15 +69,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- ///
- /// Adds the rest handlers.
- ///
- /// The web socket listeners.
- public void Init(IEnumerable listeners)
- {
- _webSocketListeners = listeners.ToArray();
- }
-
///
/// Processes the web socket message received.
///
@@ -90,7 +82,8 @@ namespace Emby.Server.Implementations.HttpServer
IEnumerable GetTasks()
{
- foreach (var x in _webSocketListeners)
+ var listeners = _webSocketListeners.Value;
+ foreach (var x in listeners)
{
yield return x.ProcessMessageAsync(result);
}
diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json
new file mode 100644
index 0000000000..df68d3bbd4
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/hi.json
@@ -0,0 +1,3 @@
+{
+ "Albums": "आल्बुम्"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json
index ecca5af4cc..0d909b06e9 100644
--- a/Emby.Server.Implementations/Localization/Core/sq.json
+++ b/Emby.Server.Implementations/Localization/Core/sq.json
@@ -112,5 +112,5 @@
"Artists": "Artistë",
"Application": "Aplikacioni",
"AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
- "Albums": "Albumet"
+ "Albums": "Albume"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 12fda8a986..bea294ba2e 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -9,7 +9,7 @@
"Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlingar",
- "DeviceOfflineWithName": "{0} har kopplat från",
+ "DeviceOfflineWithName": "{0} har kopplat ner",
"DeviceOnlineWithName": "{0} är ansluten",
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
"Favorites": "Favoriter",
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index 6a00db4b1a..da0852cebc 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -14,9 +14,9 @@
-
+
-
+
diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj
index 6bb0d8ce27..5038988f96 100644
--- a/Jellyfin.Data/Jellyfin.Data.csproj
+++ b/Jellyfin.Data/Jellyfin.Data.csproj
@@ -41,8 +41,8 @@
-
-
+
+
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 17ba092588..c52be3b8a9 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -25,11 +25,11 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 8d569a779a..c44736447f 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -4,6 +4,8 @@ using System.IO;
using System.Reflection;
using Emby.Drawing;
using Emby.Server.Implementations;
+using Emby.Server.Implementations.Session;
+using Jellyfin.Api.WebSocketListeners;
using Jellyfin.Drawing.Skia;
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
@@ -14,6 +16,7 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.IO;
using Microsoft.EntityFrameworkCore;
@@ -80,6 +83,14 @@ namespace Jellyfin.Server
ServiceCollection.AddSingleton();
ServiceCollection.AddSingleton();
+ ServiceCollection.AddScoped();
+ ServiceCollection.AddScoped();
+ ServiceCollection.AddScoped();
+ ServiceCollection.AddScoped();
+
+ // TODO fix circular dependency on IWebSocketManager
+ ServiceCollection.AddScoped(serviceProvider => new Lazy>(serviceProvider.GetRequiredService>));
+
base.RegisterServices();
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 761a92f6db..7de34f4160 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -38,10 +38,10 @@
-
-
-
-
+
+
+
+
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index c933d679f4..5573c04395 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -378,7 +378,7 @@ namespace Jellyfin.Server
.ConfigureServices(services =>
{
// Merge the external ServiceCollection into ASP.NET DI
- services.TryAdd(serviceCollection);
+ services.Add(serviceCollection);
})
.UseStartup();
}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 322740cca8..e716a6610f 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -18,8 +18,8 @@
-
-
+
+
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 6544704065..4374317d67 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -14,8 +14,8 @@
-
-
+
+
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index f913c32e43..61340cd242 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -451,11 +451,13 @@ namespace MediaBrowser.Controller.MediaEncoding
var arg = new StringBuilder();
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
+ var isSwDecoder = string.IsNullOrEmpty(videoDecoder);
+ var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
- var isNvencHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
+ var isNvdecHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
@@ -517,11 +519,12 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
- && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
+ && (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder)
+ || (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder))
{
var isColorDepth10 = IsColorDepth10(state);
- if (isNvencHevcDecoder && isColorDepth10
+ if (isColorDepth10
&& _mediaEncoder.SupportsHwaccel("opencl")
&& encodingOptions.EnableTonemapping
&& !string.IsNullOrEmpty(state.VideoStream.VideoRange)
@@ -880,6 +883,19 @@ namespace MediaBrowser.Controller.MediaEncoding
param += "-quality speed";
break;
}
+
+ var videoStream = state.VideoStream;
+ var isColorDepth10 = IsColorDepth10(state);
+
+ if (isColorDepth10
+ && _mediaEncoder.SupportsHwaccel("opencl")
+ && encodingOptions.EnableTonemapping
+ && !string.IsNullOrEmpty(videoStream.VideoRange)
+ && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ {
+ // Enhance workload when tone mapping with AMF on some APUs
+ param += " -preanalysis true";
+ }
}
else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm
{
@@ -1023,19 +1039,19 @@ namespace MediaBrowser.Controller.MediaEncoding
&& !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
}
- if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoEncoder, "h264_amf", 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
+ if (isColorDepth10
&& _mediaEncoder.SupportsHwaccel("opencl")
&& encodingOptions.EnableTonemapping
&& !string.IsNullOrEmpty(videoStream.VideoRange)
@@ -1651,47 +1667,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var outputSizeParam = ReadOnlySpan.Empty;
var request = state.BaseRequest;
- outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"');
-
- // All possible beginning of video filters
- // Don't break the order
- string[] beginOfOutputSizeParam = new[]
- {
- // for tonemap_opencl
- "hwupload,tonemap_opencl",
-
- // hwupload=extra_hw_frames=64,vpp_qsv (for overlay_qsv on linux)
- "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)
- {
- outputSizeParam = outputSizeParam.Slice(index);
- break;
- }
- }
+ outputSizeParam = GetOutputSizeParamInternal(state, options, outputVideoCodec);
var videoSizeParam = string.Empty;
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
@@ -2083,10 +2059,19 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format(CultureInfo.InvariantCulture, filter, widthParam, heightParam);
}
+ public string GetOutputSizeParam(
+ EncodingJobInfo state,
+ EncodingOptions options,
+ string outputVideoCodec)
+ {
+ string filters = GetOutputSizeParamInternal(state, options, outputVideoCodec);
+ return string.IsNullOrEmpty(filters) ? string.Empty : " -vf \"" + filters + "\"";
+ }
+
///
/// If we're going to put a fixed size on the command line, this will calculate it.
///
- public string GetOutputSizeParam(
+ public string GetOutputSizeParamInternal(
EncodingJobInfo state,
EncodingOptions options,
string outputVideoCodec)
@@ -2102,6 +2087,8 @@ namespace MediaBrowser.Controller.MediaEncoding
var inputHeight = videoStream?.Height;
var threeDFormat = state.MediaSource.Video3DFormat;
+ var isSwDecoder = string.IsNullOrEmpty(videoDecoder);
+ var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1;
@@ -2117,47 +2104,77 @@ 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
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 isScalingInAdvance = false;
+ var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
+ var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
+
+ if ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder)
+ || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder))
{
- var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
-
- if (options.TonemappingParam != 0)
+ // Currently only with the use of NVENC decoder can we get a decent performance.
+ // Currently only the HEVC/H265 format is supported with NVDEC decoder.
+ // NVIDIA Pascal and Turing or higher are recommended.
+ // AMD Polaris and Vega or higher are recommended.
+ if (isColorDepth10
+ && _mediaEncoder.SupportsHwaccel("opencl")
+ && options.EnableTonemapping
+ && !string.IsNullOrEmpty(videoStream.VideoRange)
+ && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
{
- parameters += ":param={4}";
- }
+ var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
- if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
- {
- parameters += ":range={5}";
- }
+ if (options.TonemappingParam != 0)
+ {
+ parameters += ":param={4}";
+ }
- // 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 (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
+ {
+ parameters += ":range={5}";
+ }
- if (hasGraphicalSubs || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)
- || string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
- {
- filters.Add("format=nv12");
+ if (isSwDecoder || isD3d11vaDecoder)
+ {
+ isScalingInAdvance = true;
+ // Add zscale filter before tone mapping filter for performance.
+ var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight);
+ if (width.HasValue && height.HasValue)
+ {
+ filters.Add(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "zscale=s={0}x{1}",
+ width.Value,
+ height.Value));
+ }
+
+ // Convert to hardware pixel format p010 when using SW decoder.
+ filters.Add("format=p010");
+ }
+
+ // 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 (isLibX264Encoder
+ || hasGraphicalSubs
+ || (isNvdecHevcDecoder && isDeinterlaceHevc)
+ || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc))
+ {
+ filters.Add("format=nv12");
+ }
}
}
@@ -2202,7 +2219,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Add hardware deinterlace filter before scaling filter
- if (state.DeInterlace("h264", true) || state.DeInterlace("avc", true))
+ if (isDeinterlaceH264)
{
if (isVaapiH264Encoder)
{
@@ -2215,10 +2232,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Add software deinterlace filter before scaling filter
- if ((state.DeInterlace("h264", true)
- || state.DeInterlace("avc", true)
- || state.DeInterlace("h265", true)
- || state.DeInterlace("hevc", true))
+ if ((isDeinterlaceH264 || isDeinterlaceHevc)
&& !isVaapiH264Encoder
&& !isQsvH264Encoder
&& !isNvdecH264Decoder)
@@ -2242,7 +2256,21 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Add scaling filter: scale_*=format=nv12 or scale_*=w=*:h=*:format=nv12 or scale=expr
- filters.AddRange(GetScalingFilters(state, inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight));
+ if (!isScalingInAdvance)
+ {
+ filters.AddRange(
+ GetScalingFilters(
+ state,
+ inputWidth,
+ inputHeight,
+ threeDFormat,
+ videoDecoder,
+ outputVideoCodec,
+ request.Width,
+ request.Height,
+ request.MaxWidth,
+ request.MaxHeight));
+ }
// Add parameters to use VAAPI with burn-in text subtitles (GH issue #642)
if (isVaapiH264Encoder)
@@ -2275,7 +2303,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
output += string.Format(
CultureInfo.InvariantCulture,
- " -vf \"{0}\"",
+ "{0}",
string.Join(",", filters));
}
@@ -3068,21 +3096,31 @@ namespace MediaBrowser.Controller.MediaEncoding
var isWindows8orLater = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1);
var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va");
- if ((isDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
+ if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
{
- if (isLinux)
+ // Currently there is no AMF decoder on Linux, only have h264 encoder.
+ if (isDxvaSupported && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
{
- return "-hwaccel vaapi";
- }
+ if (isWindows && isWindows8orLater)
+ {
+ return "-hwaccel d3d11va";
+ }
- if (isWindows && isWindows8orLater)
- {
- return "-hwaccel d3d11va";
+ if (isWindows && !isWindows8orLater)
+ {
+ return "-hwaccel dxva2";
+ }
}
+ }
- if (isWindows && !isWindows8orLater)
+ if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
+ {
+ if (IsVaapiSupported(state) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
{
- return "-hwaccel dxva2";
+ if (isLinux)
+ {
+ return "-hwaccel vaapi";
+ }
}
}
diff --git a/MediaBrowser.Controller/Net/IWebSocketManager.cs b/MediaBrowser.Controller/Net/IWebSocketManager.cs
index e9f00ae88b..ce74173e70 100644
--- a/MediaBrowser.Controller/Net/IWebSocketManager.cs
+++ b/MediaBrowser.Controller/Net/IWebSocketManager.cs
@@ -16,12 +16,6 @@ namespace MediaBrowser.Controller.Net
///
event EventHandler> WebSocketConnected;
- ///
- /// Inits this instance.
- ///
- /// The websocket listeners.
- void Init(IEnumerable listeners);
-
///
/// The HTTP request handler.
///
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 22537a4d95..cdeefbbbdf 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -666,6 +666,16 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate);
stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate);
+ // Interlaced video streams in Matroska containers return the field rate instead of the frame rate
+ // as both the average and real frame rate, so we half the returned frame rates to get the correct values
+ //
+ // https://gitlab.com/mbunkus/mkvtoolnix/-/wikis/Wrong-frame-rate-displayed
+ if (stream.IsInterlaced && formatInfo.FormatName.Contains("matroska", StringComparison.OrdinalIgnoreCase))
+ {
+ stream.AverageFrameRate /= 2;
+ stream.RealFrameRate /= 2;
+ }
+
if (isAudio || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) ||
string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
{
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 2646810907..253ee7e795 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -34,7 +34,7 @@
-
+
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
index d2f7556a51..53030843ae 100644
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ b/MediaBrowser.Model/System/PublicSystemInfo.cs
@@ -43,7 +43,10 @@ namespace MediaBrowser.Model.System
///
/// Gets or sets a value indicating whether the startup wizard is completed.
///
- /// The startup completion status.
- public bool StartupWizardCompleted { get; set; }
+ ///
+ /// Nullable for OpenAPI specification only to retain backwards compatibility in apiclients.
+ ///
+ /// The startup completion status.]
+ public bool? StartupWizardCompleted { get; set; }
}
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 794490cc52..24400eae53 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -16,9 +16,9 @@
-
-
-
+
+
+
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index abfa1c6e71..31f0123dcf 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -46,6 +46,7 @@ namespace MediaBrowser.Providers.Music
private readonly string _musicBrainzBaseUrl;
+ private SemaphoreSlim _apiRequestLock = new SemaphoreSlim(1, 1);
private Stopwatch _stopWatchMusicBrainz = new Stopwatch();
public MusicBrainzAlbumProvider(
@@ -742,48 +743,58 @@ namespace MediaBrowser.Providers.Music
///
internal async Task GetMusicBrainzResponse(string url, CancellationToken cancellationToken)
{
- using var options = new HttpRequestMessage(HttpMethod.Get, _musicBrainzBaseUrl.TrimEnd('/') + url);
+ await _apiRequestLock.WaitAsync(cancellationToken).ConfigureAwait(false);
- // MusicBrainz request a contact email address is supplied, as comment, in user agent field:
- // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent
- options.Headers.UserAgent.ParseAdd(string.Format(
- CultureInfo.InvariantCulture,
- "{0} ( {1} )",
- _appHost.ApplicationUserAgent,
- _appHost.ApplicationUserAgentAddress));
-
- HttpResponseMessage response;
- var attempts = 0u;
-
- do
+ try
{
- attempts++;
+ HttpResponseMessage response;
+ var attempts = 0u;
+ var requestUrl = _musicBrainzBaseUrl.TrimEnd('/') + url;
- if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs)
+ do
{
- // MusicBrainz is extremely adamant about limiting to one request per second
- var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
- await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false);
+ attempts++;
+
+ if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs)
+ {
+ // MusicBrainz is extremely adamant about limiting to one request per second.
+ var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
+ await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false);
+ }
+
+ // Write time since last request to debug log as evidence we're meeting rate limit
+ // requirement, before resetting stopwatch back to zero.
+ _logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds);
+ _stopWatchMusicBrainz.Restart();
+
+ using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
+
+ // MusicBrainz request a contact email address is supplied, as comment, in user agent field:
+ // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent .
+ request.Headers.UserAgent.ParseAdd(string.Format(
+ CultureInfo.InvariantCulture,
+ "{0} ( {1} )",
+ _appHost.ApplicationUserAgent,
+ _appHost.ApplicationUserAgentAddress));
+
+ response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(request).ConfigureAwait(false);
+
+ // We retry a finite number of times, and only whilst MB is indicating 503 (throttling).
+ }
+ while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable);
+
+ // Log error if unable to query MB database due to throttling.
+ if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable)
+ {
+ _logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, requestUrl);
}
- // Write time since last request to debug log as evidence we're meeting rate limit
- // requirement, before resetting stopwatch back to zero.
- _logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds);
- _stopWatchMusicBrainz.Restart();
-
- response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options).ConfigureAwait(false);
-
- // We retry a finite number of times, and only whilst MB is indicating 503 (throttling)
+ return response;
}
- while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable);
-
- // Log error if unable to query MB database due to throttling
- if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable)
+ finally
{
- _logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.RequestUri);
+ _apiRequestLock.Release();
}
-
- return response;
}
///
diff --git a/debian/postrm b/debian/postrm
index 1d00a984ec..3d56a5f1e8 100644
--- a/debian/postrm
+++ b/debian/postrm
@@ -25,7 +25,7 @@ case "$1" in
purge)
echo PURGE | debconf-communicate $NAME > /dev/null 2>&1 || true
- if [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.connf" ]]; then
+ if [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then
update-rc.d jellyfin remove >/dev/null 2>&1 || true
fi
@@ -54,7 +54,7 @@ case "$1" in
rm -rf $PROGRAMDATA
fi
# Remove binary symlink
- [[ -f /usr/bin/jellyfin ]] && rm /usr/bin/jellyfin
+ rm -f /usr/bin/jellyfin
# Remove sudoers config
[[ -f /etc/sudoers.d/jellyfin-sudoers ]] && rm /etc/sudoers.d/jellyfin-sudoers
# Remove anything at the default locations; catches situations where the user moved the defaults
diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64
index 7202c58838..aaca8fe01e 100644
--- a/deployment/Dockerfile.debian.amd64
+++ b/deployment/Dockerfile.debian.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64
index e9f30213f4..594da04ceb 100644
--- a/deployment/Dockerfile.debian.arm64
+++ b/deployment/Dockerfile.debian.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf
index 91a8a6e7a1..3e6e2d0d70 100644
--- a/deployment/Dockerfile.debian.armhf
+++ b/deployment/Dockerfile.debian.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64
index 828d5c2cf9..f98881ebfe 100644
--- a/deployment/Dockerfile.linux.amd64
+++ b/deployment/Dockerfile.linux.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.macos b/deployment/Dockerfile.macos
index 0b2a0fe5fa..ec9d2d8c77 100644
--- a/deployment/Dockerfile.macos
+++ b/deployment/Dockerfile.macos
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable
index 7d5de230fa..3523f8aceb 100644
--- a/deployment/Dockerfile.portable
+++ b/deployment/Dockerfile.portable
@@ -15,7 +15,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64
index 9c63f43dfa..0a365e1aee 100644
--- a/deployment/Dockerfile.ubuntu.amd64
+++ b/deployment/Dockerfile.ubuntu.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64
index 51612dd443..ab3ec9b9f8 100644
--- a/deployment/Dockerfile.ubuntu.arm64
+++ b/deployment/Dockerfile.ubuntu.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf
index 4ed7f86872..fa41bdf48a 100644
--- a/deployment/Dockerfile.ubuntu.armhf
+++ b/deployment/Dockerfile.ubuntu.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64
index 5671cc598a..7216b2363b 100644
--- a/deployment/Dockerfile.windows.amd64
+++ b/deployment/Dockerfile.windows.amd64
@@ -15,7 +15,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 8a559b7b62..aae436fb73 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -16,8 +16,8 @@
-
-
+
+
diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
index 83d44721c4..673289436d 100644
--- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
+++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Emby.Naming.AudioBook;
using Emby.Naming.Common;
using Xunit;
@@ -42,16 +43,22 @@ namespace Jellyfin.Naming.Tests.AudioBook
[Theory]
[MemberData(nameof(GetResolveFileTestData))]
- public void ResolveFile_ValidFileName_Success(AudioBookFileInfo expectedResult)
+ public void Resolve_ValidFileName_Success(AudioBookFileInfo expectedResult)
{
var result = new AudioBookResolver(_namingOptions).Resolve(expectedResult.Path);
Assert.NotNull(result);
- Assert.Equal(result.Path, expectedResult.Path);
- Assert.Equal(result.Container, expectedResult.Container);
- Assert.Equal(result.ChapterNumber, expectedResult.ChapterNumber);
- Assert.Equal(result.PartNumber, expectedResult.PartNumber);
- Assert.Equal(result.IsDirectory, expectedResult.IsDirectory);
+ Assert.Equal(result!.Path, expectedResult.Path);
+ Assert.Equal(result!.Container, expectedResult.Container);
+ Assert.Equal(result!.ChapterNumber, expectedResult.ChapterNumber);
+ Assert.Equal(result!.PartNumber, expectedResult.PartNumber);
+ Assert.Equal(result!.IsDirectory, expectedResult.IsDirectory);
+ }
+
+ [Fact]
+ public void Resolve_EmptyFileName_ArgumentException()
+ {
+ Assert.Throws(() => new AudioBookResolver(_namingOptions).Resolve(string.Empty));
}
}
}