From 991adc8efe76dbe07faaea1a96de0e746e0f2386 Mon Sep 17 00:00:00 2001
From: Luca Benini
Date: Sat, 13 Feb 2021 15:28:37 +0100
Subject: [PATCH 001/118] Fix BaseItemKind conversion for PlaylistsFolder
Return the correct ClientTypeName to allow Enum Parse
Added dynamic unit tests to ensure all BaseItem concrete descend
---
CONTRIBUTORS.md | 1 +
.../Playlists/ManualPlaylistsFolder.cs | 5 ++
MediaBrowser.sln | 9 ++-
.../BaseItemKindTests.cs | 62 +++++++++++++++++++
.../Jellyfin.Server.Tests.csproj | 48 ++++++++++++++
5 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 tests/Jellyfin.Server.Tests/BaseItemKindTests.cs
create mode 100644 tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 1200275d52..89788b2343 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -207,3 +207,4 @@
- [Tim Hobbs](https://github.com/timhobbs)
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
- [olsh](https://github.com/olsh)
+ - [lbenini] (https://github.com/lbenini)
diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index 358606b0dc..4160f3a500 100644
--- a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -49,5 +49,10 @@ namespace Emby.Server.Implementations.Playlists
query.Parent = null;
return LibraryManager.GetItemsResult(query);
}
+
+ public override string GetClientTypeName()
+ {
+ return "ManualPlaylistsFolder";
+ }
}
}
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index 4e6687cceb..c16e6032e4 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -72,7 +72,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{B26F671A-D5C0-4461-B7C3-324EB167E4B3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -200,6 +202,10 @@ Global
{30922383-D513-4F4D-B890-A940B57FA353}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30922383-D513-4F4D-B890-A940B57FA353}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30922383-D513-4F4D-B890-A940B57FA353}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B26F671A-D5C0-4461-B7C3-324EB167E4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B26F671A-D5C0-4461-B7C3-324EB167E4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B26F671A-D5C0-4461-B7C3-324EB167E4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B26F671A-D5C0-4461-B7C3-324EB167E4B3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -214,6 +220,7 @@ Global
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{30922383-D513-4F4D-B890-A940B57FA353} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
+ {B26F671A-D5C0-4461-B7C3-324EB167E4B3} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
diff --git a/tests/Jellyfin.Server.Tests/BaseItemKindTests.cs b/tests/Jellyfin.Server.Tests/BaseItemKindTests.cs
new file mode 100644
index 0000000000..282760bf97
--- /dev/null
+++ b/tests/Jellyfin.Server.Tests/BaseItemKindTests.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using MediaBrowser.Controller.Entities;
+using Xunit;
+
+namespace Jellyfin.Server.Tests
+{
+ public class BaseItemKindTests
+ {
+ [Theory]
+ [ClassData(typeof(GetBaseItemDescendant))]
+ public void BaseKindEnumTest(Type baseItemDescendantType)
+ {
+ var defaultConstructor = baseItemDescendantType.GetConstructor(Type.EmptyTypes);
+
+ Assert.NotNull(defaultConstructor);
+ if (defaultConstructor != null)
+ {
+ var instance = (BaseItem)defaultConstructor.Invoke(null);
+ var exception = Record.Exception(() => instance.GetBaseItemKind());
+ Assert.Null(exception);
+ }
+ }
+
+ private static bool IsProjectAssemblyName(string? name)
+ {
+ if (name == null)
+ {
+ return false;
+ }
+
+ return name.Contains("Jellyfin", StringComparison.InvariantCulture)
+ || name.Contains("Emby", StringComparison.InvariantCulture)
+ || name.Contains("MediaBrowser", StringComparison.InvariantCulture)
+ || name.Contains("RSSDP", StringComparison.InvariantCulture);
+ }
+
+ private class GetBaseItemDescendant : IEnumerable
---
From aaab6a351876880fe1f240b356de7c319f3bd01b Mon Sep 17 00:00:00 2001
From: nyanmisaka
Date: Sun, 25 Jul 2021 00:52:03 +0800
Subject: [PATCH 013/118] add tests for FFmpeg 4.4 and 4.3.2
---
.../EncoderValidatorTests.cs | 6 ++++-
.../EncoderValidatorTestsData.cs | 24 +++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
index 39fd8afda1..2310f5b244 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
@@ -14,10 +14,12 @@ namespace Jellyfin.MediaEncoding.Tests
public void GetFFmpegVersionTest(string versionOutput, Version? version)
{
var val = new EncoderValidator(new NullLogger());
- Assert.Equal(version, val.GetFFmpegVersion(versionOutput));
+ Assert.Equal(version, val.GetFFmpegVersionInternal(versionOutput));
}
[Theory]
+ [InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)]
+ [InlineData(EncoderValidatorTestsData.FFmpegV432Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)]
@@ -36,6 +38,8 @@ namespace Jellyfin.MediaEncoding.Tests
{
public IEnumerator GetEnumerator()
{
+ yield return new object?[] { EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4) };
+ yield return new object?[] { EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) };
diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
index 9f5bef9a88..02bf046ed1 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
@@ -2,6 +2,30 @@ namespace Jellyfin.MediaEncoding.Tests
{
internal static class EncoderValidatorTestsData
{
+ public const string FFmpegV44Output = @"ffmpeg version 4.4-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers
+built with gcc 10.3.0 (Rev5, Built by MSYS2 project)
+configuration: --disable-static --enable-shared --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls
+libavutil 56. 70.100 / 56. 70.100
+libavcodec 58.134.100 / 58.134.100
+libavformat 58. 76.100 / 58. 76.100
+libavdevice 58. 13.100 / 58. 13.100
+libavfilter 7.110.100 / 7.110.100
+libswscale 5. 9.100 / 5. 9.100
+libswresample 3. 9.100 / 3. 9.100
+libpostproc 55. 9.100 / 55. 9.100";
+
+ public const string FFmpegV432Output = @"ffmpeg version n4.3.2-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers
+built with gcc 10.2.0 (Rev9, Built by MSYS2 project)
+configuration: --disable-static --enable-shared --cc='ccache gcc' --cxx='ccache g++' --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-lto --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls
+libavutil 56. 51.100 / 56. 51.100
+libavcodec 58. 91.100 / 58. 91.100
+libavformat 58. 45.100 / 58. 45.100
+libavdevice 58. 10.100 / 58. 10.100
+libavfilter 7. 85.100 / 7. 85.100
+libswscale 5. 7.100 / 5. 7.100
+libswresample 3. 7.100 / 3. 7.100
+libpostproc 55. 7.100 / 55. 7.100";
+
public const string FFmpegV431Output = @"ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
built with gcc 10.1.0 (GCC)
configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
From 3beda02d925c74c7a7083eaee733537f3396ec92 Mon Sep 17 00:00:00 2001
From: nyanmisaka
Date: Sun, 25 Jul 2021 00:52:16 +0800
Subject: [PATCH 014/118] add support for cuda tonemap and overlay
---
.../MediaEncoding/EncodingHelper.cs | 385 ++++++++++++++----
.../MediaEncoding/FilterOptionType.cs | 23 ++
.../MediaEncoding/IMediaEncoder.cs | 14 +-
.../Encoder/EncoderValidator.cs | 102 ++++-
.../Encoder/MediaEncoder.cs | 39 +-
.../Probing/ProbeResultNormalizer.cs | 17 +
6 files changed, 477 insertions(+), 103 deletions(-)
create mode 100644 MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 257cd5df6d..b12cacb6fa 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -40,6 +40,8 @@ namespace MediaBrowser.Controller.MediaEncoding
"ConstrainedHigh"
};
+ private static readonly Version minVersionForCudaOverlay = new Version(4, 4);
+
public EncodingHelper(
IMediaEncoder mediaEncoder,
ISubtitleEncoder subtitleEncoder)
@@ -109,17 +111,41 @@ namespace MediaBrowser.Controller.MediaEncoding
private bool IsCudaSupported()
{
return _mediaEncoder.SupportsHwaccel("cuda")
- && _mediaEncoder.SupportsFilter("scale_cuda", null)
- && _mediaEncoder.SupportsFilter("yadif_cuda", null);
+ && _mediaEncoder.SupportsFilter("scale_cuda")
+ && _mediaEncoder.SupportsFilter("yadif_cuda")
+ && _mediaEncoder.SupportsFilter("hwupload_cuda");
}
- private bool IsTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
+ private bool IsOpenclTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
{
var videoStream = state.VideoStream;
- return IsColorDepth10(state)
+ if (videoStream == null)
+ {
+ return false;
+ }
+
+ return (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ && IsColorDepth10(state)
&& _mediaEncoder.SupportsHwaccel("opencl")
- && options.EnableTonemapping
- && string.Equals(videoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase);
+ && _mediaEncoder.SupportsFilter("tonemap_opencl")
+ && options.EnableTonemapping;
+ }
+
+ private bool IsCudaTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
+ {
+ var videoStream = state.VideoStream;
+ if (videoStream == null)
+ {
+ return false;
+ }
+
+ return (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ && IsColorDepth10(state)
+ && _mediaEncoder.SupportsHwaccel("cuda")
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapCudaName)
+ && options.EnableTonemapping;
}
private bool IsVppTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
@@ -135,23 +161,25 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
- return IsColorDepth10(state)
+ return string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ && IsColorDepth10(state)
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
&& _mediaEncoder.SupportsHwaccel("vaapi")
- && options.EnableVppTonemapping
- && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase);
+ && _mediaEncoder.SupportsFilter("tonemap_vaapi")
+ && options.EnableVppTonemapping;
}
// Hybrid VPP tonemapping for QSV with VAAPI
if (OperatingSystem.IsLinux() && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
- return IsColorDepth10(state)
+ return string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ && IsColorDepth10(state)
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
&& _mediaEncoder.SupportsHwaccel("vaapi")
+ && _mediaEncoder.SupportsFilter("tonemap_vaapi")
&& _mediaEncoder.SupportsHwaccel("qsv")
- && options.EnableVppTonemapping
- && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase);
+ && options.EnableVppTonemapping;
}
// Native VPP tonemapping may come to QSV in the future.
@@ -489,11 +517,14 @@ namespace MediaBrowser.Controller.MediaEncoding
///
/// Gets the input argument.
///
- public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
+ public string GetInputArgument(EncodingJobInfo state, EncodingOptions options)
{
var arg = new StringBuilder();
- var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
- var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
+ var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
+ var outputVideoCodec = GetVideoEncoder(state, options) ?? string.Empty;
+ var isWindows = OperatingSystem.IsWindows();
+ var isLinux = OperatingSystem.IsLinux();
+ var isMacOS = OperatingSystem.IsMacOS();
var isSwDecoder = string.IsNullOrEmpty(videoDecoder);
var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
@@ -502,26 +533,24 @@ namespace MediaBrowser.Controller.MediaEncoding
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
var isNvdecDecoder = videoDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase);
var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase);
- var isWindows = OperatingSystem.IsWindows();
- var isLinux = OperatingSystem.IsLinux();
- var isMacOS = OperatingSystem.IsMacOS();
- var isTonemappingSupported = IsTonemappingSupported(state, encodingOptions);
- var isVppTonemappingSupported = IsVppTonemappingSupported(state, encodingOptions);
+ var isCuvidVp9Decoder = videoDecoder.Contains("vp9_cuvid", StringComparison.OrdinalIgnoreCase);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
+ var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
if (!IsCopyCodec(outputVideoCodec))
{
if (state.IsVideoRequest
&& _mediaEncoder.SupportsHwaccel("vaapi")
- && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
+ && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
if (isVaapiDecoder)
{
- if (isTonemappingSupported && !isVppTonemappingSupported)
+ if (isOpenclTonemappingSupported && !isVppTonemappingSupported)
{
arg.Append("-init_hw_device vaapi=va:")
- .Append(encodingOptions.VaapiDevice)
- .Append(' ')
- .Append("-init_hw_device opencl=ocl@va ")
+ .Append(options.VaapiDevice)
+ .Append(" -init_hw_device opencl=ocl@va ")
.Append("-hwaccel_device va ")
.Append("-hwaccel_output_format vaapi ")
.Append("-filter_hw_device ocl ");
@@ -530,14 +559,14 @@ namespace MediaBrowser.Controller.MediaEncoding
{
arg.Append("-hwaccel_output_format vaapi ")
.Append("-vaapi_device ")
- .Append(encodingOptions.VaapiDevice)
+ .Append(options.VaapiDevice)
.Append(' ');
}
}
else if (!isVaapiDecoder && isVaapiEncoder)
{
arg.Append("-vaapi_device ")
- .Append(encodingOptions.VaapiDevice)
+ .Append(options.VaapiDevice)
.Append(' ');
}
@@ -545,7 +574,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
- && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+ && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -581,9 +610,8 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (isVaapiDecoder && isVppTonemappingSupported)
{
arg.Append("-init_hw_device vaapi=va:")
- .Append(encodingOptions.VaapiDevice)
- .Append(' ')
- .Append("-init_hw_device qsv@va ")
+ .Append(options.VaapiDevice)
+ .Append(" -init_hw_device qsv@va ")
.Append("-hwaccel_output_format vaapi ");
}
@@ -592,7 +620,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
- && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
+ && string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
&& isNvdecDecoder)
{
// Fix for 'No decoder surfaces left' error. https://trac.ffmpeg.org/ticket/7562
@@ -600,22 +628,31 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
- && ((string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
- && (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder))
- || (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)
- && (isD3d11vaDecoder || isSwDecoder))))
+ && ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
+ && (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder))))
+ {
+ if (!isCudaTonemappingSupported && isOpenclTonemappingSupported)
+ {
+ arg.Append("-init_hw_device opencl=ocl:")
+ .Append(options.OpenclDevice)
+ .Append(" -filter_hw_device ocl ");
+ }
+ }
+
+ if (state.IsVideoRequest
+ && string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)
+ && (isD3d11vaDecoder || isSwDecoder))
{
- if (isTonemappingSupported)
+ if (isOpenclTonemappingSupported)
{
arg.Append("-init_hw_device opencl=ocl:")
- .Append(encodingOptions.OpenclDevice)
- .Append(' ')
- .Append("-filter_hw_device ocl ");
+ .Append(options.OpenclDevice)
+ .Append(" -filter_hw_device ocl ");
}
}
if (state.IsVideoRequest
- && string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ && string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
{
arg.Append("-hwaccel videotoolbox ");
}
@@ -1991,14 +2028,18 @@ namespace MediaBrowser.Controller.MediaEncoding
var isQsvHevcEncoder = outputVideoCodec.Contains("hevc_qsv", StringComparison.OrdinalIgnoreCase);
var isNvdecDecoder = videoDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase);
var isNvencEncoder = outputVideoCodec.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
- var isTonemappingSupported = IsTonemappingSupported(state, options);
- var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
+
+ var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
+ var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
+ var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
// Tonemapping and burn-in graphical subtitles requires overlay_vaapi.
// But it's still in ffmpeg mailing list. Disable it for now.
- if (isTonemappingSupportedOnVaapi && isTonemappingSupported && !isVppTonemappingSupported)
+ if (isTonemappingSupportedOnVaapi && isOpenclTonemappingSupported && !isVppTonemappingSupported)
{
return GetOutputSizeParam(state, options, outputVideoCodec);
}
@@ -2024,13 +2065,22 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(videoSizeParam)
&& !(isTonemappingSupportedOnQsv && isVppTonemappingSupported))
{
- // For QSV, feed it into hardware encoder now
+ // upload graphical subtitle to QSV
if (isLinux && (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)))
{
videoSizeParam += ",hwupload=extra_hw_frames=64";
}
}
+
+ if (!string.IsNullOrEmpty(videoSizeParam))
+ {
+ // upload graphical subtitle to cuda
+ if (isNvdecDecoder && isNvencEncoder && isCudaOverlaySupported && isCudaFormatConversionSupported)
+ {
+ videoSizeParam += ",hwupload_cuda";
+ }
+ }
}
var mapPrefix = state.SubtitleStream.IsExternal ?
@@ -2043,9 +2093,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
// Always put the scaler before the overlay for better performance
- var retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
+ var retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
// When the input may or may not be hardware VAAPI decodable
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
@@ -2056,9 +2106,9 @@ namespace MediaBrowser.Controller.MediaEncoding
[sub]: SW scaling subtitle to FixedOutputSize
[base][sub]: SW overlay
*/
- retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
}
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
@@ -2071,9 +2121,9 @@ namespace MediaBrowser.Controller.MediaEncoding
[sub]: SW scaling subtitle to FixedOutputSize
[base][sub]: SW overlay
*/
- retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
}
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase))
@@ -2090,16 +2140,25 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isLinux)
{
- 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}][sub]overlay_qsv\"";
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"";
}
}
else if (isNvdecDecoder && isNvencEncoder)
{
- retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay,format=nv12|yuv420p,hwupload_cuda\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay,format=nv12|yuv420p,hwupload_cuda\"";
+ if (isCudaOverlaySupported && isCudaFormatConversionSupported)
+ {
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]scale_cuda=format=yuv420p[base];[base][sub]overlay_cuda\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_cuda\"";
+ }
+ else
+ {
+ retStr = !outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay,format=nv12|yuv420p,hwupload_cuda\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay,format=nv12|yuv420p,hwupload_cuda\"";
+ }
}
return string.Format(
@@ -2196,11 +2255,11 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiHevcEncoder = videoEncoder.Contains("hevc_vaapi", StringComparison.OrdinalIgnoreCase);
var isQsvH264Encoder = videoEncoder.Contains("h264_qsv", StringComparison.OrdinalIgnoreCase);
var isQsvHevcEncoder = videoEncoder.Contains("hevc_qsv", StringComparison.OrdinalIgnoreCase);
- var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
- var isP010PixFmtRequired = (isTonemappingSupportedOnVaapi && (isTonemappingSupported || isVppTonemappingSupported))
+ var isP010PixFmtRequired = (isTonemappingSupportedOnVaapi && (isOpenclTonemappingSupported || isVppTonemappingSupported))
|| (isTonemappingSupportedOnQsv && isVppTonemappingSupported);
var outputPixFmt = "format=nv12";
@@ -2251,15 +2310,23 @@ namespace MediaBrowser.Controller.MediaEncoding
var outputWidth = width.Value;
var outputHeight = height.Value;
- var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isNvencEncoder = videoEncoder.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase);
- var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilter("scale_cuda", "Output format (default \"same\")");
+ var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
+ var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
+ var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
+ var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var outputPixFmt = string.Empty;
if (isCudaFormatConversionSupported)
{
- outputPixFmt = "format=nv12";
- if (isTonemappingSupported && isTonemappingSupportedOnNvenc)
+ outputPixFmt = (hasGraphicalSubs && isCudaOverlaySupported && isNvencEncoder)
+ ? "format=yuv420p"
+ : "format=nv12";
+ if ((isOpenclTonemappingSupported || isCudaTonemappingSupported)
+ && isTonemappingSupportedOnNvenc)
{
outputPixFmt = "format=p010";
}
@@ -2525,16 +2592,21 @@ namespace MediaBrowser.Controller.MediaEncoding
var isNvencEncoder = outputVideoCodec.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
var isCuvidH264Decoder = videoDecoder.Contains("h264_cuvid", StringComparison.OrdinalIgnoreCase);
var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase);
+ var isCuvidVp9Decoder = videoDecoder.Contains("vp9_cuvid", StringComparison.OrdinalIgnoreCase);
var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1;
var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1;
var isLinux = OperatingSystem.IsLinux();
var isColorDepth10 = IsColorDepth10(state);
- var isTonemappingSupported = IsTonemappingSupported(state, options);
- var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
- var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder);
+
+ var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder);
var isTonemappingSupportedOnAmf = string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && (isD3d11vaDecoder || isSwDecoder);
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
+ var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
+ var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
+ var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -2546,19 +2618,25 @@ namespace MediaBrowser.Controller.MediaEncoding
var isScalingInAdvance = false;
var isCudaDeintInAdvance = false;
var isHwuploadCudaRequired = false;
+ var isNoTonemapFilterApplied = true;
var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
// Add OpenCL tonemapping filter for NVENC/AMF/VAAPI.
- if (isTonemappingSupportedOnNvenc || isTonemappingSupportedOnAmf || (isTonemappingSupportedOnVaapi && !isVppTonemappingSupported))
+ if ((isTonemappingSupportedOnNvenc && !isCudaTonemappingSupported) || isTonemappingSupportedOnAmf || (isTonemappingSupportedOnVaapi && !isVppTonemappingSupported))
{
- // 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.
// Intel Kaby Lake or newer is required.
- if (isTonemappingSupported)
+ if (isOpenclTonemappingSupported)
{
+ isNoTonemapFilterApplied = false;
+ var inputHdrParams = GetInputHdrParams(videoStream.ColorTransfer);
+ if (!string.IsNullOrEmpty(inputHdrParams))
+ {
+ filters.Add(inputHdrParams);
+ }
+
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
if (options.TonemappingParam != 0)
@@ -2630,7 +2708,11 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwdownload,format=p010");
}
- if (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder || isD3d11vaDecoder)
+ if (isNvdecDecoder
+ || isCuvidHevcDecoder
+ || isCuvidVp9Decoder
+ || isSwDecoder
+ || isD3d11vaDecoder)
{
// Upload the HDR10 or HLG data to the OpenCL device,
// use tonemap_opencl filter for tone mapping,
@@ -2638,6 +2720,14 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwupload");
}
+ // Fallback to hable if bt2390 is chosen but not supported in tonemap_opencl.
+ var isBt2390SupportedInOpenclTonemap = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapOpenclBt2390);
+ if (string.Equals(options.TonemappingAlgorithm, "bt2390", StringComparison.OrdinalIgnoreCase)
+ && !isBt2390SupportedInOpenclTonemap)
+ {
+ options.TonemappingAlgorithm = "hable";
+ }
+
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
@@ -2649,7 +2739,11 @@ namespace MediaBrowser.Controller.MediaEncoding
options.TonemappingParam,
options.TonemappingRange));
- if (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder || isD3d11vaDecoder)
+ if (isNvdecDecoder
+ || isCuvidHevcDecoder
+ || isCuvidVp9Decoder
+ || isSwDecoder
+ || isD3d11vaDecoder)
{
filters.Add("hwdownload");
filters.Add("format=nv12");
@@ -2665,12 +2759,18 @@ namespace MediaBrowser.Controller.MediaEncoding
// Reverse the data route from opencl to vaapi.
filters.Add("hwmap=derive_device=vaapi:reverse=1");
}
+
+ var outputSdrParams = GetOutputSdrParams(options.TonemappingRange);
+ if (!string.IsNullOrEmpty(outputSdrParams))
+ {
+ filters.Add(outputSdrParams);
+ }
}
}
// When the input may or may not be hardware VAAPI decodable.
if ((isVaapiH264Encoder || isVaapiHevcEncoder)
- && !(isTonemappingSupportedOnVaapi && (isTonemappingSupported || isVppTonemappingSupported)))
+ && !(isTonemappingSupportedOnVaapi && (isOpenclTonemappingSupported || isVppTonemappingSupported)))
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
@@ -2778,6 +2878,61 @@ namespace MediaBrowser.Controller.MediaEncoding
request.MaxHeight));
}
+ // Add Cuda tonemapping filter.
+ if (isNvdecDecoder && isCudaTonemappingSupported)
+ {
+ isNoTonemapFilterApplied = false;
+ var inputHdrParams = GetInputHdrParams(videoStream.ColorTransfer);
+ if (!string.IsNullOrEmpty(inputHdrParams))
+ {
+ filters.Add(inputHdrParams);
+ }
+
+ var parameters = (hasGraphicalSubs && isCudaOverlaySupported && isNvencEncoder)
+ ? "tonemap_cuda=format=yuv420p:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:peak={1}:desat={2}"
+ : "tonemap_cuda=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:peak={1}:desat={2}";
+
+ if (options.TonemappingParam != 0)
+ {
+ parameters += ":param={3}";
+ }
+
+ if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
+ {
+ parameters += ":range={4}";
+ }
+
+ filters.Add(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ parameters,
+ options.TonemappingAlgorithm,
+ options.TonemappingPeak,
+ options.TonemappingDesat,
+ options.TonemappingParam,
+ options.TonemappingRange));
+
+ if (isLibX264Encoder
+ || isLibX265Encoder
+ || hasTextSubs
+ || (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder))
+ {
+ if (isNvencEncoder)
+ {
+ isHwuploadCudaRequired = true;
+ }
+
+ filters.Add("hwdownload");
+ filters.Add("format=nv12");
+ }
+
+ var outputSdrParams = GetOutputSdrParams(options.TonemappingRange);
+ if (!string.IsNullOrEmpty(outputSdrParams))
+ {
+ filters.Add(outputSdrParams);
+ }
+ }
+
// Add VPP tonemapping filter for VAAPI.
// Full hardware based video post processing, faster than OpenCL but lacks fine tuning options.
if ((isTonemappingSupportedOnVaapi || isTonemappingSupportedOnQsv)
@@ -2787,10 +2942,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Another case is when using Nvenc decoder.
- if (isNvdecDecoder && !isTonemappingSupported)
+ if (isNvdecDecoder && !isOpenclTonemappingSupported && !isCudaTonemappingSupported)
{
var codec = videoStream.Codec;
- var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilter("scale_cuda", "Output format (default \"same\")");
+ var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
// Assert 10-bit hardware decodable
if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
@@ -2799,7 +2954,10 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isCudaFormatConversionSupported)
{
- if (isLibX264Encoder || isLibX265Encoder || hasSubs)
+ if (isLibX264Encoder
+ || isLibX265Encoder
+ || hasTextSubs
+ || (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder))
{
if (isNvencEncoder)
{
@@ -2826,7 +2984,11 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Assert 8-bit hardware decodable
- else if (!isColorDepth10 && (isLibX264Encoder || isLibX265Encoder || hasSubs))
+ else if (!isColorDepth10
+ && (isLibX264Encoder
+ || isLibX265Encoder
+ || hasTextSubs
+ || (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder)))
{
if (isNvencEncoder)
{
@@ -2847,7 +3009,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// Convert hw context from ocl to va.
// For tonemapping and text subs burn-in.
- if (isTonemappingSupportedOnVaapi && isTonemappingSupported && !isVppTonemappingSupported)
+ if (isTonemappingSupportedOnVaapi && isOpenclTonemappingSupported && !isVppTonemappingSupported)
{
filters.Add("scale_vaapi");
}
@@ -2893,6 +3055,17 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwupload_cuda");
}
+ // If no tonemap filter is applied,
+ // tag the video range as SDR to prevent the encoder from encoding HDR video.
+ if (isNoTonemapFilterApplied)
+ {
+ var outputSdrParams = GetOutputSdrParams(null);
+ if (!string.IsNullOrEmpty(outputSdrParams))
+ {
+ filters.Add(outputSdrParams);
+ }
+ }
+
var output = string.Empty;
if (filters.Count > 0)
{
@@ -2905,6 +3078,36 @@ namespace MediaBrowser.Controller.MediaEncoding
return output;
}
+ public static string GetInputHdrParams(string colorTransfer)
+ {
+ if (string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ {
+ // HLG
+ return "setparams=color_primaries=bt2020:color_trc=arib-std-b67:colorspace=bt2020nc";
+ }
+ else
+ {
+ // HDR10
+ return "setparams=color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc";
+ }
+ }
+
+ public static string GetOutputSdrParams(string tonemappingRange)
+ {
+ // SDR
+ if (string.Equals(tonemappingRange, "tv", StringComparison.OrdinalIgnoreCase))
+ {
+ return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=tv";
+ }
+
+ if (string.Equals(tonemappingRange, "pc", StringComparison.OrdinalIgnoreCase))
+ {
+ return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=pc";
+ }
+
+ return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709";
+ }
+
///
/// Gets the number of threads.
///
@@ -3371,8 +3574,13 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)
&& IsVppTonemappingSupported(state, encodingOptions))
{
- // Since tonemap_vaapi only support HEVC for now, no need to check the codec again.
- return GetHwaccelType(state, encodingOptions, "hevc", isColorDepth10);
+ var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
+ var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
+ if (isQsvEncoder)
+ {
+ // Since tonemap_vaapi only support HEVC for now, no need to check the codec again.
+ return GetHwaccelType(state, encodingOptions, "hevc", isColorDepth10);
+ }
}
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
@@ -3895,6 +4103,11 @@ namespace MediaBrowser.Controller.MediaEncoding
if (videoStream != null)
{
+ if (videoStream.BitDepth.HasValue)
+ {
+ return videoStream.BitDepth.Value == 10;
+ }
+
if (!string.IsNullOrEmpty(videoStream.PixelFormat))
{
result = videoStream.PixelFormat.Contains("p10", StringComparison.OrdinalIgnoreCase);
@@ -3914,12 +4127,6 @@ namespace MediaBrowser.Controller.MediaEncoding
return true;
}
}
-
- result = (videoStream.BitDepth ?? 8) == 10;
- if (result)
- {
- return true;
- }
}
return result;
diff --git a/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
new file mode 100644
index 0000000000..7ce707b19e
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
@@ -0,0 +1,23 @@
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ ///
+ /// Enum FilterOptionType.
+ ///
+ public enum FilterOptionType
+ {
+ ///
+ /// The scale_cuda_format.
+ ///
+ ScaleCudaFormat = 0,
+
+ ///
+ /// The tonemap_cuda_name.
+ ///
+ TonemapCudaName = 1,
+
+ ///
+ /// The tonemap_opencl_bt2390.
+ ///
+ TonemapOpenclBt2390 = 2
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 76a9fd7c74..31913acefd 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -55,9 +55,21 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Whether given filter is supported.
///
/// The filter.
+ /// true if the filter is supported, false otherwise.
+ bool SupportsFilter(string filter);
+
+ ///
+ /// Whether filter is supported with the given option.
+ ///
/// The option.
/// true if the filter is supported, false otherwise.
- bool SupportsFilter(string filter, string option);
+ bool SupportsFilterWithOption(FilterOptionType option);
+
+ ///
+ /// Get the version of media encoder.
+ ///
+ /// The version of media encoder.
+ Version GetMediaEncoderVersion();
///
/// Extracts the audio image.
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index f782e65bd1..1ec159c9a4 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -89,6 +89,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
"hevc_videotoolbox"
};
+ private static readonly string[] _requiredFilters = new[]
+ {
+ "scale_cuda",
+ "yadif_cuda",
+ "hwupload_cuda",
+ "overlay_cuda",
+ "tonemap_cuda",
+ "tonemap_opencl",
+ "tonemap_vaapi",
+ };
+
+ private static readonly IReadOnlyDictionary _filterOptionsDict = new Dictionary
+ {
+ { 0, new string[] { "scale_cuda", "Output format (default \"same\")" } },
+ { 1, new string[] { "tonemap_cuda", "GPU accelerated HDR to SDR tonemapping" } },
+ { 2, new string[] { "tonemap_opencl", "bt2390" } }
+ };
+
// These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below
private static readonly IReadOnlyDictionary _ffmpegMinimumLibraryVersions = new Dictionary
{
@@ -156,7 +174,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
// Work out what the version under test is
- var version = GetFFmpegVersion(versionOutput);
+ var version = GetFFmpegVersionInternal(versionOutput);
_logger.LogInformation("Found ffmpeg version {Version}", version != null ? version.ToString() : "unknown");
@@ -200,6 +218,34 @@ namespace MediaBrowser.MediaEncoding.Encoder
public IEnumerable GetHwaccels() => GetHwaccelTypes();
+ public IEnumerable GetFilters() => GetFFmpegFilters();
+
+ public IDictionary GetFiltersWithOption() => GetFFmpegFiltersWithOption();
+
+ public Version? GetFFmpegVersion()
+ {
+ string output;
+ try
+ {
+ output = GetProcessOutput(_encoderPath, "-version");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error validating encoder");
+ return null;
+ }
+
+ if (string.IsNullOrWhiteSpace(output))
+ {
+ _logger.LogError("FFmpeg validation: The process returned no result");
+ return null;
+ }
+
+ _logger.LogDebug("ffmpeg output: {Output}", output);
+
+ return GetFFmpegVersionInternal(output);
+ }
+
///
/// Using the output from "ffmpeg -version" work out the FFmpeg version.
/// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
@@ -208,7 +254,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
///
/// The output from "ffmpeg -version".
/// The FFmpeg version.
- internal Version? GetFFmpegVersion(string output)
+ internal Version? GetFFmpegVersionInternal(string output)
{
// For pre-built binaries the FFmpeg version should be mentioned at the very start of the output
var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)");
@@ -297,9 +343,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
return found;
}
- public bool CheckFilter(string filter, string option)
+ public bool CheckFilterWithOption(string filter, string option)
{
- if (string.IsNullOrEmpty(filter))
+ if (string.IsNullOrEmpty(filter) || string.IsNullOrEmpty(option))
{
return false;
}
@@ -317,11 +363,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (output.Contains("Filter " + filter, StringComparison.Ordinal))
{
- if (string.IsNullOrEmpty(option))
- {
- return true;
- }
-
return output.Contains(option, StringComparison.Ordinal);
}
@@ -362,6 +403,49 @@ namespace MediaBrowser.MediaEncoding.Encoder
return found;
}
+ private IEnumerable GetFFmpegFilters()
+ {
+ string output;
+ try
+ {
+ output = GetProcessOutput(_encoderPath, "-filters");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error detecting available filters");
+ return Enumerable.Empty();
+ }
+
+ if (string.IsNullOrWhiteSpace(output))
+ {
+ return Enumerable.Empty();
+ }
+
+ var found = Regex
+ .Matches(output, @"^\s\S{3}\s(?[\w|-]+)\s+.+$", RegexOptions.Multiline)
+ .Cast()
+ .Select(x => x.Groups["filter"].Value)
+ .Where(x => _requiredFilters.Contains(x));
+
+ _logger.LogInformation("Available filters: {Filters}", found);
+
+ return found;
+ }
+
+ private IDictionary GetFFmpegFiltersWithOption()
+ {
+ IDictionary dict = new Dictionary();
+ for (int i = 0; i < _filterOptionsDict.Count; i++)
+ {
+ if (_filterOptionsDict.TryGetValue(i, out var val) && val.Length == 2)
+ {
+ dict.Add(i, CheckFilterWithOption(val[0], val[1]));
+ }
+ }
+
+ return dict;
+ }
+
private string GetProcessOutput(string path, string arguments)
{
using (var process = new Process()
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 412a953216..238627e96e 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -66,7 +66,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
private List _encoders = new List();
private List _decoders = new List();
private List _hwaccels = new List();
+ private List _filters = new List();
+ private IDictionary _filtersWithOption = new Dictionary();
+ private Version _ffmpegVersion = null;
private string _ffmpegPath = string.Empty;
private string _ffprobePath;
private int threads;
@@ -130,7 +133,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
SetAvailableDecoders(validator.GetDecoders());
SetAvailableEncoders(validator.GetEncoders());
+ SetAvailableFilters(validator.GetFilters());
+ SetAvailableFiltersWithOption(validator.GetFiltersWithOption());
SetAvailableHwaccels(validator.GetHwaccels());
+ SetMediaEncoderVersion(validator);
+
threads = EncodingHelper.GetNumberOfThreads(null, _configurationManager.GetEncodingOptions(), null);
}
@@ -278,6 +285,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
_hwaccels = list.ToList();
}
+ public void SetAvailableFilters(IEnumerable list)
+ {
+ _filters = list.ToList();
+ }
+
+ public void SetAvailableFiltersWithOption(IDictionary dict)
+ {
+ _filtersWithOption = dict;
+ }
+
+ public void SetMediaEncoderVersion(EncoderValidator validator)
+ {
+ _ffmpegVersion = validator.GetFFmpegVersion();
+ }
+
public bool SupportsEncoder(string encoder)
{
return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
@@ -293,17 +315,26 @@ namespace MediaBrowser.MediaEncoding.Encoder
return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
}
- public bool SupportsFilter(string filter, string option)
+ public bool SupportsFilter(string filter)
{
- if (_ffmpegPath != null)
+ return _filters.Contains(filter, StringComparer.OrdinalIgnoreCase);
+ }
+
+ public bool SupportsFilterWithOption(FilterOptionType option)
+ {
+ if (_filtersWithOption.TryGetValue((int)option, out var val))
{
- var validator = new EncoderValidator(_logger, _ffmpegPath);
- return validator.CheckFilter(filter, option);
+ return val;
}
return false;
}
+ public Version GetMediaEncoderVersion()
+ {
+ return _ffmpegVersion;
+ }
+
public bool CanEncodeToAudioCodec(string codec)
{
if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index c9ad3c41eb..d6799a170f 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -739,6 +739,23 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.BitDepth = streamInfo.BitsPerRawSample;
}
+ if (!stream.BitDepth.HasValue)
+ {
+ if (!string.IsNullOrEmpty(streamInfo.PixelFormat)
+ && streamInfo.PixelFormat.Contains("p10", StringComparison.OrdinalIgnoreCase))
+ {
+ stream.BitDepth = 10;
+ }
+
+ if (!string.IsNullOrEmpty(streamInfo.Profile)
+ && (streamInfo.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase)
+ || streamInfo.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase)
+ || streamInfo.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase)))
+ {
+ stream.BitDepth = 10;
+ }
+ }
+
// stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
// string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) ||
// string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase);
From 19e3c38fa8766e1dccb21d80224ae87cecf42df4 Mon Sep 17 00:00:00 2001
From: nyanmisaka
Date: Mon, 26 Jul 2021 03:09:44 +0800
Subject: [PATCH 015/118] Apply suggestions from code review
Co-authored-by: Claus Vium
---
.../MediaEncoding/EncodingHelper.cs | 26 +++++++++----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index b12cacb6fa..249c6f46e1 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -124,12 +124,12 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- return (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ return options.EnableTonemapping
+ && (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
&& IsColorDepth10(state)
&& _mediaEncoder.SupportsHwaccel("opencl")
- && _mediaEncoder.SupportsFilter("tonemap_opencl")
- && options.EnableTonemapping;
+ && _mediaEncoder.SupportsFilter("tonemap_opencl");
}
private bool IsCudaTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
@@ -140,12 +140,12 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- return (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ return options.EnableTonemapping
+ && (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
&& IsColorDepth10(state)
&& _mediaEncoder.SupportsHwaccel("cuda")
- && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapCudaName)
- && options.EnableTonemapping;
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapCudaName);
}
private bool IsVppTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
@@ -161,25 +161,25 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
- return string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ return options.EnableVppTonemapping
+ && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
&& IsColorDepth10(state)
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
&& _mediaEncoder.SupportsHwaccel("vaapi")
- && _mediaEncoder.SupportsFilter("tonemap_vaapi")
- && options.EnableVppTonemapping;
+ && _mediaEncoder.SupportsFilter("tonemap_vaapi");
}
// Hybrid VPP tonemapping for QSV with VAAPI
if (OperatingSystem.IsLinux() && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
- return string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ return options.EnableVppTonemapping
+ && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
&& IsColorDepth10(state)
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
&& _mediaEncoder.SupportsHwaccel("vaapi")
&& _mediaEncoder.SupportsFilter("tonemap_vaapi")
- && _mediaEncoder.SupportsHwaccel("qsv")
- && options.EnableVppTonemapping;
+ && _mediaEncoder.SupportsHwaccel("qsv");
}
// Native VPP tonemapping may come to QSV in the future.
@@ -2155,7 +2155,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else
{
- retStr = !outputSizeParam.IsEmpty
+ retStr = outputSizeParam.IsEmpty
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay,format=nv12|yuv420p,hwupload_cuda\""
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay,format=nv12|yuv420p,hwupload_cuda\"";
}
From 4e9fbabef25a73a3bfed255f2cb586817ec83bb2 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Tue, 3 Aug 2021 17:54:55 +0200
Subject: [PATCH 016/118] Enable nullable for DlnaManager
---
Emby.Dlna/DlnaManager.cs | 68 +++++++++++---------
MediaBrowser.Controller/Dlna/IDlnaManager.cs | 8 +--
2 files changed, 42 insertions(+), 34 deletions(-)
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index b08f7590d4..af70793ccf 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -1,7 +1,4 @@
-#nullable disable
-
#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -96,12 +93,14 @@ namespace Emby.Dlna
}
}
+ ///
public DeviceProfile GetDefaultProfile()
{
return new DefaultProfile();
}
- public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
+ ///
+ public DeviceProfile? GetProfile(DeviceIdentification deviceInfo)
{
if (deviceInfo == null)
{
@@ -111,13 +110,13 @@ namespace Emby.Dlna
var profile = GetProfiles()
.FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification));
- if (profile != null)
+ if (profile == null)
{
- _logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
+ LogUnmatchedProfile(deviceInfo);
}
else
{
- LogUnmatchedProfile(deviceInfo);
+ _logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
}
return profile;
@@ -187,7 +186,8 @@ namespace Emby.Dlna
}
}
- public DeviceProfile GetProfile(IHeaderDictionary headers)
+ ///
+ public DeviceProfile? GetProfile(IHeaderDictionary headers)
{
if (headers == null)
{
@@ -195,15 +195,13 @@ namespace Emby.Dlna
}
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
-
- if (profile != null)
+ if (profile == null)
{
- _logger.LogDebug("Found matching device profile: {0}", profile.Name);
+ _logger.LogDebug("No matching device profile found. {@Headers}", headers);
}
else
{
- var headerString = string.Join(", ", headers.Select(i => string.Format(CultureInfo.InvariantCulture, "{0}={1}", i.Key, i.Value)));
- _logger.LogDebug("No matching device profile found. {0}", headerString);
+ _logger.LogDebug("Found matching device profile: {0}", profile.Name);
}
return profile;
@@ -253,19 +251,19 @@ namespace Emby.Dlna
return xmlFies
.Select(i => ParseProfileFile(i, type))
.Where(i => i != null)
- .ToList();
+ .ToList()!; // We just filtered out all the nulls
}
catch (IOException)
{
- return new List();
+ return Array.Empty();
}
}
- private DeviceProfile ParseProfileFile(string path, DeviceProfileType type)
+ private DeviceProfile? ParseProfileFile(string path, DeviceProfileType type)
{
lock (_profiles)
{
- if (_profiles.TryGetValue(path, out Tuple profileTuple))
+ if (_profiles.TryGetValue(path, out Tuple? profileTuple))
{
return profileTuple.Item2;
}
@@ -293,7 +291,8 @@ namespace Emby.Dlna
}
}
- public DeviceProfile GetProfile(string id)
+ ///
+ public DeviceProfile? GetProfile(string id)
{
if (string.IsNullOrEmpty(id))
{
@@ -322,6 +321,7 @@ namespace Emby.Dlna
}
}
+ ///
public IEnumerable GetProfileInfos()
{
return GetProfileInfosInternal().Select(i => i.Info);
@@ -329,17 +329,14 @@ namespace Emby.Dlna
private InternalProfileInfo GetInternalProfileInfo(FileSystemMetadata file, DeviceProfileType type)
{
- return new InternalProfileInfo
- {
- Path = file.FullName,
-
- Info = new DeviceProfileInfo
+ return new InternalProfileInfo(
+ new DeviceProfileInfo
{
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
Name = _fileSystem.GetFileNameWithoutExtension(file),
Type = type
- }
- };
+ },
+ file.FullName);
}
private async Task ExtractSystemProfilesAsync()
@@ -359,7 +356,8 @@ namespace Emby.Dlna
systemProfilesPath,
Path.GetFileName(name.AsSpan()).Slice(namespaceName.Length));
- using (var stream = _assembly.GetManifestResourceStream(name))
+ // The stream should exist as we just got its name from GetManifestResourceNames
+ using (var stream = _assembly.GetManifestResourceStream(name)!)
{
var fileInfo = _fileSystem.GetFileInfo(path);
@@ -380,6 +378,7 @@ namespace Emby.Dlna
Directory.CreateDirectory(UserProfilesPath);
}
+ ///
public void DeleteProfile(string id)
{
var info = GetProfileInfosInternal().First(i => string.Equals(id, i.Info.Id, StringComparison.OrdinalIgnoreCase));
@@ -397,6 +396,7 @@ namespace Emby.Dlna
}
}
+ ///
public void CreateProfile(DeviceProfile profile)
{
profile = ReserializeProfile(profile);
@@ -412,6 +412,7 @@ namespace Emby.Dlna
SaveProfile(profile, path, DeviceProfileType.User);
}
+ ///
public void UpdateProfile(DeviceProfile profile)
{
profile = ReserializeProfile(profile);
@@ -470,9 +471,11 @@ namespace Emby.Dlna
var json = JsonSerializer.Serialize(profile, _jsonOptions);
- return JsonSerializer.Deserialize(json, _jsonOptions);
+ // Output can't be null if the input isn't null
+ return JsonSerializer.Deserialize(json, _jsonOptions)!;
}
+ ///
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
{
var profile = GetDefaultProfile();
@@ -482,6 +485,7 @@ namespace Emby.Dlna
return new DescriptionXmlBuilder(profile, serverUuId, serverAddress, _appHost.FriendlyName, serverId).GetXml();
}
+ ///
public ImageStream GetIcon(string filename)
{
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
@@ -499,9 +503,15 @@ namespace Emby.Dlna
private class InternalProfileInfo
{
- internal DeviceProfileInfo Info { get; set; }
+ internal InternalProfileInfo(DeviceProfileInfo info, string path)
+ {
+ Info = info;
+ Path = path;
+ }
+
+ internal DeviceProfileInfo Info { get; }
- internal string Path { get; set; }
+ internal string Path { get; }
}
}
diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
index b51dc255ce..a64919700d 100644
--- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs
+++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System.Collections.Generic;
@@ -22,7 +20,7 @@ namespace MediaBrowser.Controller.Dlna
///
/// The headers.
/// DeviceProfile.
- DeviceProfile GetProfile(IHeaderDictionary headers);
+ DeviceProfile? GetProfile(IHeaderDictionary headers);
///
/// Gets the default profile.
@@ -53,14 +51,14 @@ namespace MediaBrowser.Controller.Dlna
///
/// The identifier.
/// DeviceProfile.
- DeviceProfile GetProfile(string id);
+ DeviceProfile? GetProfile(string id);
///
/// Gets the profile.
///
/// The device information.
/// DeviceProfile.
- DeviceProfile GetProfile(DeviceIdentification deviceInfo);
+ DeviceProfile? GetProfile(DeviceIdentification deviceInfo);
///
/// Gets the server description XML.
From d4f09c6c9b142081064c4008bc1e84fb17c81ad8 Mon Sep 17 00:00:00 2001
From: Nyanmisaka
Date: Wed, 4 Aug 2021 16:25:17 +0800
Subject: [PATCH 017/118] Apply suggestions from code review
Co-authored-by: Claus Vium
---
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 249c6f46e1..3f90de3186 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -3575,7 +3575,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& IsVppTonemappingSupported(state, encodingOptions))
{
var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
- var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
+ var isQsvEncoder = outputVideoCodec.Contains("qsv", StringComparison.OrdinalIgnoreCase);
if (isQsvEncoder)
{
// Since tonemap_vaapi only support HEVC for now, no need to check the codec again.
From d212b6fb08fc8d45008499f3dfce33f59bb425e3 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Wed, 4 Aug 2021 06:24:25 -0600
Subject: [PATCH 018/118] Clean test
---
.../BaseItem/BaseItemKindTests.cs | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/BaseItem/BaseItemKindTests.cs b/tests/Jellyfin.Server.Implementations.Tests/BaseItem/BaseItemKindTests.cs
index f3e7e208ac..061e81f30a 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/BaseItem/BaseItemKindTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/BaseItem/BaseItemKindTests.cs
@@ -25,14 +25,9 @@ namespace Jellyfin.Server.Implementations.Tests.BaseItem
public void GetBaseKindEnumTest(Type baseItemDescendantType)
{
var defaultConstructor = baseItemDescendantType.GetConstructor(Type.EmptyTypes);
-
- Assert.NotNull(defaultConstructor);
- if (defaultConstructor != null)
- {
- var instance = (MediaBrowser.Controller.Entities.BaseItem)defaultConstructor.Invoke(null);
- var exception = Record.Exception(() => instance.GetBaseItemKind());
- Assert.Null(exception);
- }
+ var instance = (MediaBrowser.Controller.Entities.BaseItem)defaultConstructor!.Invoke(null);
+ var exception = Record.Exception(() => instance.GetBaseItemKind());
+ Assert.Null(exception);
}
private class GetBaseItemDescendants : IEnumerable
From 442dc10aaccf153873fd711dc80ee55052fc2063 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 4 Aug 2021 18:43:26 +0200
Subject: [PATCH 019/118] ApiServiceCollectionExtensions.AddProxyAddresses: Add
more tests
---
MediaBrowser.Common/Net/IPHost.cs | 42 ++++-----
.../ParseNetworkTests.cs | 94 ++++++++++++++-----
2 files changed, 89 insertions(+), 47 deletions(-)
diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs
index d78d7def2b..bdadcbf952 100644
--- a/MediaBrowser.Common/Net/IPHost.cs
+++ b/MediaBrowser.Common/Net/IPHost.cs
@@ -4,7 +4,6 @@ using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
-using System.Threading.Tasks;
namespace MediaBrowser.Common.Net
{
@@ -400,7 +399,7 @@ namespace MediaBrowser.Common.Net
if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout)))
{
_lastResolved = DateTime.UtcNow;
- ResolveHostInternal().GetAwaiter().GetResult();
+ ResolveHostInternal();
Resolved = true;
}
@@ -410,30 +409,31 @@ namespace MediaBrowser.Common.Net
///
/// Task that looks up a Host name and returns its IP addresses.
///
- /// A representing the asynchronous operation.
- private async Task ResolveHostInternal()
+ private void ResolveHostInternal()
{
- if (!string.IsNullOrEmpty(HostName))
+ var hostName = HostName;
+ if (string.IsNullOrEmpty(hostName))
{
- // Resolves the host name - so save a DNS lookup.
- if (string.Equals(HostName, "localhost", StringComparison.OrdinalIgnoreCase))
+ return;
+ }
+
+ // Resolves the host name - so save a DNS lookup.
+ if (string.Equals(hostName, "localhost", StringComparison.OrdinalIgnoreCase))
+ {
+ _addresses = new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback };
+ return;
+ }
+
+ if (Uri.CheckHostName(hostName) == UriHostNameType.Dns)
+ {
+ try
{
- _addresses = new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback };
- return;
+ _addresses = Dns.GetHostAddresses(hostName);
}
-
- if (Uri.CheckHostName(HostName).Equals(UriHostNameType.Dns))
+ catch (SocketException ex)
{
- try
- {
- IPHostEntry ip = await Dns.GetHostEntryAsync(HostName).ConfigureAwait(false);
- _addresses = ip.AddressList;
- }
- catch (SocketException ex)
- {
- // Log and then ignore socket errors, as the result value will just be an empty array.
- Debug.WriteLine("GetHostEntryAsync failed with {Message}.", ex.Message);
- }
+ // Log and then ignore socket errors, as the result value will just be an empty array.
+ Debug.WriteLine("GetHostEntryAsync failed with {Message}.", ex.Message);
}
}
}
diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
index 146b16cf94..b92cb165c9 100644
--- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
+++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
@@ -1,10 +1,15 @@
+using System;
+using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
+using System.Net;
using System.Text;
using Jellyfin.Networking.Configuration;
using Jellyfin.Networking.Manager;
using Jellyfin.Server.Extensions;
using MediaBrowser.Common.Configuration;
using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
@@ -13,20 +18,63 @@ namespace Jellyfin.Server.Tests
{
public class ParseNetworkTests
{
- ///
- /// Order of the result has always got to be hosts, then networks.
- ///
- /// IP4 enabled.
- /// IP6 enabled.
- /// List to parse.
- /// What it should match.
+ public static TheoryData TestNetworks_TestData()
+ {
+ var data = new TheoryData();
+ data.Add(
+ true,
+ true,
+ new string[] { "192.168.t", "127.0.0.1", "1234.1232.12.1234" },
+ new IPAddress[] { IPAddress.Loopback.MapToIPv6() },
+ Array.Empty());
+
+ data.Add(
+ true,
+ false,
+ new string[] { "192.168.x", "127.0.0.1", "1234.1232.12.1234" },
+ new IPAddress[] { IPAddress.Loopback },
+ Array.Empty());
+
+ data.Add(
+ true,
+ true,
+ new string[] { "::1" },
+ Array.Empty(),
+ new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) });
+
+ data.Add(
+ false,
+ false,
+ new string[] { "localhost" },
+ Array.Empty(),
+ Array.Empty());
+
+ data.Add(
+ true,
+ false,
+ new string[] { "localhost" },
+ new IPAddress[] { IPAddress.Loopback },
+ Array.Empty());
+
+ data.Add(
+ false,
+ true,
+ new string[] { "localhost" },
+ Array.Empty(),
+ new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) });
+
+ data.Add(
+ true,
+ true,
+ new string[] { "localhost" },
+ new IPAddress[] { IPAddress.Loopback.MapToIPv6() },
+ new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) });
+ return data;
+ }
+
[Theory]
- // [InlineData(true, true, "192.168.0.0/16,www.yahoo.co.uk", "::ffff:212.82.100.150,::ffff:192.168.0.0/16")] <- fails on Max. www.yahoo.co.uk resolves to a different ip address.
- // [InlineData(true, false, "192.168.0.0/16,www.yahoo.co.uk", "212.82.100.150,192.168.0.0/16")]
- [InlineData(true, true, "192.168.t,127.0.0.1,1234.1232.12.1234", "::ffff:127.0.0.1")]
- [InlineData(true, false, "192.168.x,127.0.0.1,1234.1232.12.1234", "127.0.0.1")]
- [InlineData(true, true, "::1", "::1/128")]
- public void TestNetworks(bool ip4, bool ip6, string hostList, string match)
+ [MemberData(nameof(TestNetworks_TestData))]
+ public void TestNetworks(bool ip4, bool ip6, string[] hostList, IPAddress[] knownProxies, IPNetwork[] knownNetworks)
{
using var nm = CreateNetworkManager();
@@ -36,31 +84,25 @@ namespace Jellyfin.Server.Tests
EnableIPV6 = ip6
};
- var result = match + ",";
ForwardedHeadersOptions options = new ForwardedHeadersOptions();
// Need this here as ::1 and 127.0.0.1 are in them by default.
options.KnownProxies.Clear();
options.KnownNetworks.Clear();
- ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList.Split(','), options);
+ ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList, options);
- var sb = new StringBuilder();
- foreach (var item in options.KnownProxies)
+ Assert.Equal(knownProxies.Length, options.KnownProxies.Count);
+ foreach (var item in knownProxies)
{
- sb.Append(item)
- .Append(',');
+ Assert.True(options.KnownProxies.Contains(item));
}
- foreach (var item in options.KnownNetworks)
+ Assert.Equal(knownNetworks.Length, options.KnownNetworks.Count);
+ foreach (var item in knownNetworks)
{
- sb.Append(item.Prefix)
- .Append('/')
- .Append(item.PrefixLength.ToString(CultureInfo.InvariantCulture))
- .Append(',');
+ Assert.NotNull(options.KnownNetworks.FirstOrDefault(x => x.Prefix.Equals(item.Prefix) && x.PrefixLength == item.PrefixLength));
}
-
- Assert.Equal(sb.ToString(), result);
}
private static IConfigurationManager GetMockConfig(NetworkConfiguration conf)
From 60053c7f3d2ea6dbdeae5eecc8a46da23c672cd6 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 4 Aug 2021 18:49:54 +0200
Subject: [PATCH 020/118] Fix log messages
---
MediaBrowser.Common/Net/IPHost.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs
index bdadcbf952..e4f9142508 100644
--- a/MediaBrowser.Common/Net/IPHost.cs
+++ b/MediaBrowser.Common/Net/IPHost.cs
@@ -195,7 +195,7 @@ namespace MediaBrowser.Common.Net
return res;
}
- throw new InvalidCastException("Host does not contain a valid value. {host}");
+ throw new InvalidCastException($"Host does not contain a valid value. {host}");
}
///
@@ -220,7 +220,7 @@ namespace MediaBrowser.Common.Net
return res;
}
- throw new InvalidCastException("Host does not contain a valid value. {host}");
+ throw new InvalidCastException($"Host does not contain a valid value. {host}");
}
///
@@ -433,7 +433,7 @@ namespace MediaBrowser.Common.Net
catch (SocketException ex)
{
// Log and then ignore socket errors, as the result value will just be an empty array.
- Debug.WriteLine("GetHostEntryAsync failed with {Message}.", ex.Message);
+ Debug.WriteLine("GetHostAddresses failed with {Message}.", ex.Message);
}
}
}
From 6c42d2345dd575d1f9bfd5293aa0a1eb30b00518 Mon Sep 17 00:00:00 2001
From: MrChip53
Date: Wed, 4 Aug 2021 19:19:03 -0500
Subject: [PATCH 021/118] Properly stream M3U file over http
---
.../LiveTv/TunerHosts/BaseTunerHost.cs | 3 +-
.../LiveTv/TunerHosts/M3uParser.cs | 33 +++++++++++--------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 5941613cf9..b3524f27c3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -83,9 +83,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
try
{
var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false);
- var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
- list.AddRange(newChannels);
+ list.AddRange(channels);
if (!enableCache)
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index c9657f6057..2725f65c3c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -13,7 +13,6 @@ using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.LiveTv;
using Microsoft.Extensions.Logging;
@@ -44,22 +43,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
{
- if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ if (info == null)
{
- using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
- if (!string.IsNullOrEmpty(info.UserAgent))
- {
- requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
- }
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return File.OpenRead(info.Url);
+ }
- var response = await _httpClientFactory.CreateClient(NamedClient.Default)
- .SendAsync(requestMessage, cancellationToken)
- .ConfigureAwait(false);
- response.EnsureSuccessStatusCode();
- return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
+ if (!string.IsNullOrEmpty(info.UserAgent))
+ {
+ requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
}
- return File.OpenRead(info.Url);
+ var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
+ .ConfigureAwait(false);
+ response.EnsureSuccessStatusCode();
+
+ return await response.Content.ReadAsStreamAsync(cancellationToken);
}
private async Task> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId)
@@ -83,7 +88,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
{
extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim();
- _logger.LogInformation("Found m3u channel: {0}", extInf);
}
else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#'))
{
@@ -99,6 +103,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
channel.Path = trimmedLine;
channels.Add(channel);
+ _logger.LogInformation("Parsed channel: {0}", channel.Name);
extInf = string.Empty;
}
}
From 883d28d03df02d2674c82c110071d2aa2baa0918 Mon Sep 17 00:00:00 2001
From: Chris Simoni <57076668+MrChip53@users.noreply.github.com>
Date: Fri, 6 Aug 2021 08:10:28 -0500
Subject: [PATCH 022/118] Update
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
Co-authored-by: Claus Vium
---
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 2725f65c3c..a6334c1b3e 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -103,7 +103,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
channel.Path = trimmedLine;
channels.Add(channel);
- _logger.LogInformation("Parsed channel: {0}", channel.Name);
+ _logger.LogInformation("Parsed channel: {ChannelName}", channel.Name);
extInf = string.Empty;
}
}
From eaa5575b232570453315431d5461a736831b88d2 Mon Sep 17 00:00:00 2001
From: MrChip53
Date: Fri, 6 Aug 2021 10:07:50 -0500
Subject: [PATCH 023/118] Add comment
---
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index a6334c1b3e..16ff98a7d8 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -59,6 +59,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
}
+ // Set HttpCompletionOption.ResponseHeadersRead to prevent timeouts on larger files
var response = await _httpClientFactory.CreateClient(NamedClient.Default)
.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
.ConfigureAwait(false);
From 14a53fe402feb62bef99eaf8100dba822c500e3f Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Fri, 6 Aug 2021 23:12:29 -0700
Subject: [PATCH 024/118] Adding tests for AiredEpisodeOrderComparer.
---
Jellyfin.sln | 21 +++---
.../AiredEpisodeOrderComparerTests.cs | 74 +++++++++++++++++++
.../Emby.Server.Implementations.Tests.csproj | 27 +++++++
3 files changed, 113 insertions(+), 9 deletions(-)
create mode 100644 tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs
create mode 100644 tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj
diff --git a/Jellyfin.sln b/Jellyfin.sln
index 4626601c3d..69e3618626 100644
--- a/Jellyfin.sln
+++ b/Jellyfin.sln
@@ -77,17 +77,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Model.Tests", "tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Providers.Tests", "tests\Jellyfin.Providers.Tests\Jellyfin.Providers.Tests.csproj", "{A964008C-2136-4716-B6CB-B3426C22320A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Providers.Tests", "tests\Jellyfin.Providers.Tests\Jellyfin.Providers.Tests.csproj", "{A964008C-2136-4716-B6CB-B3426C22320A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions", "src\Jellyfin.Extensions\Jellyfin.Extensions.csproj", "{750B8757-BE3D-4F8C-941A-FBAD94904ADA}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Extensions", "src\Jellyfin.Extensions\Jellyfin.Extensions.csproj", "{750B8757-BE3D-4F8C-941A-FBAD94904ADA}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions.Tests", "tests\Jellyfin.Extensions.Tests\Jellyfin.Extensions.Tests.csproj", "{332A5C7A-F907-47CA-910E-BE6F7371B9E0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Extensions.Tests", "tests\Jellyfin.Extensions.Tests\Jellyfin.Extensions.Tests.csproj", "{332A5C7A-F907-47CA-910E-BE6F7371B9E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations.Tests", "tests\Emby.Server.Implementations.Tests\Emby.Server.Implementations.Tests.csproj", "{3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -219,10 +221,6 @@ Global
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU
- {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -243,6 +241,10 @@ Global
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -263,6 +265,7 @@ Global
{A964008C-2136-4716-B6CB-B3426C22320A} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{750B8757-BE3D-4F8C-941A-FBAD94904ADA} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C}
{332A5C7A-F907-47CA-910E-BE6F7371B9E0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
+ {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
diff --git a/tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs b/tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs
new file mode 100644
index 0000000000..a13c8a5441
--- /dev/null
+++ b/tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Emby.Server.Implementations.Sorting;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using Xunit;
+
+namespace Emby.Server.Implementations.Tests
+{
+ public class AiredEpisodeOrderComparerTests
+ {
+ [Theory]
+ [ClassData(typeof(EpisodeTestData))]
+ public void Test1(BaseItem x, BaseItem y, int expected, bool err)
+ {
+ var cmp = new AiredEpisodeOrderComparer();
+ if (err == true)
+ {
+ Assert.Throws(() => cmp.Compare(x, y));
+ }
+ else
+ {
+ Assert.Equal(expected, cmp.Compare(x, y));
+ if (expected == 1)
+ {
+ Assert.Equal(expected * -1, cmp.Compare(y, x));
+ }
+ }
+ }
+
+ private class EpisodeTestData : IEnumerable
+ {
+ public IEnumerator GetEnumerator()
+ {
+ // Some Error or "bad" cases
+ yield return new object?[] { null, new Episode(), 0, true };
+ yield return new object?[] { new Episode(), null, 0, true };
+
+ yield return new object?[] { new Movie(), new Movie(), 0, false };
+ yield return new object?[] { new Movie(), new Episode(), 1, false };
+ // Good cases
+ yield return new object?[] { new Episode(), new Episode(), 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 2, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
+ // Good Specials
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+
+ // Specials to Episodes
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 3 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+ }
+}
diff --git a/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj b/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj
new file mode 100644
index 0000000000..aa5d4bff4b
--- /dev/null
+++ b/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net5.0
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
From 8b4d9339cf73d0d227f76866205b917546c152e0 Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Sat, 7 Aug 2021 15:07:12 -0700
Subject: [PATCH 025/118] Changing namespace to be within
Jellyfin.Server.Implementations.Tests
---
Jellyfin.sln | 7 --
.../Sorting/AiredEpisodeOrderComparerTests.cs | 74 +++++++++++++++++++
2 files changed, 74 insertions(+), 7 deletions(-)
create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
diff --git a/Jellyfin.sln b/Jellyfin.sln
index 69e3618626..1e5e10993f 100644
--- a/Jellyfin.sln
+++ b/Jellyfin.sln
@@ -89,8 +89,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Extensions", "src\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Extensions.Tests", "tests\Jellyfin.Extensions.Tests\Jellyfin.Extensions.Tests.csproj", "{332A5C7A-F907-47CA-910E-BE6F7371B9E0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations.Tests", "tests\Emby.Server.Implementations.Tests\Emby.Server.Implementations.Tests.csproj", "{3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -241,10 +239,6 @@ Global
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.Build.0 = Release|Any CPU
- {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -265,7 +259,6 @@ Global
{A964008C-2136-4716-B6CB-B3426C22320A} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{750B8757-BE3D-4F8C-941A-FBAD94904ADA} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C}
{332A5C7A-F907-47CA-910E-BE6F7371B9E0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
- {3FF50D0E-DA00-42B5-8742-55F2EA34C0EA} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
new file mode 100644
index 0000000000..46128f48b4
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Emby.Server.Implementations.Sorting;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Sorting
+{
+ public class AiredEpisodeOrderComparerTests
+ {
+ [Theory]
+ [ClassData(typeof(EpisodeTestData))]
+ public void Test1(BaseItem x, BaseItem y, int expected, bool err)
+ {
+ var cmp = new AiredEpisodeOrderComparer();
+ if (err == true)
+ {
+ Assert.Throws(() => cmp.Compare(x, y));
+ }
+ else
+ {
+ Assert.Equal(expected, cmp.Compare(x, y));
+ if (expected == 1)
+ {
+ Assert.Equal(expected * -1, cmp.Compare(y, x));
+ }
+ }
+ }
+
+ private class EpisodeTestData : IEnumerable
+ {
+ public IEnumerator GetEnumerator()
+ {
+ // Some Error or "bad" cases
+ yield return new object?[] { null, new Episode(), 0, true };
+ yield return new object?[] { new Episode(), null, 0, true };
+
+ yield return new object?[] { new Movie(), new Movie(), 0, false };
+ yield return new object?[] { new Movie(), new Episode(), 1, false };
+ // Good cases
+ yield return new object?[] { new Episode(), new Episode(), 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 2, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
+ // Good Specials
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+
+ // Specials to Episodes
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 0, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 3 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+ }
+}
From 8e6436d8dda7640db6a02fd669bbbd03b7c29c03 Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Sat, 7 Aug 2021 15:36:23 -0700
Subject: [PATCH 026/118] Removing moved files.
---
.../AiredEpisodeOrderComparerTests.cs | 74 -------------------
.../Emby.Server.Implementations.Tests.csproj | 27 -------
2 files changed, 101 deletions(-)
delete mode 100644 tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs
delete mode 100644 tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj
diff --git a/tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs b/tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs
deleted file mode 100644
index a13c8a5441..0000000000
--- a/tests/Emby.Server.Implementations.Tests/AiredEpisodeOrderComparerTests.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using Emby.Server.Implementations.Sorting;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using Xunit;
-
-namespace Emby.Server.Implementations.Tests
-{
- public class AiredEpisodeOrderComparerTests
- {
- [Theory]
- [ClassData(typeof(EpisodeTestData))]
- public void Test1(BaseItem x, BaseItem y, int expected, bool err)
- {
- var cmp = new AiredEpisodeOrderComparer();
- if (err == true)
- {
- Assert.Throws(() => cmp.Compare(x, y));
- }
- else
- {
- Assert.Equal(expected, cmp.Compare(x, y));
- if (expected == 1)
- {
- Assert.Equal(expected * -1, cmp.Compare(y, x));
- }
- }
- }
-
- private class EpisodeTestData : IEnumerable
- {
- public IEnumerator GetEnumerator()
- {
- // Some Error or "bad" cases
- yield return new object?[] { null, new Episode(), 0, true };
- yield return new object?[] { new Episode(), null, 0, true };
-
- yield return new object?[] { new Movie(), new Movie(), 0, false };
- yield return new object?[] { new Movie(), new Episode(), 1, false };
- // Good cases
- yield return new object?[] { new Episode(), new Episode(), 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 2, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
- // Good Specials
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
-
- // Specials to Episodes
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
-
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
-
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, 1, false };
-
- yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
-
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 3 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
- }
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- }
- }
-}
diff --git a/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj b/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj
deleted file mode 100644
index aa5d4bff4b..0000000000
--- a/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
- net5.0
-
- false
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
-
From 5eac2675696b7774e212983bf18d56f62ad1a7ba Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Sat, 7 Aug 2021 15:55:52 -0700
Subject: [PATCH 027/118] Reverting sln file.
---
Jellyfin.sln | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/Jellyfin.sln b/Jellyfin.sln
index 1e5e10993f..4626601c3d 100644
--- a/Jellyfin.sln
+++ b/Jellyfin.sln
@@ -77,17 +77,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Model.Tests", "tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Providers.Tests", "tests\Jellyfin.Providers.Tests\Jellyfin.Providers.Tests.csproj", "{A964008C-2136-4716-B6CB-B3426C22320A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Providers.Tests", "tests\Jellyfin.Providers.Tests\Jellyfin.Providers.Tests.csproj", "{A964008C-2136-4716-B6CB-B3426C22320A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Extensions", "src\Jellyfin.Extensions\Jellyfin.Extensions.csproj", "{750B8757-BE3D-4F8C-941A-FBAD94904ADA}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions", "src\Jellyfin.Extensions\Jellyfin.Extensions.csproj", "{750B8757-BE3D-4F8C-941A-FBAD94904ADA}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Extensions.Tests", "tests\Jellyfin.Extensions.Tests\Jellyfin.Extensions.Tests.csproj", "{332A5C7A-F907-47CA-910E-BE6F7371B9E0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions.Tests", "tests\Jellyfin.Extensions.Tests\Jellyfin.Extensions.Tests.csproj", "{332A5C7A-F907-47CA-910E-BE6F7371B9E0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -219,6 +219,10 @@ Global
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
From 579ed5c1fc771f5598747ad3bdfdb5e025aac051 Mon Sep 17 00:00:00 2001
From: Rob
Date: Mon, 9 Aug 2021 11:27:40 -0700
Subject: [PATCH 028/118] Update
tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
Co-authored-by: Cody Robibero
---
.../Sorting/AiredEpisodeOrderComparerTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
index 46128f48b4..5646a1bd97 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -25,7 +25,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
Assert.Equal(expected, cmp.Compare(x, y));
if (expected == 1)
{
- Assert.Equal(expected * -1, cmp.Compare(y, x));
+ Assert.Equal(-expected, cmp.Compare(y, x));
}
}
}
From 67f81e8c07d57186e8ef28e9caa40dab552f1ffe Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Mon, 9 Aug 2021 11:27:19 -0700
Subject: [PATCH 029/118] Changing test name to be more descrptive.
---
.../Sorting/AiredEpisodeOrderComparerTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
index 5646a1bd97..d11a4f6b69 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -13,7 +13,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
{
[Theory]
[ClassData(typeof(EpisodeTestData))]
- public void Test1(BaseItem x, BaseItem y, int expected, bool err)
+ public void AiredEpisodeOrderCompareTest(BaseItem x, BaseItem y, int expected, bool err)
{
var cmp = new AiredEpisodeOrderComparer();
if (err == true)
From 08152d2a981401d803d0c64aba51df3e7891fea6 Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Wed, 11 Aug 2021 12:50:35 +0200
Subject: [PATCH 030/118] Apply suggestions from code review
---
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index b3524f27c3..de178ad5b1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -83,8 +83,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
try
{
var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false);
+ var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase)));
- list.AddRange(channels);
+ list.AddRange(newChannels);
if (!enableCache)
{
From 78bbfca990de552427317260dcdb02f515e2db1c Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Wed, 11 Aug 2021 09:56:36 -0700
Subject: [PATCH 031/118] Separating out error cases from good cases.
---
.../Sorting/AiredEpisodeOrderComparerTests.cs | 75 +++++++++++--------
1 file changed, 42 insertions(+), 33 deletions(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
index d11a4f6b69..ff7999612b 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -11,61 +11,70 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
{
public class AiredEpisodeOrderComparerTests
{
+ [Theory]
+ [ClassData(typeof(EpisodeBadData))]
+ public void AiredEpisodeOrderCompareErrorTest(BaseItem x, BaseItem y)
+ {
+ var cmp = new AiredEpisodeOrderComparer();
+ Assert.Throws(() => cmp.Compare(x, y));
+ }
+
[Theory]
[ClassData(typeof(EpisodeTestData))]
- public void AiredEpisodeOrderCompareTest(BaseItem x, BaseItem y, int expected, bool err)
+ public void AiredEpisodeOrderCompareTest(BaseItem x, BaseItem y, int expected)
{
var cmp = new AiredEpisodeOrderComparer();
- if (err == true)
+
+ Assert.Equal(expected, cmp.Compare(x, y));
+ if (expected == 1)
{
- Assert.Throws(() => cmp.Compare(x, y));
+ Assert.Equal(-expected, cmp.Compare(y, x));
}
- else
+ }
+
+ private class EpisodeBadData : IEnumerable
+ {
+ public IEnumerator GetEnumerator()
{
- Assert.Equal(expected, cmp.Compare(x, y));
- if (expected == 1)
- {
- Assert.Equal(-expected, cmp.Compare(y, x));
- }
+ yield return new object?[] { null, new Episode() };
+ yield return new object?[] { new Episode() };
}
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
private class EpisodeTestData : IEnumerable
{
public IEnumerator GetEnumerator()
{
- // Some Error or "bad" cases
- yield return new object?[] { null, new Episode(), 0, true };
- yield return new object?[] { new Episode(), null, 0, true };
-
- yield return new object?[] { new Movie(), new Movie(), 0, false };
- yield return new object?[] { new Movie(), new Episode(), 1, false };
+ yield return new object?[] { new Movie(), new Movie(), 0 };
+ yield return new object?[] { new Movie(), new Episode(), 1 };
// Good cases
- yield return new object?[] { new Episode(), new Episode(), 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 2, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode(), new Episode(), 0 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 0 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 2, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1 };
// Good Specials
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 0 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
// Specials to Episodes
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 0, false };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 3 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1, false };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 }, 1 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 0 };
+ yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 3 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1 };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
From 4c1286fd24139527f1fbd3db23e8d74e4d8da283 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Thu, 12 Aug 2021 21:38:54 +0200
Subject: [PATCH 032/118] Address comment
---
MediaBrowser.Common/Net/IPHost.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs
index e4f9142508..1f125f2b1d 100644
--- a/MediaBrowser.Common/Net/IPHost.cs
+++ b/MediaBrowser.Common/Net/IPHost.cs
@@ -348,7 +348,7 @@ namespace MediaBrowser.Common.Net
}
}
- output = output[0..^1];
+ output = output[..^1];
if (moreThanOne)
{
@@ -428,7 +428,7 @@ namespace MediaBrowser.Common.Net
{
try
{
- _addresses = Dns.GetHostAddresses(hostName);
+ _addresses = Dns.GetHostEntry(hostName).AddressList;
}
catch (SocketException ex)
{
From 577d665192ab79cdd2f725ca0be0b9948ff5eed3 Mon Sep 17 00:00:00 2001
From: David Ullmer
Date: Fri, 13 Aug 2021 20:16:05 +0200
Subject: [PATCH 033/118] Move thumb tag parsing to separate method
---
.../Parsers/BaseNfoParser.cs | 110 +++++++++---------
1 file changed, 57 insertions(+), 53 deletions(-)
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 2c86f9242d..242a7132b1 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -783,59 +783,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "thumb":
{
- var artType = reader.GetAttribute("aspect");
- var val = reader.ReadElementContentAsString();
-
- // skip:
- // - empty aspect tag
- // - empty uri
- // - tag containing '.' because we can't set images for seasons, episodes or movie sets within series or movies
- if (string.IsNullOrEmpty(artType) || string.IsNullOrEmpty(val) || artType.Contains('.', StringComparison.Ordinal))
- {
- break;
- }
-
- ImageType imageType = GetImageType(artType);
-
- if (!Uri.TryCreate(val, UriKind.Absolute, out var uri))
- {
- Logger.LogError("Image location {Path} specified in nfo file for {ItemName} is not a valid URL or file path.", val, item.Name);
- break;
- }
-
- if (uri.IsFile)
- {
- // only allow one item of each type
- if (itemResult.Images.Any(x => x.Type == imageType))
- {
- break;
- }
-
- var fileSystemMetadata = _directoryService.GetFile(val);
- // non existing file returns null
- if (fileSystemMetadata == null || !fileSystemMetadata.Exists)
- {
- Logger.LogWarning("Artwork file {Path} specified in nfo file for {ItemName} does not exist.", uri, item.Name);
- break;
- }
-
- itemResult.Images.Add(new LocalImageInfo()
- {
- FileInfo = fileSystemMetadata,
- Type = imageType
- });
- }
- else
- {
- // only allow one item of each type
- if (itemResult.RemoteImages.Any(x => x.type == imageType))
- {
- break;
- }
-
- itemResult.RemoteImages.Add((uri.ToString(), imageType));
- }
-
+ FetchThumbNode(reader, itemResult);
break;
}
@@ -858,6 +806,62 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
+ private void FetchThumbNode(XmlReader reader, MetadataResult itemResult)
+ {
+ var artType = reader.GetAttribute("aspect");
+ var val = reader.ReadElementContentAsString();
+
+ // skip:
+ // - empty aspect tag
+ // - empty uri
+ // - tag containing '.' because we can't set images for seasons, episodes or movie sets within series or movies
+ if (string.IsNullOrEmpty(artType) || string.IsNullOrEmpty(val) || artType.Contains('.', StringComparison.Ordinal))
+ {
+ return;
+ }
+
+ ImageType imageType = GetImageType(artType);
+
+ if (!Uri.TryCreate(val, UriKind.Absolute, out var uri))
+ {
+ Logger.LogError("Image location {Path} specified in nfo file for {ItemName} is not a valid URL or file path.", val, itemResult.Item.Name);
+ return;
+ }
+
+ if (uri.IsFile)
+ {
+ // only allow one item of each type
+ if (itemResult.Images.Any(x => x.Type == imageType))
+ {
+ return;
+ }
+
+ var fileSystemMetadata = _directoryService.GetFile(val);
+ // non existing file returns null
+ if (fileSystemMetadata == null || !fileSystemMetadata.Exists)
+ {
+ Logger.LogWarning("Artwork file {Path} specified in nfo file for {ItemName} does not exist.", uri, itemResult.Item.Name);
+ return;
+ }
+
+ itemResult.Images.Add(new LocalImageInfo()
+ {
+ FileInfo = fileSystemMetadata,
+ Type = imageType
+ });
+ }
+ else
+ {
+ // only allow one item of each type
+ if (itemResult.RemoteImages.Any(x => x.type == imageType))
+ {
+ return;
+ }
+
+ itemResult.RemoteImages.Add((uri.ToString(), imageType));
+ }
+ }
+
private void FetchFromFileInfoNode(XmlReader reader, T item)
{
reader.MoveToContent();
From 12e58840eb6d7045e6b706580e057f4fb910fc3d Mon Sep 17 00:00:00 2001
From: David Ullmer
Date: Fri, 13 Aug 2021 20:33:53 +0200
Subject: [PATCH 034/118] Modify FetchThumbNode method to read children of
fanart tag
---
.../Parsers/BaseNfoParser.cs | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 242a7132b1..0edb7f43c4 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -787,6 +787,14 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
+ case "fanart":
+ {
+ var subtree = reader.ReadSubtree();
+ subtree.ReadToDescendant("thumb");
+ FetchThumbNode(subtree, itemResult);
+ break;
+ }
+
default:
string readerName = reader.Name;
if (_validProviderIds.TryGetValue(readerName, out string? providerIdValue))
@@ -811,11 +819,17 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var artType = reader.GetAttribute("aspect");
var val = reader.ReadElementContentAsString();
+ // artType is null if the thumb node is a child of the fanart tag
+ // -> set image type to fanart
+ if (string.IsNullOrWhiteSpace(artType))
+ {
+ artType = "fanart";
+ }
+
// skip:
- // - empty aspect tag
// - empty uri
// - tag containing '.' because we can't set images for seasons, episodes or movie sets within series or movies
- if (string.IsNullOrEmpty(artType) || string.IsNullOrEmpty(val) || artType.Contains('.', StringComparison.Ordinal))
+ if (string.IsNullOrEmpty(val) || artType.Contains('.', StringComparison.Ordinal))
{
return;
}
From bf441e49b9128024eec57de1122960ad4cde90f7 Mon Sep 17 00:00:00 2001
From: David Ullmer
Date: Fri, 13 Aug 2021 20:36:14 +0200
Subject: [PATCH 035/118] Add test for fanart tag
---
.../Parsers/MovieNfoParserTests.cs | 14 ++++++++
.../Test Data/Fanart.nfo | 33 +++++++++++++++++++
2 files changed, 47 insertions(+)
create mode 100644 tests/Jellyfin.XbmcMetadata.Tests/Test Data/Fanart.nfo
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
index cbcce73eb6..ef3ca15d5a 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
@@ -207,6 +207,20 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal(id, item.ProviderIds[provider]);
}
+ [Fact]
+ public void Parse_GivenFileWithFanartTag_Success()
+ {
+ var result = new MetadataResult
public abstract class BaseItem : IHasProviderIds, IHasLookupInfo, IEquatable
{
+ ///
+ /// The trailer folder name.
+ ///
+ public const string TrailerFolderName = "trailers";
+ public const string ThemeSongsFolderName = "theme-music";
+ public const string ThemeSongFilename = "theme";
+ public const string ThemeVideosFolderName = "backdrops";
+ public const string ExtrasFolderName = "extras";
+ public const string BehindTheScenesFolderName = "behind the scenes";
+ public const string DeletedScenesFolderName = "deleted scenes";
+ public const string InterviewFolderName = "interviews";
+ public const string SceneFolderName = "scenes";
+ public const string SampleFolderName = "samples";
+ public const string ShortsFolderName = "shorts";
+ public const string FeaturettesFolderName = "featurettes";
+
///
/// The supported image extensions.
///
@@ -61,38 +77,21 @@ namespace MediaBrowser.Controller.Entities
".ttml"
};
- protected BaseItem()
- {
- Tags = Array.Empty();
- Genres = Array.Empty();
- Studios = Array.Empty();
- ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
- LockedFields = Array.Empty();
- ImageInfos = Array.Empty();
- ProductionLocations = Array.Empty();
- RemoteTrailers = Array.Empty();
- ExtraIds = Array.Empty();
- }
-
- public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
- public static char SlugChar = '-';
-
///
- /// The trailer folder name.
+ /// Extra types that should be counted and displayed as "Special Features" in the UI.
///
- public const string TrailerFolderName = "trailers";
- public const string ThemeSongsFolderName = "theme-music";
- public const string ThemeSongFilename = "theme";
- public const string ThemeVideosFolderName = "backdrops";
- public const string ExtrasFolderName = "extras";
- public const string BehindTheScenesFolderName = "behind the scenes";
- public const string DeletedScenesFolderName = "deleted scenes";
- public const string InterviewFolderName = "interviews";
- public const string SceneFolderName = "scenes";
- public const string SampleFolderName = "samples";
- public const string ShortsFolderName = "shorts";
- public const string FeaturettesFolderName = "featurettes";
+ public static readonly IReadOnlyCollection DisplayExtraTypes = new HashSet
+ {
+ Model.Entities.ExtraType.Unknown,
+ Model.Entities.ExtraType.BehindTheScenes,
+ Model.Entities.ExtraType.Clip,
+ Model.Entities.ExtraType.DeletedScene,
+ Model.Entities.ExtraType.Interview,
+ Model.Entities.ExtraType.Sample,
+ Model.Entities.ExtraType.Scene
+ };
+ public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
public static readonly string[] AllExtrasTypesFolderNames =
{
ExtrasFolderName,
@@ -105,6 +104,29 @@ namespace MediaBrowser.Controller.Entities
FeaturettesFolderName
};
+ private string _sortName;
+ private Guid[] _themeSongIds;
+ private Guid[] _themeVideoIds;
+
+ private string _forcedSortName;
+
+ private string _name;
+
+ public static char SlugChar = '-';
+
+ protected BaseItem()
+ {
+ Tags = Array.Empty();
+ Genres = Array.Empty();
+ Studios = Array.Empty();
+ ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ LockedFields = Array.Empty();
+ ImageInfos = Array.Empty();
+ ProductionLocations = Array.Empty();
+ RemoteTrailers = Array.Empty();
+ ExtraIds = Array.Empty();
+ }
+
[JsonIgnore]
public Guid[] ThemeSongIds
{
@@ -194,8 +216,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public virtual bool SupportsRemoteImageDownloading => true;
- private string _name;
-
///
/// Gets or sets the name.
///
@@ -328,12 +348,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public virtual bool IsHidden => false;
- public BaseItem GetOwner()
- {
- var ownerId = OwnerId;
- return ownerId.Equals(Guid.Empty) ? null : LibraryManager.GetItemById(ownerId);
- }
-
///
/// Gets the type of the location.
///
@@ -379,13 +393,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- public bool IsPathProtocol(MediaProtocol protocol)
- {
- var current = PathProtocol;
-
- return current.HasValue && current.Value == protocol;
- }
-
[JsonIgnore]
public bool IsFileProtocol => IsPathProtocol(MediaProtocol.File);
@@ -423,35 +430,17 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public virtual bool EnableAlphaNumericSorting => true;
- private List> GetSortChunks(string s1)
- {
- var list = new List>();
-
- int thisMarker = 0;
-
- while (thisMarker < s1.Length)
- {
- char thisCh = s1[thisMarker];
+ public virtual bool IsHD => Height >= 720;
- var thisChunk = new StringBuilder();
- bool isNumeric = char.IsDigit(thisCh);
+ public bool IsShortcut { get; set; }
- while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric)
- {
- thisChunk.Append(thisCh);
- thisMarker++;
+ public string ShortcutPath { get; set; }
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
+ public int Width { get; set; }
- list.Add(new Tuple(thisChunk, isNumeric));
- }
+ public int Height { get; set; }
- return list;
- }
+ public Guid[] ExtraIds { get; set; }
///
/// Gets the primary image path.
@@ -463,72 +452,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public string PrimaryImagePath => this.GetImagePath(ImageType.Primary);
- public virtual bool CanDelete()
- {
- if (SourceType == SourceType.Channel)
- {
- return ChannelManager.CanDelete(this);
- }
-
- return IsFileProtocol;
- }
-
- public virtual bool IsAuthorizedToDelete(User user, List allCollectionFolders)
- {
- if (user.HasPermission(PermissionKind.EnableContentDeletion))
- {
- return true;
- }
-
- var allowed = user.GetPreferenceValues(PreferenceKind.EnableContentDeletionFromFolders);
-
- if (SourceType == SourceType.Channel)
- {
- return allowed.Contains(ChannelId);
- }
- else
- {
- var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders);
-
- foreach (var folder in collectionFolders)
- {
- if (allowed.Contains(folder.Id))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- public bool CanDelete(User user, List allCollectionFolders)
- {
- return CanDelete() && IsAuthorizedToDelete(user, allCollectionFolders);
- }
-
- public bool CanDelete(User user)
- {
- var allCollectionFolders = LibraryManager.GetUserRootFolder().Children.OfType().ToList();
-
- return CanDelete(user, allCollectionFolders);
- }
-
- public virtual bool CanDownload()
- {
- return false;
- }
-
- public virtual bool IsAuthorizedToDownload(User user)
- {
- return user.HasPermission(PermissionKind.EnableContentDownloading);
- }
-
- public bool CanDownload(User user)
- {
- return CanDownload() && IsAuthorizedToDownload(user);
- }
-
///
/// Gets or sets the date created.
///
@@ -548,38 +471,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public DateTime DateLastRefreshed { get; set; }
- ///
- /// Gets or sets the logger.
- ///
- public static ILogger Logger { get; set; }
-
- public static ILibraryManager LibraryManager { get; set; }
-
- public static IServerConfigurationManager ConfigurationManager { get; set; }
-
- public static IProviderManager ProviderManager { get; set; }
-
- public static ILocalizationManager LocalizationManager { get; set; }
-
- public static IItemRepository ItemRepository { get; set; }
-
- public static IFileSystem FileSystem { get; set; }
-
- public static IUserDataManager UserDataManager { get; set; }
-
- public static IChannelManager ChannelManager { get; set; }
-
- public static IMediaSourceManager MediaSourceManager { get; set; }
-
- ///
- /// Returns a that represents this instance.
- ///
- /// A that represents this instance.
- public override string ToString()
- {
- return Name;
- }
-
[JsonIgnore]
public bool IsLocked { get; set; }
@@ -611,211 +502,87 @@ namespace MediaBrowser.Controller.Entities
}
}
- private string _forcedSortName;
-
- ///
- /// Gets or sets the name of the forced sort.
- ///
- /// The name of the forced sort.
[JsonIgnore]
- public string ForcedSortName
+ public bool EnableMediaSourceDisplay
{
- get => _forcedSortName;
- set
+ get
{
- _forcedSortName = value;
- _sortName = null;
+ if (SourceType == SourceType.Channel)
+ {
+ return ChannelManager.EnableMediaSourceDisplay(this);
+ }
+
+ return true;
}
}
- private string _sortName;
- private Guid[] _themeSongIds;
- private Guid[] _themeVideoIds;
+ [JsonIgnore]
+ public Guid ParentId { get; set; }
///
- /// Gets or sets the name of the sort.
+ /// Gets or sets the logger.
///
- /// The name of the sort.
- [JsonIgnore]
- public string SortName
- {
- get
- {
- if (_sortName == null)
- {
- if (!string.IsNullOrEmpty(ForcedSortName))
- {
- // Need the ToLower because that's what CreateSortName does
- _sortName = ModifySortChunks(ForcedSortName).ToLowerInvariant();
- }
- else
- {
- _sortName = CreateSortName();
- }
- }
-
- return _sortName;
- }
-
- set => _sortName = value;
- }
-
- public string GetInternalMetadataPath()
- {
- var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
-
- return GetInternalMetadataPath(basePath);
- }
-
- protected virtual string GetInternalMetadataPath(string basePath)
- {
- if (SourceType == SourceType.Channel)
- {
- return System.IO.Path.Join(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture));
- }
-
- ReadOnlySpan idString = Id.ToString("N", CultureInfo.InvariantCulture);
-
- return System.IO.Path.Join(basePath, "library", idString.Slice(0, 2), idString);
- }
-
- ///
- /// Creates the name of the sort.
- ///
- /// System.String.
- protected virtual string CreateSortName()
- {
- if (Name == null)
- {
- return null; // some items may not have name filled in properly
- }
-
- if (!EnableAlphaNumericSorting)
- {
- return Name.TrimStart();
- }
-
- var sortable = Name.Trim().ToLowerInvariant();
-
- foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters)
- {
- sortable = sortable.Replace(removeChar, string.Empty, StringComparison.Ordinal);
- }
-
- foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters)
- {
- sortable = sortable.Replace(replaceChar, " ", StringComparison.Ordinal);
- }
-
- foreach (var search in ConfigurationManager.Configuration.SortRemoveWords)
- {
- // Remove from beginning if a space follows
- if (sortable.StartsWith(search + " ", StringComparison.Ordinal))
- {
- sortable = sortable.Remove(0, search.Length + 1);
- }
-
- // Remove from middle if surrounded by spaces
- sortable = sortable.Replace(" " + search + " ", " ", StringComparison.Ordinal);
-
- // Remove from end if followed by a space
- if (sortable.EndsWith(" " + search, StringComparison.Ordinal))
- {
- sortable = sortable.Remove(sortable.Length - (search.Length + 1));
- }
- }
+ public static ILogger Logger { get; set; }
- return ModifySortChunks(sortable);
- }
+ public static ILibraryManager LibraryManager { get; set; }
- private string ModifySortChunks(string name)
- {
- var chunks = GetSortChunks(name);
+ public static IServerConfigurationManager ConfigurationManager { get; set; }
- var builder = new StringBuilder();
+ public static IProviderManager ProviderManager { get; set; }
- foreach (var chunk in chunks)
- {
- var chunkBuilder = chunk.Item1;
+ public static ILocalizationManager LocalizationManager { get; set; }
- // This chunk is numeric
- if (chunk.Item2)
- {
- while (chunkBuilder.Length < 10)
- {
- chunkBuilder.Insert(0, '0');
- }
- }
+ public static IItemRepository ItemRepository { get; set; }
- builder.Append(chunkBuilder);
- }
+ public static IFileSystem FileSystem { get; set; }
- // logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
- return builder.ToString().RemoveDiacritics();
- }
+ public static IUserDataManager UserDataManager { get; set; }
- [JsonIgnore]
- public bool EnableMediaSourceDisplay
- {
- get
- {
- if (SourceType == SourceType.Channel)
- {
- return ChannelManager.EnableMediaSourceDisplay(this);
- }
+ public static IChannelManager ChannelManager { get; set; }
- return true;
- }
- }
+ public static IMediaSourceManager MediaSourceManager { get; set; }
+ ///
+ /// Gets or sets the name of the forced sort.
+ ///
+ /// The name of the forced sort.
[JsonIgnore]
- public Guid ParentId { get; set; }
-
- public void SetParent(Folder parent)
- {
- ParentId = parent == null ? Guid.Empty : parent.Id;
- }
-
- public BaseItem GetParent()
- {
- var parentId = ParentId;
- if (!parentId.Equals(Guid.Empty))
- {
- return LibraryManager.GetItemById(parentId);
- }
-
- return null;
- }
-
- public IEnumerable GetParents()
+ public string ForcedSortName
{
- var parent = GetParent();
-
- while (parent != null)
+ get => _forcedSortName;
+ set
{
- yield return parent;
-
- parent = parent.GetParent();
+ _forcedSortName = value;
+ _sortName = null;
}
}
///
- /// Finds a parent of a given type.
+ /// Gets or sets the name of the sort.
///
- ///
- /// ``0.
- public T FindParent()
- where T : Folder
+ /// The name of the sort.
+ [JsonIgnore]
+ public string SortName
{
- foreach (var parent in GetParents())
+ get
{
- if (parent is T item)
+ if (_sortName == null)
{
- return item;
+ if (!string.IsNullOrEmpty(ForcedSortName))
+ {
+ // Need the ToLower because that's what CreateSortName does
+ _sortName = ModifySortChunks(ForcedSortName).ToLowerInvariant();
+ }
+ else
+ {
+ _sortName = CreateSortName();
+ }
}
+
+ return _sortName;
}
- return null;
+ set => _sortName = value;
}
[JsonIgnore]
@@ -948,56 +715,399 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public int? IndexNumber { get; set; }
- ///
- /// Gets or sets the parent index number. For an episode this could be the season number, or for a song this could be the disc number.
- ///
- /// The parent index number.
- [JsonIgnore]
- public int? ParentIndexNumber { get; set; }
+ ///
+ /// Gets or sets the parent index number. For an episode this could be the season number, or for a song this could be the disc number.
+ ///
+ /// The parent index number.
+ [JsonIgnore]
+ public int? ParentIndexNumber { get; set; }
+
+ [JsonIgnore]
+ public virtual bool HasLocalAlternateVersions => false;
+
+ [JsonIgnore]
+ public string OfficialRatingForComparison
+ {
+ get
+ {
+ var officialRating = OfficialRating;
+ if (!string.IsNullOrEmpty(officialRating))
+ {
+ return officialRating;
+ }
+
+ var parent = DisplayParent;
+ if (parent != null)
+ {
+ return parent.OfficialRatingForComparison;
+ }
+
+ return null;
+ }
+ }
+
+ [JsonIgnore]
+ public string CustomRatingForComparison
+ {
+ get
+ {
+ var customRating = CustomRating;
+ if (!string.IsNullOrEmpty(customRating))
+ {
+ return customRating;
+ }
+
+ var parent = DisplayParent;
+ if (parent != null)
+ {
+ return parent.CustomRatingForComparison;
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Gets or sets the provider ids.
+ ///
+ /// The provider ids.
+ [JsonIgnore]
+ public Dictionary ProviderIds { get; set; }
+
+ [JsonIgnore]
+ public virtual Folder LatestItemsIndexContainer => null;
+
+ [JsonIgnore]
+ public string PresentationUniqueKey { get; set; }
+
+ [JsonIgnore]
+ public virtual bool EnableRememberingTrackSelections => true;
+
+ [JsonIgnore]
+ public virtual bool IsTopParent
+ {
+ get
+ {
+ if (this is BasePluginFolder || this is Channel)
+ {
+ return true;
+ }
+
+ if (this is IHasCollectionType view)
+ {
+ if (string.Equals(view.CollectionType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ if (GetParent() is AggregateFolder)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ [JsonIgnore]
+ public virtual bool SupportsAncestors => true;
+
+ [JsonIgnore]
+ public virtual bool StopRefreshIfLocalMetadataFound => true;
+
+ [JsonIgnore]
+ protected virtual bool SupportsOwnedItems => !ParentId.Equals(Guid.Empty) && IsFileProtocol;
+
+ [JsonIgnore]
+ public virtual bool SupportsPeople => false;
+
+ [JsonIgnore]
+ public virtual bool SupportsThemeMedia => false;
+
+ [JsonIgnore]
+ public virtual bool SupportsInheritedParentImages => false;
+
+ ///
+ /// Gets a value indicating whether this instance is folder.
+ ///
+ /// true if this instance is folder; otherwise, false.
+ [JsonIgnore]
+ public virtual bool IsFolder => false;
+
+ [JsonIgnore]
+ public virtual bool IsDisplayedAsFolder => false;
+
+ ///
+ /// Gets or sets the remote trailers.
+ ///
+ /// The remote trailers.
+ public IReadOnlyList RemoteTrailers { get; set; }
+
+ public virtual bool SupportsExternalTransfer => false;
+
+ public virtual double GetDefaultPrimaryImageAspectRatio()
+ {
+ return 0;
+ }
+
+ public virtual string CreatePresentationUniqueKey()
+ {
+ return Id.ToString("N", CultureInfo.InvariantCulture);
+ }
+
+ public bool IsPathProtocol(MediaProtocol protocol)
+ {
+ var current = PathProtocol;
+
+ return current.HasValue && current.Value == protocol;
+ }
+
+ private List> GetSortChunks(string s1)
+ {
+ var list = new List>();
+
+ int thisMarker = 0;
+
+ while (thisMarker < s1.Length)
+ {
+ char thisCh = s1[thisMarker];
+
+ var thisChunk = new StringBuilder();
+ bool isNumeric = char.IsDigit(thisCh);
+
+ while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric)
+ {
+ thisChunk.Append(thisCh);
+ thisMarker++;
+
+ if (thisMarker < s1.Length)
+ {
+ thisCh = s1[thisMarker];
+ }
+ }
+
+ list.Add(new Tuple(thisChunk, isNumeric));
+ }
+
+ return list;
+ }
+
+ public virtual bool CanDelete()
+ {
+ if (SourceType == SourceType.Channel)
+ {
+ return ChannelManager.CanDelete(this);
+ }
+
+ return IsFileProtocol;
+ }
+
+ public virtual bool IsAuthorizedToDelete(User user, List allCollectionFolders)
+ {
+ if (user.HasPermission(PermissionKind.EnableContentDeletion))
+ {
+ return true;
+ }
+
+ var allowed = user.GetPreferenceValues(PreferenceKind.EnableContentDeletionFromFolders);
+
+ if (SourceType == SourceType.Channel)
+ {
+ return allowed.Contains(ChannelId);
+ }
+ else
+ {
+ var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders);
+
+ foreach (var folder in collectionFolders)
+ {
+ if (allowed.Contains(folder.Id))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public BaseItem GetOwner()
+ {
+ var ownerId = OwnerId;
+ return ownerId.Equals(Guid.Empty) ? null : LibraryManager.GetItemById(ownerId);
+ }
+
+ public bool CanDelete(User user, List allCollectionFolders)
+ {
+ return CanDelete() && IsAuthorizedToDelete(user, allCollectionFolders);
+ }
+
+ public bool CanDelete(User user)
+ {
+ var allCollectionFolders = LibraryManager.GetUserRootFolder().Children.OfType().ToList();
+
+ return CanDelete(user, allCollectionFolders);
+ }
+
+ public virtual bool CanDownload()
+ {
+ return false;
+ }
+
+ public virtual bool IsAuthorizedToDownload(User user)
+ {
+ return user.HasPermission(PermissionKind.EnableContentDownloading);
+ }
+
+ public bool CanDownload(User user)
+ {
+ return CanDownload() && IsAuthorizedToDownload(user);
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// A that represents this instance.
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ public string GetInternalMetadataPath()
+ {
+ var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
+
+ return GetInternalMetadataPath(basePath);
+ }
+
+ protected virtual string GetInternalMetadataPath(string basePath)
+ {
+ if (SourceType == SourceType.Channel)
+ {
+ return System.IO.Path.Join(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture));
+ }
+
+ ReadOnlySpan idString = Id.ToString("N", CultureInfo.InvariantCulture);
+
+ return System.IO.Path.Join(basePath, "library", idString.Slice(0, 2), idString);
+ }
+
+ ///
+ /// Creates the name of the sort.
+ ///
+ /// System.String.
+ protected virtual string CreateSortName()
+ {
+ if (Name == null)
+ {
+ return null; // some items may not have name filled in properly
+ }
+
+ if (!EnableAlphaNumericSorting)
+ {
+ return Name.TrimStart();
+ }
+
+ var sortable = Name.Trim().ToLowerInvariant();
+
+ foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters)
+ {
+ sortable = sortable.Replace(removeChar, string.Empty, StringComparison.Ordinal);
+ }
+
+ foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters)
+ {
+ sortable = sortable.Replace(replaceChar, " ", StringComparison.Ordinal);
+ }
+
+ foreach (var search in ConfigurationManager.Configuration.SortRemoveWords)
+ {
+ // Remove from beginning if a space follows
+ if (sortable.StartsWith(search + " ", StringComparison.Ordinal))
+ {
+ sortable = sortable.Remove(0, search.Length + 1);
+ }
+
+ // Remove from middle if surrounded by spaces
+ sortable = sortable.Replace(" " + search + " ", " ", StringComparison.Ordinal);
+
+ // Remove from end if followed by a space
+ if (sortable.EndsWith(" " + search, StringComparison.Ordinal))
+ {
+ sortable = sortable.Remove(sortable.Length - (search.Length + 1));
+ }
+ }
+
+ return ModifySortChunks(sortable);
+ }
+
+ private string ModifySortChunks(string name)
+ {
+ var chunks = GetSortChunks(name);
+
+ var builder = new StringBuilder();
+
+ foreach (var chunk in chunks)
+ {
+ var chunkBuilder = chunk.Item1;
+
+ // This chunk is numeric
+ if (chunk.Item2)
+ {
+ while (chunkBuilder.Length < 10)
+ {
+ chunkBuilder.Insert(0, '0');
+ }
+ }
+
+ builder.Append(chunkBuilder);
+ }
- [JsonIgnore]
- public virtual bool HasLocalAlternateVersions => false;
+ // logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
+ return builder.ToString().RemoveDiacritics();
+ }
- [JsonIgnore]
- public string OfficialRatingForComparison
+ public BaseItem GetParent()
{
- get
+ var parentId = ParentId;
+ if (!parentId.Equals(Guid.Empty))
{
- var officialRating = OfficialRating;
- if (!string.IsNullOrEmpty(officialRating))
- {
- return officialRating;
- }
+ return LibraryManager.GetItemById(parentId);
+ }
- var parent = DisplayParent;
- if (parent != null)
- {
- return parent.OfficialRatingForComparison;
- }
+ return null;
+ }
- return null;
+ public IEnumerable GetParents()
+ {
+ var parent = GetParent();
+
+ while (parent != null)
+ {
+ yield return parent;
+
+ parent = parent.GetParent();
}
}
- [JsonIgnore]
- public string CustomRatingForComparison
+ ///
+ /// Finds a parent of a given type.
+ ///
+ /// Type of parent.
+ /// ``0.
+ public T FindParent()
+ where T : Folder
{
- get
+ foreach (var parent in GetParents())
{
- var customRating = CustomRating;
- if (!string.IsNullOrEmpty(customRating))
- {
- return customRating;
- }
-
- var parent = DisplayParent;
- if (parent != null)
+ if (parent is T item)
{
- return parent.CustomRatingForComparison;
+ return item;
}
-
- return null;
}
+
+ return null;
}
///
@@ -1405,14 +1515,46 @@ namespace MediaBrowser.Controller.Entities
}
}
- [JsonIgnore]
- protected virtual bool SupportsOwnedItems => !ParentId.Equals(Guid.Empty) && IsFileProtocol;
+ protected bool IsVisibleStandaloneInternal(User user, bool checkFolders)
+ {
+ if (!IsVisible(user))
+ {
+ return false;
+ }
- [JsonIgnore]
- public virtual bool SupportsPeople => false;
+ if (GetParents().Any(i => !i.IsVisible(user)))
+ {
+ return false;
+ }
- [JsonIgnore]
- public virtual bool SupportsThemeMedia => false;
+ if (checkFolders)
+ {
+ var topParent = GetParents().LastOrDefault() ?? this;
+
+ if (string.IsNullOrEmpty(topParent.Path))
+ {
+ return true;
+ }
+
+ var itemCollectionFolders = LibraryManager.GetCollectionFolders(this).Select(i => i.Id).ToList();
+
+ if (itemCollectionFolders.Count > 0)
+ {
+ var userCollectionFolders = LibraryManager.GetUserRootFolder().GetChildren(user, true).Select(i => i.Id).ToList();
+ if (!itemCollectionFolders.Any(userCollectionFolders.Contains))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void SetParent(Folder parent)
+ {
+ ParentId = parent == null ? Guid.Empty : parent.Id;
+ }
///
/// Refreshes owned items such as trailers, theme videos, special features, etc.
@@ -1609,29 +1751,6 @@ namespace MediaBrowser.Controller.Entities
return themeSongsChanged;
}
- ///
- /// Gets or sets the provider ids.
- ///
- /// The provider ids.
- [JsonIgnore]
- public Dictionary ProviderIds { get; set; }
-
- [JsonIgnore]
- public virtual Folder LatestItemsIndexContainer => null;
-
- public virtual double GetDefaultPrimaryImageAspectRatio()
- {
- return 0;
- }
-
- public virtual string CreatePresentationUniqueKey()
- {
- return Id.ToString("N", CultureInfo.InvariantCulture);
- }
-
- [JsonIgnore]
- public string PresentationUniqueKey { get; set; }
-
public string GetPresentationUniqueKey()
{
return PresentationUniqueKey ?? CreatePresentationUniqueKey();
@@ -1929,55 +2048,6 @@ namespace MediaBrowser.Controller.Entities
return IsVisibleStandaloneInternal(user, true);
}
- [JsonIgnore]
- public virtual bool SupportsInheritedParentImages => false;
-
- protected bool IsVisibleStandaloneInternal(User user, bool checkFolders)
- {
- if (!IsVisible(user))
- {
- return false;
- }
-
- if (GetParents().Any(i => !i.IsVisible(user)))
- {
- return false;
- }
-
- if (checkFolders)
- {
- var topParent = GetParents().LastOrDefault() ?? this;
-
- if (string.IsNullOrEmpty(topParent.Path))
- {
- return true;
- }
-
- var itemCollectionFolders = LibraryManager.GetCollectionFolders(this).Select(i => i.Id).ToList();
-
- if (itemCollectionFolders.Count > 0)
- {
- var userCollectionFolders = LibraryManager.GetUserRootFolder().GetChildren(user, true).Select(i => i.Id).ToList();
- if (!itemCollectionFolders.Any(userCollectionFolders.Contains))
- {
- return false;
- }
- }
- }
-
- return true;
- }
-
- ///
- /// Gets a value indicating whether this instance is folder.
- ///
- /// true if this instance is folder; otherwise, false.
- [JsonIgnore]
- public virtual bool IsFolder => false;
-
- [JsonIgnore]
- public virtual bool IsDisplayedAsFolder => false;
-
public virtual string GetClientTypeName()
{
if (IsFolder && SourceType == SourceType.Channel && !(this is Channel))
@@ -2066,14 +2136,11 @@ namespace MediaBrowser.Controller.Entities
return null;
}
- [JsonIgnore]
- public virtual bool EnableRememberingTrackSelections => true;
-
///
/// Adds a studio to the item.
///
/// The name.
- ///
+ /// Throws if name is null.
public void AddStudio(string name)
{
if (string.IsNullOrEmpty(name))
@@ -2109,7 +2176,7 @@ namespace MediaBrowser.Controller.Entities
/// Adds a genre to the item.
///
/// The name.
- ///
+ /// Throwns if name is null.
public void AddGenre(string name)
{
if (string.IsNullOrEmpty(name))
@@ -2132,8 +2199,7 @@ namespace MediaBrowser.Controller.Entities
/// The user.
/// The date played.
/// if set to true [reset position].
- /// Task.
- ///
+ /// Throws if user is null.
public virtual void MarkPlayed(
User user,
DateTime? datePlayed,
@@ -2170,8 +2236,7 @@ namespace MediaBrowser.Controller.Entities
/// Marks the unplayed.
///
/// The user.
- /// Task.
- ///
+ /// Throws if user is null.
public virtual void MarkUnplayed(User user)
{
if (user == null)
@@ -2271,6 +2336,7 @@ namespace MediaBrowser.Controller.Entities
///
/// The type.
/// The index.
+ /// A task.
public async Task DeleteImageAsync(ImageType type, int index)
{
var info = GetImageInfo(type, index);
@@ -2308,6 +2374,8 @@ namespace MediaBrowser.Controller.Entities
///
/// Validates that images within the item are still on the filesystem.
///
+ /// The directory service to use.
+ /// true if the images validate, false if not.
public bool ValidateImages(IDirectoryService directoryService)
{
var allFiles = ImageInfos
@@ -2335,7 +2403,6 @@ namespace MediaBrowser.Controller.Entities
/// Type of the image.
/// Index of the image.
/// System.String.
- ///
/// Item is null.
public string GetImagePath(ImageType imageType, int imageIndex)
=> GetImageInfo(imageType, imageIndex)?.Path;
@@ -2821,39 +2888,6 @@ namespace MediaBrowser.Controller.Entities
return GetParents().FirstOrDefault(parent => parent.IsTopParent);
}
- [JsonIgnore]
- public virtual bool IsTopParent
- {
- get
- {
- if (this is BasePluginFolder || this is Channel)
- {
- return true;
- }
-
- if (this is IHasCollectionType view)
- {
- if (string.Equals(view.CollectionType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- if (GetParent() is AggregateFolder)
- {
- return true;
- }
-
- return false;
- }
- }
-
- [JsonIgnore]
- public virtual bool SupportsAncestors => true;
-
- [JsonIgnore]
- public virtual bool StopRefreshIfLocalMetadataFound => true;
-
public virtual IEnumerable GetIdsForAncestorQuery()
{
return new[] { Id };
@@ -2888,6 +2922,7 @@ namespace MediaBrowser.Controller.Entities
///
/// Updates the official rating based on content and returns true or false indicating if it changed.
///
+ /// Media children.
/// true if the rating was updated; otherwise false.
public bool UpdateRatingToItems(IList children)
{
@@ -2920,12 +2955,6 @@ namespace MediaBrowser.Controller.Entities
return ThemeVideoIds.Select(LibraryManager.GetItemById);
}
- ///
- /// Gets or sets the remote trailers.
- ///
- /// The remote trailers.
- public IReadOnlyList RemoteTrailers { get; set; }
-
///
/// Get all extras associated with this item, sorted by .
///
@@ -2963,39 +2992,11 @@ namespace MediaBrowser.Controller.Entities
}
}
- public virtual bool IsHD => Height >= 720;
-
- public bool IsShortcut { get; set; }
-
- public string ShortcutPath { get; set; }
-
- public int Width { get; set; }
-
- public int Height { get; set; }
-
- public Guid[] ExtraIds { get; set; }
-
public virtual long GetRunTimeTicksForPlayState()
{
return RunTimeTicks ?? 0;
}
- ///
- /// Extra types that should be counted and displayed as "Special Features" in the UI.
- ///
- public static readonly IReadOnlyCollection DisplayExtraTypes = new HashSet
- {
- Model.Entities.ExtraType.Unknown,
- Model.Entities.ExtraType.BehindTheScenes,
- Model.Entities.ExtraType.Clip,
- Model.Entities.ExtraType.DeletedScene,
- Model.Entities.ExtraType.Interview,
- Model.Entities.ExtraType.Sample,
- Model.Entities.ExtraType.Scene
- };
-
- public virtual bool SupportsExternalTransfer => false;
-
///
public override bool Equals(object obj)
{
diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
index 89ad392a4b..e88121212a 100644
--- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
+++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
@@ -64,6 +64,8 @@ namespace MediaBrowser.Controller.Entities
///
/// The source object.
/// The destination object.
+ /// Source type.
+ /// Destination type.
public static void DeepCopy(this T source, TU dest)
where T : BaseItem
where TU : BaseItem
@@ -109,6 +111,9 @@ namespace MediaBrowser.Controller.Entities
/// Copies all properties on newly created object. Skips properties that do not exist.
///
/// The source object.
+ /// Source type.
+ /// Destination type.
+ /// Destination object.
public static TU DeepCopy(this T source)
where T : BaseItem
where TU : BaseItem, new()
diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
index 1bd25042f2..272a37df1b 100644
--- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs
+++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
@@ -15,6 +15,12 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public virtual string CollectionType => null;
+ [JsonIgnore]
+ public override bool SupportsInheritedParentImages => false;
+
+ [JsonIgnore]
+ public override bool SupportsPeople => false;
+
public override bool CanDelete()
{
return false;
@@ -24,11 +30,5 @@ namespace MediaBrowser.Controller.Entities
{
return true;
}
-
- [JsonIgnore]
- public override bool SupportsInheritedParentImages => false;
-
- [JsonIgnore]
- public override bool SupportsPeople => false;
}
}
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 4f367fe2b5..0fb4771dd3 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -41,6 +41,23 @@ namespace MediaBrowser.Controller.Entities
PhysicalFolderIds = Array.Empty();
}
+ ///
+ /// Gets the display preferences id.
+ ///
+ ///
+ /// Allow different display preferences for each collection folder.
+ ///
+ /// The display prefs id.
+ [JsonIgnore]
+ public override Guid DisplayPreferencesId => Id;
+
+ [JsonIgnore]
+ public override string[] PhysicalLocations => PhysicalLocationsList;
+
+ public string[] PhysicalLocationsList { get; set; }
+
+ public Guid[] PhysicalFolderIds { get; set; }
+
public static IXmlSerializer XmlSerializer { get; set; }
public static IServerApplicationHost ApplicationHost { get; set; }
@@ -63,6 +80,9 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override IEnumerable Children => GetActualChildren();
+ [JsonIgnore]
+ public override bool SupportsPeople => false;
+
public override bool CanDelete()
{
return false;
@@ -160,23 +180,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- ///
- /// Gets the display preferences id.
- ///
- ///
- /// Allow different display preferences for each collection folder.
- ///
- /// The display prefs id.
- [JsonIgnore]
- public override Guid DisplayPreferencesId => Id;
-
- [JsonIgnore]
- public override string[] PhysicalLocations => PhysicalLocationsList;
-
- public string[] PhysicalLocationsList { get; set; }
-
- public Guid[] PhysicalFolderIds { get; set; }
-
public override bool IsSaveLocalMetadataEnabled()
{
return true;
@@ -373,8 +376,5 @@ namespace MediaBrowser.Controller.Entities
return result;
}
-
- [JsonIgnore]
- public override bool SupportsPeople => false;
}
}
diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs
index d8bc0069c7..9ce8eebe34 100644
--- a/MediaBrowser.Controller/Entities/Extensions.cs
+++ b/MediaBrowser.Controller/Entities/Extensions.cs
@@ -15,6 +15,8 @@ namespace MediaBrowser.Controller.Entities
///
/// Adds the trailer URL.
///
+ /// Media item.
+ /// Trailer URL.
public static void AddTrailerUrl(this BaseItem item, string url)
{
if (string.IsNullOrEmpty(url))
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 34ad5bbbb6..d45a02cf2d 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -1669,7 +1669,6 @@ namespace MediaBrowser.Controller.Entities
/// The user.
/// The date played.
/// if set to true [reset position].
- /// Task.
public override void MarkPlayed(
User user,
DateTime? datePlayed,
@@ -1711,7 +1710,6 @@ namespace MediaBrowser.Controller.Entities
/// Marks the unplayed.
///
/// The user.
- /// Task.
public override void MarkUnplayed(User user)
{
var itemsResult = GetItemList(new InternalItemsQuery
diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
index 98c3b3edf6..b11dac381c 100644
--- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs
+++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
@@ -20,6 +20,8 @@ namespace MediaBrowser.Controller.Entities
///
/// Gets the media sources.
///
+ /// true to enable path substitution, false to not.
+ /// A lits of media sources.
List GetMediaSources(bool enablePathSubstitution);
List GetMediaStreams();
diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs
index 2bd9ded337..f4271678d4 100644
--- a/MediaBrowser.Controller/Entities/IHasTrailers.cs
+++ b/MediaBrowser.Controller/Entities/IHasTrailers.cs
@@ -39,6 +39,7 @@ namespace MediaBrowser.Controller.Entities
///
/// Gets the trailer count.
///
+ /// Media item.
/// .
public static int GetTrailerCount(this IHasTrailers item)
=> item.LocalTrailerIds.Count + item.RemoteTrailerIds.Count;
@@ -46,6 +47,7 @@ namespace MediaBrowser.Controller.Entities
///
/// Gets the trailer ids.
///
+ /// Media item.
/// .
public static IReadOnlyList GetTrailerIds(this IHasTrailers item)
{
@@ -70,6 +72,7 @@ namespace MediaBrowser.Controller.Entities
///
/// Gets the trailers.
///
+ /// Media item.
/// .
public static IReadOnlyList GetTrailers(this IHasTrailers item)
{
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index b9e37269e4..045c1b89fd 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -129,6 +129,8 @@ namespace MediaBrowser.Controller.Entities
///
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
+ /// true to replace all metadata, false to not.
+ /// true if changes were made, false if not.
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index 556624e14e..c8feb1c946 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -105,6 +105,8 @@ namespace MediaBrowser.Controller.Entities
///
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
+ /// true to replace all metadata, false to not.
+ /// true if changes were made, false if not.
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 31c179bcac..27c3ff81bd 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -49,12 +49,6 @@ namespace MediaBrowser.Controller.Entities.TV
/// The index number.
public int? IndexNumberEnd { get; set; }
- public string FindSeriesSortName()
- {
- var series = Series;
- return series == null ? SeriesName : series.SortName;
- }
-
[JsonIgnore]
protected override bool SupportsOwnedItems => IsStacked || MediaSourceCount > 1;
@@ -76,45 +70,6 @@ namespace MediaBrowser.Controller.Entities.TV
[JsonIgnore]
protected override bool EnableDefaultVideoUserDataKeys => false;
- public override double GetDefaultPrimaryImageAspectRatio()
- {
- // hack for tv plugins
- if (SourceType == SourceType.Channel)
- {
- return 0;
- }
-
- return 16.0 / 9;
- }
-
- public override List GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- var series = Series;
- if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
- {
- var seriesUserDataKeys = series.GetUserDataKeys();
- var take = seriesUserDataKeys.Count;
- if (seriesUserDataKeys.Count > 1)
- {
- take--;
- }
-
- var newList = seriesUserDataKeys.GetRange(0, take);
- var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture);
- for (int i = 0; i < take; i++)
- {
- newList[i] = newList[i] + suffix;
- }
-
- newList.AddRange(list);
- list = newList;
- }
-
- return list;
- }
-
///
/// Gets the Episode's Series Instance.
///
@@ -161,6 +116,74 @@ namespace MediaBrowser.Controller.Entities.TV
[JsonIgnore]
public string SeasonName { get; set; }
+ [JsonIgnore]
+ public override bool SupportsRemoteImageDownloading
+ {
+ get
+ {
+ if (IsMissingEpisode)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ [JsonIgnore]
+ public bool IsMissingEpisode => LocationType == LocationType.Virtual;
+
+ [JsonIgnore]
+ public Guid SeasonId { get; set; }
+
+ [JsonIgnore]
+ public Guid SeriesId { get; set; }
+
+ public string FindSeriesSortName()
+ {
+ var series = Series;
+ return series == null ? SeriesName : series.SortName;
+ }
+
+ public override double GetDefaultPrimaryImageAspectRatio()
+ {
+ // hack for tv plugins
+ if (SourceType == SourceType.Channel)
+ {
+ return 0;
+ }
+
+ return 16.0 / 9;
+ }
+
+ public override List GetUserDataKeys()
+ {
+ var list = base.GetUserDataKeys();
+
+ var series = Series;
+ if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
+ {
+ var seriesUserDataKeys = series.GetUserDataKeys();
+ var take = seriesUserDataKeys.Count;
+ if (seriesUserDataKeys.Count > 1)
+ {
+ take--;
+ }
+
+ var newList = seriesUserDataKeys.GetRange(0, take);
+ var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture);
+ for (int i = 0; i < take; i++)
+ {
+ newList[i] = newList[i] + suffix;
+ }
+
+ newList.AddRange(list);
+ list = newList;
+ }
+
+ return list;
+ }
+
public string FindSeriesPresentationUniqueKey()
{
var series = Series;
@@ -242,29 +265,6 @@ namespace MediaBrowser.Controller.Entities.TV
return false;
}
- [JsonIgnore]
- public override bool SupportsRemoteImageDownloading
- {
- get
- {
- if (IsMissingEpisode)
- {
- return false;
- }
-
- return true;
- }
- }
-
- [JsonIgnore]
- public bool IsMissingEpisode => LocationType == LocationType.Virtual;
-
- [JsonIgnore]
- public Guid SeasonId { get; set; }
-
- [JsonIgnore]
- public Guid SeriesId { get; set; }
-
public Guid FindSeriesId()
{
var series = FindParent();
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index aa62bb35b0..926c7b0459 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -38,6 +38,50 @@ namespace MediaBrowser.Controller.Entities.TV
[JsonIgnore]
public override Guid DisplayParentId => SeriesId;
+ ///
+ /// Gets this Episode's Series Instance.
+ ///
+ /// The series.
+ [JsonIgnore]
+ public Series Series
+ {
+ get
+ {
+ var seriesId = SeriesId;
+ if (seriesId == Guid.Empty)
+ {
+ seriesId = FindSeriesId();
+ }
+
+ return seriesId == Guid.Empty ? null : (LibraryManager.GetItemById(seriesId) as Series);
+ }
+ }
+
+ [JsonIgnore]
+ public string SeriesPath
+ {
+ get
+ {
+ var series = Series;
+
+ if (series != null)
+ {
+ return series.Path;
+ }
+
+ return System.IO.Path.GetDirectoryName(Path);
+ }
+ }
+
+ [JsonIgnore]
+ public string SeriesPresentationUniqueKey { get; set; }
+
+ [JsonIgnore]
+ public string SeriesName { get; set; }
+
+ [JsonIgnore]
+ public Guid SeriesId { get; set; }
+
public override double GetDefaultPrimaryImageAspectRatio()
{
double value = 2;
@@ -80,41 +124,6 @@ namespace MediaBrowser.Controller.Entities.TV
return result;
}
- ///
- /// Gets this Episode's Series Instance.
- ///
- /// The series.
- [JsonIgnore]
- public Series Series
- {
- get
- {
- var seriesId = SeriesId;
- if (seriesId == Guid.Empty)
- {
- seriesId = FindSeriesId();
- }
-
- return seriesId == Guid.Empty ? null : (LibraryManager.GetItemById(seriesId) as Series);
- }
- }
-
- [JsonIgnore]
- public string SeriesPath
- {
- get
- {
- var series = Series;
-
- if (series != null)
- {
- return series.Path;
- }
-
- return System.IO.Path.GetDirectoryName(Path);
- }
- }
-
public override string CreatePresentationUniqueKey()
{
if (IndexNumber.HasValue)
@@ -157,6 +166,9 @@ namespace MediaBrowser.Controller.Entities.TV
///
/// Gets the episodes.
///
+ /// The user.
+ /// The options to use.
+ /// Set of episodes.
public List GetEpisodes(User user, DtoOptions options)
{
return GetEpisodes(Series, user, options);
@@ -193,15 +205,6 @@ namespace MediaBrowser.Controller.Entities.TV
return UnratedItem.Series;
}
- [JsonIgnore]
- public string SeriesPresentationUniqueKey { get; set; }
-
- [JsonIgnore]
- public string SeriesName { get; set; }
-
- [JsonIgnore]
- public Guid SeriesId { get; set; }
-
public string FindSeriesPresentationUniqueKey()
{
var series = Series;
@@ -241,6 +244,7 @@ namespace MediaBrowser.Controller.Entities.TV
///
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
+ /// true to replace metdata, false to not.
/// true if XXXX, false otherwise.
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 44d07b4a48..beda504b91 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -72,6 +72,9 @@ namespace MediaBrowser.Controller.Entities.TV
/// The status.
public SeriesStatus? Status { get; set; }
+ [JsonIgnore]
+ public override bool StopRefreshIfLocalMetadataFound => false;
+
public override double GetDefaultPrimaryImageAspectRatio()
{
double value = 2;
@@ -394,6 +397,10 @@ namespace MediaBrowser.Controller.Entities.TV
///
/// Filters the episodes by season.
///
+ /// The episodes.
+ /// The season.
+ /// true to include special, false to not.
+ /// The set of episodes.
public static IEnumerable FilterEpisodesBySeason(IEnumerable episodes, Season parentSeason, bool includeSpecials)
{
var seasonNumber = parentSeason.IndexNumber;
@@ -424,6 +431,10 @@ namespace MediaBrowser.Controller.Entities.TV
///
/// Filters the episodes by season.
///
+ /// The episodes.
+ /// The season.
+ /// true to include special, false to not.
+ /// The set of episodes.
public static IEnumerable FilterEpisodesBySeason(IEnumerable episodes, int seasonNumber, bool includeSpecials)
{
if (!includeSpecials || seasonNumber < 1)
@@ -499,8 +510,5 @@ namespace MediaBrowser.Controller.Entities.TV
return list;
}
-
- [JsonIgnore]
- public override bool StopRefreshIfLocalMetadataFound => false;
}
}
diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs
index 6ab2116d73..9179eae939 100644
--- a/MediaBrowser.Controller/Entities/UserItemData.cs
+++ b/MediaBrowser.Controller/Entities/UserItemData.cs
@@ -12,6 +12,13 @@ namespace MediaBrowser.Controller.Entities
///
public class UserItemData
{
+ ///
+ /// The _rating.
+ ///
+ private double? _rating;
+
+ public const double MinLikeValue = 6.5;
+
///
/// Gets or sets the user id.
///
@@ -24,11 +31,6 @@ namespace MediaBrowser.Controller.Entities
/// The key.
public string Key { get; set; }
- ///
- /// The _rating.
- ///
- private double? _rating;
-
///
/// Gets or sets the users 0-10 rating.
///
@@ -93,8 +95,6 @@ namespace MediaBrowser.Controller.Entities
/// The index of the subtitle stream.
public int? SubtitleStreamIndex { get; set; }
- public const double MinLikeValue = 6.5;
-
///
/// Gets or sets a value indicating whether the item is liked or not.
/// This should never be serialized.
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 2b15a52f09..f3bf4749d2 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -21,8 +21,28 @@ namespace MediaBrowser.Controller.Entities
///
public class UserRootFolder : Folder
{
- private List _childrenIds = null;
private readonly object _childIdsLock = new object();
+ private List _childrenIds = null;
+
+ [JsonIgnore]
+ public override bool SupportsInheritedParentImages => false;
+
+ [JsonIgnore]
+ public override bool SupportsPlayedStatus => false;
+
+ [JsonIgnore]
+ protected override bool SupportsShortcutChildren => true;
+
+ [JsonIgnore]
+ public override bool IsPreSorted => true;
+
+ private void ClearCache()
+ {
+ lock (_childIdsLock)
+ {
+ _childrenIds = null;
+ }
+ }
protected override List LoadChildren()
{
@@ -39,20 +59,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- [JsonIgnore]
- public override bool SupportsInheritedParentImages => false;
-
- [JsonIgnore]
- public override bool SupportsPlayedStatus => false;
-
- private void ClearCache()
- {
- lock (_childIdsLock)
- {
- _childrenIds = null;
- }
- }
-
protected override QueryResult GetItemsInternal(InternalItemsQuery query)
{
if (query.Recursive)
@@ -74,12 +80,6 @@ namespace MediaBrowser.Controller.Entities
return GetChildren(user, true).Count;
}
- [JsonIgnore]
- protected override bool SupportsShortcutChildren => true;
-
- [JsonIgnore]
- public override bool IsPreSorted => true;
-
protected override IEnumerable GetEligibleChildrenForRecursiveChildren(User user)
{
var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index d05b5df2f1..7dd95b85cf 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -28,6 +28,14 @@ namespace MediaBrowser.Controller.Entities
ISupportsPlaceHolders,
IHasMediaSources
{
+ public Video()
+ {
+ AdditionalParts = Array.Empty();
+ LocalAlternateVersions = Array.Empty();
+ SubtitleFiles = Array.Empty();
+ LinkedAlternateVersions = Array.Empty();
+ }
+
[JsonIgnore]
public string PrimaryVersionId { get; set; }
@@ -74,30 +82,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- public void SetPrimaryVersionId(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- PrimaryVersionId = null;
- }
- else
- {
- PrimaryVersionId = id;
- }
-
- PresentationUniqueKey = CreatePresentationUniqueKey();
- }
-
- public override string CreatePresentationUniqueKey()
- {
- if (!string.IsNullOrEmpty(PrimaryVersionId))
- {
- return PrimaryVersionId;
- }
-
- return base.CreatePresentationUniqueKey();
- }
-
[JsonIgnore]
public override bool SupportsThemeMedia => true;
@@ -151,24 +135,6 @@ namespace MediaBrowser.Controller.Entities
/// The aspect ratio.
public string AspectRatio { get; set; }
- public Video()
- {
- AdditionalParts = Array.Empty();
- LocalAlternateVersions = Array.Empty();
- SubtitleFiles = Array.Empty();
- LinkedAlternateVersions = Array.Empty();
- }
-
- public override bool CanDownload()
- {
- if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
- {
- return false;
- }
-
- return IsFileProtocol;
- }
-
[JsonIgnore]
public override bool SupportsAddingToPlaylist => true;
@@ -196,16 +162,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override bool HasLocalAlternateVersions => LocalAlternateVersions.Length > 0;
- public IEnumerable GetAdditionalPartIds()
- {
- return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
- }
-
- public IEnumerable GetLocalAlternateVersionIds()
- {
- return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
- }
-
public static ILiveTvManager LiveTvManager { get; set; }
[JsonIgnore]
@@ -222,37 +178,77 @@ namespace MediaBrowser.Controller.Entities
}
}
- protected override bool IsActiveRecording()
+ [JsonIgnore]
+ public bool IsCompleteMedia
{
- return LiveTvManager.GetActiveRecordingInfo(Path) != null;
+ get
+ {
+ if (SourceType == SourceType.Channel)
+ {
+ return !Tags.Contains("livestream", StringComparer.OrdinalIgnoreCase);
+ }
+
+ return !IsActiveRecording();
+ }
}
- public override bool CanDelete()
+ [JsonIgnore]
+ protected virtual bool EnableDefaultVideoUserDataKeys => true;
+
+ [JsonIgnore]
+ public override string ContainingFolderPath
{
- if (IsActiveRecording())
+ get
{
- return false;
- }
+ if (IsStacked)
+ {
+ return System.IO.Path.GetDirectoryName(Path);
+ }
- return base.CanDelete();
+ if (!IsPlaceHolder)
+ {
+ if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
+ {
+ return Path;
+ }
+ }
+
+ return base.ContainingFolderPath;
+ }
}
[JsonIgnore]
- public bool IsCompleteMedia
+ public override string FileNameWithoutExtension
{
get
{
- if (SourceType == SourceType.Channel)
+ if (IsFileProtocol)
{
- return !Tags.Contains("livestream", StringComparer.OrdinalIgnoreCase);
+ if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
+ {
+ return System.IO.Path.GetFileName(Path);
+ }
+
+ return System.IO.Path.GetFileNameWithoutExtension(Path);
}
- return !IsActiveRecording();
+ return null;
}
}
+ ///
+ /// Gets a value indicating whether [is3 D].
+ ///
+ /// true if [is3 D]; otherwise, false.
[JsonIgnore]
- protected virtual bool EnableDefaultVideoUserDataKeys => true;
+ public bool Is3D => Video3DFormat.HasValue;
+
+ ///
+ /// Gets the type of the media.
+ ///
+ /// The type of the media.
+ [JsonIgnore]
+ public override string MediaType => Model.Entities.MediaType.Video;
public override List GetUserDataKeys()
{
@@ -293,6 +289,65 @@ namespace MediaBrowser.Controller.Entities
return list;
}
+ public void SetPrimaryVersionId(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ PrimaryVersionId = null;
+ }
+ else
+ {
+ PrimaryVersionId = id;
+ }
+
+ PresentationUniqueKey = CreatePresentationUniqueKey();
+ }
+
+ public override string CreatePresentationUniqueKey()
+ {
+ if (!string.IsNullOrEmpty(PrimaryVersionId))
+ {
+ return PrimaryVersionId;
+ }
+
+ return base.CreatePresentationUniqueKey();
+ }
+
+ public override bool CanDownload()
+ {
+ if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
+ {
+ return false;
+ }
+
+ return IsFileProtocol;
+ }
+
+ protected override bool IsActiveRecording()
+ {
+ return LiveTvManager.GetActiveRecordingInfo(Path) != null;
+ }
+
+ public override bool CanDelete()
+ {
+ if (IsActiveRecording())
+ {
+ return false;
+ }
+
+ return base.CanDelete();
+ }
+
+ public IEnumerable GetAdditionalPartIds()
+ {
+ return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
+ }
+
+ public IEnumerable GetLocalAlternateVersionIds()
+ {
+ return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
+ }
+
private string GetUserDataKey(string providerId)
{
var key = providerId + "-" + ExtraType.ToString().ToLowerInvariant();
@@ -328,47 +383,6 @@ namespace MediaBrowser.Controller.Entities
.OrderBy(i => i.SortName);
}
- [JsonIgnore]
- public override string ContainingFolderPath
- {
- get
- {
- if (IsStacked)
- {
- return System.IO.Path.GetDirectoryName(Path);
- }
-
- if (!IsPlaceHolder)
- {
- if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
- {
- return Path;
- }
- }
-
- return base.ContainingFolderPath;
- }
- }
-
- [JsonIgnore]
- public override string FileNameWithoutExtension
- {
- get
- {
- if (IsFileProtocol)
- {
- if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
- {
- return System.IO.Path.GetFileName(Path);
- }
-
- return System.IO.Path.GetFileNameWithoutExtension(Path);
- }
-
- return null;
- }
- }
-
internal override ItemUpdateType UpdateFromResolvedItem(BaseItem newItem)
{
var updateType = base.UpdateFromResolvedItem(newItem);
@@ -397,20 +411,6 @@ namespace MediaBrowser.Controller.Entities
return updateType;
}
- ///
- /// Gets a value indicating whether [is3 D].
- ///
- /// true if [is3 D]; otherwise, false.
- [JsonIgnore]
- public bool Is3D => Video3DFormat.HasValue;
-
- ///
- /// Gets the type of the media.
- ///
- /// The type of the media.
- [JsonIgnore]
- public override string MediaType => Model.Entities.MediaType.Video;
-
protected override async Task RefreshedOwnedItems(MetadataRefreshOptions options, List fileSystemChildren, CancellationToken cancellationToken)
{
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
index d8995ce74a..0813a8e7d5 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -1,6 +1,6 @@
#nullable disable
-#pragma warning disable CS1591
+#pragma warning disable CS1591, SA1306, SA1401
using System;
using System.Collections.Generic;
@@ -30,6 +30,21 @@ namespace MediaBrowser.Controller.Net
private readonly List> _activeConnections =
new List>();
+ ///
+ /// The logger.
+ ///
+ protected ILogger> Logger;
+
+ protected BasePeriodicWebSocketListener(ILogger> logger)
+ {
+ if (logger == null)
+ {
+ throw new ArgumentNullException(nameof(logger));
+ }
+
+ Logger = logger;
+ }
+
///
/// Gets the type used for the messages sent to the client.
///
@@ -54,21 +69,6 @@ namespace MediaBrowser.Controller.Net
/// Task{`1}.
protected abstract Task GetDataToSend();
- ///
- /// The logger.
- ///
- protected ILogger> Logger;
-
- protected BasePeriodicWebSocketListener(ILogger> logger)
- {
- if (logger == null)
- {
- throw new ArgumentNullException(nameof(logger));
- }
-
- Logger = logger;
- }
-
///
/// Processes the message.
///
diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
index 5fa5834c85..c43acfb6de 100644
--- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
@@ -18,7 +18,6 @@ namespace MediaBrowser.Controller.Persistence
/// The key.
/// The user data.
/// The cancellation token.
- /// Task.
void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken);
///
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 3eaf235152..5e671a725d 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -31,24 +31,18 @@ namespace MediaBrowser.Controller.Playlists
".zpl"
};
- public Guid OwnerUserId { get; set; }
-
- public Share[] Shares { get; set; }
-
public Playlist()
{
Shares = Array.Empty();
}
+ public Guid OwnerUserId { get; set; }
+
+ public Share[] Shares { get; set; }
+
[JsonIgnore]
public bool IsFile => IsPlaylistFile(Path);
- public static bool IsPlaylistFile(string path)
- {
- // The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot).
- return System.IO.Path.HasExtension(path) && !Directory.Exists(path);
- }
-
[JsonIgnore]
public override string ContainingFolderPath
{
@@ -80,6 +74,41 @@ namespace MediaBrowser.Controller.Playlists
[JsonIgnore]
public override bool SupportsCumulativeRunTimeTicks => true;
+ [JsonIgnore]
+ public override bool IsPreSorted => true;
+
+ public string PlaylistMediaType { get; set; }
+
+ [JsonIgnore]
+ public override string MediaType => PlaylistMediaType;
+
+ [JsonIgnore]
+ private bool IsSharedItem
+ {
+ get
+ {
+ var path = Path;
+
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+
+ return FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, path);
+ }
+ }
+
+ public static bool IsPlaylistFile(string path)
+ {
+ // The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot).
+ return System.IO.Path.HasExtension(path) && !Directory.Exists(path);
+ }
+
+ public void SetMediaType(string value)
+ {
+ PlaylistMediaType = value;
+ }
+
public override double GetDefaultPrimaryImageAspectRatio()
{
return 1;
@@ -197,35 +226,6 @@ namespace MediaBrowser.Controller.Playlists
return new[] { item };
}
- [JsonIgnore]
- public override bool IsPreSorted => true;
-
- public string PlaylistMediaType { get; set; }
-
- [JsonIgnore]
- public override string MediaType => PlaylistMediaType;
-
- public void SetMediaType(string value)
- {
- PlaylistMediaType = value;
- }
-
- [JsonIgnore]
- private bool IsSharedItem
- {
- get
- {
- var path = Path;
-
- if (string.IsNullOrEmpty(path))
- {
- return false;
- }
-
- return FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, path);
- }
- }
-
public override bool IsVisible(User user)
{
if (!IsSharedItem)
diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
index 75286eadc0..b95d00aa3c 100644
--- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
@@ -13,18 +13,18 @@ namespace MediaBrowser.Controller.Resolvers
///
public interface IItemResolver
{
+ ///
+ /// Gets the priority.
+ ///
+ /// The priority.
+ ResolverPriority Priority { get; }
+
///
/// Resolves the path.
///
/// The args.
/// BaseItem.
BaseItem ResolvePath(ItemResolveArgs args);
-
- ///
- /// Gets the priority.
- ///
- /// The priority.
- ResolverPriority Priority { get; }
}
public interface IMultiItemResolver
@@ -38,14 +38,14 @@ namespace MediaBrowser.Controller.Resolvers
public class MultiItemResolverResult
{
- public List Items { get; set; }
-
- public List ExtraFiles { get; set; }
-
public MultiItemResolverResult()
{
Items = new List();
ExtraFiles = new List();
}
+
+ public List Items { get; set; }
+
+ public List ExtraFiles { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
index 9e661cbe42..3330dd5408 100644
--- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
+++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
@@ -28,6 +28,11 @@ namespace MediaBrowser.Controller.Subtitles
///
/// Searches the subtitles.
///
+ /// The video.
+ /// Subtitle language.
+ /// Require perfect match.
+ /// CancellationToken to use for the operation.
+ /// Subtitles, wrapped in task.
Task SearchSubtitles(
Video video,
string language,
@@ -47,11 +52,20 @@ namespace MediaBrowser.Controller.Subtitles
///
/// Downloads the subtitles.
///
+ /// The video.
+ /// Subtitle ID.
+ /// CancellationToken to use for the operation.
+ /// A task.
Task DownloadSubtitles(Video video, string subtitleId, CancellationToken cancellationToken);
///
/// Downloads the subtitles.
///
+ /// The video.
+ /// Library options to use.
+ /// Subtitle ID.
+ /// CancellationToken to use for the operation.
+ /// A task.
Task DownloadSubtitles(Video video, LibraryOptions libraryOptions, string subtitleId, CancellationToken cancellationToken);
///
@@ -73,11 +87,16 @@ namespace MediaBrowser.Controller.Subtitles
///
/// Deletes the subtitles.
///
+ /// Media item.
+ /// Subtitle index.
+ /// A task.
Task DeleteSubtitles(BaseItem item, int index);
///
/// Gets the providers.
///
+ /// The media item.
+ /// Subtitles providers.
SubtitleProviderInfo[] GetSupportedProviders(BaseItem item);
}
}
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
index 0f7c47e767..767d87d465 100644
--- a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
+++ b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
@@ -11,6 +11,15 @@ namespace MediaBrowser.Controller.Subtitles
{
public class SubtitleSearchRequest : IHasProviderIds
{
+ public SubtitleSearchRequest()
+ {
+ SearchAllProviders = true;
+ ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ DisabledSubtitleFetchers = Array.Empty();
+ SubtitleFetcherOrder = Array.Empty();
+ }
+
public string Language { get; set; }
public string TwoLetterISOLanguageName { get; set; }
@@ -42,14 +51,5 @@ namespace MediaBrowser.Controller.Subtitles
public string[] DisabledSubtitleFetchers { get; set; }
public string[] SubtitleFetcherOrder { get; set; }
-
- public SubtitleSearchRequest()
- {
- SearchAllProviders = true;
- ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- DisabledSubtitleFetchers = Array.Empty();
- SubtitleFetcherOrder = Array.Empty();
- }
}
}
From 2345646ff159f474588e24c29ab94a97a4f41e6d Mon Sep 17 00:00:00 2001
From: boolemancer
Date: Sat, 14 Aug 2021 02:55:51 -0700
Subject: [PATCH 037/118] Fix explicit stream selection in
MediaEncoder.ExtractImageInternal
---
.../Encoder/MediaEncoder.cs | 12 ++----------
.../MediaInfo/VideoImageProvider.cs | 18 +-----------------
2 files changed, 3 insertions(+), 27 deletions(-)
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 412a953216..e0a8102f92 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -503,15 +503,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var inputArgument = GetInputArgument(inputFile, mediaSource);
- if (isAudio)
- {
- if (imageStreamIndex.HasValue && imageStreamIndex.Value > 0)
- {
- // It seems for audio files we need to subtract 1 (for the audio stream??)
- imageStreamIndex = imageStreamIndex.Value - 1;
- }
- }
- else
+ if (!isAudio)
{
// The failure of HDR extraction usually occurs when using custom ffmpeg that does not contain the zscale filter.
try
@@ -582,7 +574,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_ => string.Empty
};
- var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
+ var mapArg = imageStreamIndex.HasValue ? (" -map 0:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
var enableHdrExtraction = allowTonemap && string.Equals(videoStream?.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase);
if (enableHdrExtraction)
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index 30af6710ab..453938be79 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -88,22 +88,6 @@ namespace MediaBrowser.Providers.MediaInfo
if (imageStream != null)
{
- // Instead of using the raw stream index, we need to use nth video/embedded image stream
- var videoIndex = -1;
- foreach (var mediaStream in mediaStreams)
- {
- if (mediaStream.Type == MediaStreamType.Video ||
- mediaStream.Type == MediaStreamType.EmbeddedImage)
- {
- videoIndex++;
- }
-
- if (mediaStream == imageStream)
- {
- break;
- }
- }
-
MediaSourceInfo mediaSource = new MediaSourceInfo
{
VideoType = item.VideoType,
@@ -111,7 +95,7 @@ namespace MediaBrowser.Providers.MediaInfo
Protocol = item.PathProtocol.Value,
};
- extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, mediaSource, imageStream, videoIndex, cancellationToken).ConfigureAwait(false);
+ extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, mediaSource, imageStream, imageStream.Index, cancellationToken).ConfigureAwait(false);
}
else
{
From 7ee404ec98715ac31d4893c568d62c216d6bedc8 Mon Sep 17 00:00:00 2001
From: artiume
Date: Sat, 14 Aug 2021 06:21:44 -0400
Subject: [PATCH 038/118] Added translation using Weblate (Pirate)
---
Emby.Server.Implementations/Localization/Core/pr.json | 1 +
1 file changed, 1 insertion(+)
create mode 100644 Emby.Server.Implementations/Localization/Core/pr.json
diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/pr.json
@@ -0,0 +1 @@
+{}
From 9b451f357f22b7a45b58128c1b5d0baf580e4812 Mon Sep 17 00:00:00 2001
From: FancyNerd92
Date: Fri, 13 Aug 2021 23:44:57 +0000
Subject: [PATCH 039/118] Translated using Weblate (English) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en/
---
Emby.Server.Implementations/Localization/Core/en-US.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index 65964f6d9f..ca127cdb86 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -17,7 +17,7 @@
"Folders": "Folders",
"Forced": "Forced",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album Artists",
+ "HeaderAlbumArtists": "Artist's Album",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
@@ -27,7 +27,7 @@
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up",
"HeaderRecordingGroups": "Recording Groups",
- "HomeVideos": "Home videos",
+ "HomeVideos": "Home Videos",
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
@@ -41,7 +41,7 @@
"MixedContent": "Mixed content",
"Movies": "Movies",
"Music": "Music",
- "MusicVideos": "Music videos",
+ "MusicVideos": "Music Videos",
"NameInstallFailed": "{0} installation failed",
"NameSeasonNumber": "Season {0}",
"NameSeasonUnknown": "Season Unknown",
From b6691d3fe9cb6a0c76cd92a7ea529dadd1e5f68e Mon Sep 17 00:00:00 2001
From: cotlol
Date: Fri, 13 Aug 2021 11:06:04 +0000
Subject: [PATCH 040/118] Translated using Weblate (Swedish) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/
---
Emby.Server.Implementations/Localization/Core/sv.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index d992bf79b1..88b182f8d9 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -118,5 +118,6 @@
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
"Undefined": "odefinierad",
"Forced": "Tvingad",
- "Default": "Standard"
+ "Default": "Standard",
+ "TaskOptimizeDatabase": "Optimera databasen"
}
From 7eb4da4eefe9ed0699289b273d0ec2e4c8aee86d Mon Sep 17 00:00:00 2001
From: FancyNerd92
Date: Fri, 13 Aug 2021 22:51:38 +0000
Subject: [PATCH 041/118] Translated using Weblate (Greek) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/el/
---
Emby.Server.Implementations/Localization/Core/el.json | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 23d45b4737..834d5e75f2 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -39,7 +39,7 @@
"MixedContent": "Ανάμεικτο Περιεχόμενο",
"Movies": "Ταινίες",
"Music": "Μουσική",
- "MusicVideos": "Μουσικά βίντεο",
+ "MusicVideos": "Μουσικά Βίντεο",
"NameInstallFailed": "{0} η εγκατάσταση απέτυχε",
"NameSeasonNumber": "Κύκλος {0}",
"NameSeasonUnknown": "Άγνωστος Κύκλος",
@@ -62,7 +62,7 @@
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
"Photos": "Φωτογραφίες",
"Playlists": "Λίστες αναπαραγωγής",
- "Plugin": "Plugin",
+ "Plugin": "Πρόσθετο",
"PluginInstalledWithName": "{0} εγκαταστήθηκε",
"PluginUninstalledWithName": "{0} έχει απεγκατασταθεί",
"PluginUpdatedWithName": "{0} έχει αναβαθμιστεί",
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
"Undefined": "Απροσδιόριστο",
"Forced": "Εξαναγκασμένο",
- "Default": "Προεπιλογή"
+ "Default": "Προεπιλογή",
+ "TaskOptimizeDatabaseDescription": "Συμπιέζει τη βάση δεδομένων και δημιουργεί ελεύθερο χώρο. Η εκτέλεση αυτής της εργασίας μετά τη σάρωση της βιβλιοθήκης ή την πραγματοποίηση άλλων αλλαγών που συνεπάγονται τροποποιήσεις της βάσης δεδομένων μπορεί να βελτιώσει την απόδοση.",
+ "TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων"
}
From 585207e05bdec2f2e468b549270a16c73bc2a657 Mon Sep 17 00:00:00 2001
From: Daniel
Date: Thu, 12 Aug 2021 12:07:02 +0000
Subject: [PATCH 042/118] Translated using Weblate (Catalan) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/
---
Emby.Server.Implementations/Localization/Core/ca.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 1b612dc716..7715daa7ce 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -5,7 +5,7 @@
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
"Books": "Llibres",
- "CameraImageUploadedFrom": "Una nova imatge de la càmera ha estat pujada des de {0}",
+ "CameraImageUploadedFrom": "S'ha pujat una nova imatge des de la camera desde {0}",
"Channels": "Canals",
"ChapterNameValue": "Capítol {0}",
"Collections": "Col·leccions",
From c80930c6d417db1c999c986b42de9fe10fa92341 Mon Sep 17 00:00:00 2001
From: dtorner
Date: Sat, 14 Aug 2021 05:39:10 +0000
Subject: [PATCH 043/118] Translated using Weblate (Spanish) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/
---
Emby.Server.Implementations/Localization/Core/es.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 7d42182b0c..d3d9d27038 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -15,7 +15,7 @@
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del álbum",
+ "HeaderAlbumArtists": "Artista del álbum",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
From 890e99725e9222550750d4742557d77e55acb0ee Mon Sep 17 00:00:00 2001
From: Winter <78392041+winterqt@users.noreply.github.com>
Date: Sat, 14 Aug 2021 19:29:31 -0400
Subject: [PATCH 044/118] Disable UseAppHost in portable deployment
---
deployment/build.portable | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/deployment/build.portable b/deployment/build.portable
index ea40ade5d9..a6c7418810 100755
--- a/deployment/build.portable
+++ b/deployment/build.portable
@@ -16,7 +16,7 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=false"
tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version}
rm -rf dist/jellyfin-server_${version}
From e8e0836f95a38b5723136839def75a60a1907afc Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 15 Aug 2021 14:13:20 +0200
Subject: [PATCH 045/118] Fix PasswordHash tests
---
.../Cryptography/PasswordHashTests.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs
index e6c325bac0..18d3f97638 100644
--- a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs
+++ b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs
@@ -171,11 +171,11 @@ namespace Jellyfin.Common.Tests.Cryptography
[InlineData("$PBKDF2$=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter
[InlineData("$PBKDF2$=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter
[InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter
- [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $
- [InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment
- [InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment
- [InlineData("$PBKDF2$iterations=$invalidstalt$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid salt
- [InlineData("$PBKDF2$iterations=$69F420$invalid hash")] // Invalid hash
+ [InlineData("$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $
+ [InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment
+ [InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment
+ [InlineData("$PBKDF2$iterations=1000$invalidstalt$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid salt
+ [InlineData("$PBKDF2$iterations=1000$69F420$invalid hash")] // Invalid hash
[InlineData("$PBKDF2$69F420$")] // Empty hash
public static void Parse_InvalidFormat_ThrowsFormatException(string passwordHash)
{
From 709f8e9faa64d3d51a717be8453b4a57faf371b5 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Sun, 15 Aug 2021 08:30:15 -0600
Subject: [PATCH 046/118] Update to dotnet 5.0.9
---
.../Emby.Server.Implementations.csproj | 2 +-
Jellyfin.Api/Jellyfin.Api.csproj | 2 +-
.../Jellyfin.Server.Implementations.csproj | 8 ++++----
Jellyfin.Server/Jellyfin.Server.csproj | 4 ++--
deployment/Dockerfile.ubuntu.amd64 | 2 +-
deployment/Dockerfile.ubuntu.arm64 | 2 +-
deployment/Dockerfile.ubuntu.armhf | 2 +-
tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +-
.../Jellyfin.Server.Integration.Tests.csproj | 2 +-
tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +-
10 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 4c9e058212..e0f841d529 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -28,7 +28,7 @@
-
+
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index a527282d15..669925198b 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 728f9021dc..a75b285936 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -19,13 +19,13 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 49529b7944..4ad39c60a9 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -33,8 +33,8 @@
-
-
+
+
diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64
index 97e3ff8023..d88efcdc95 100644
--- a/deployment/Dockerfile.ubuntu.amd64
+++ b/deployment/Dockerfile.ubuntu.amd64
@@ -19,7 +19,7 @@ RUN apt-get update -yqq \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/13b9d84c-a35b-4ffe-8f62-447a01403d64/1f9ae31daa0f7d98513e7551246899f2/dotnet-sdk-5.0.400-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 c94ee91dd7..4f41bba2d9 100644
--- a/deployment/Dockerfile.ubuntu.arm64
+++ b/deployment/Dockerfile.ubuntu.arm64
@@ -18,7 +18,7 @@ RUN apt-get update -yqq \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/13b9d84c-a35b-4ffe-8f62-447a01403d64/1f9ae31daa0f7d98513e7551246899f2/dotnet-sdk-5.0.400-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 aaaedda82c..01752d5367 100644
--- a/deployment/Dockerfile.ubuntu.armhf
+++ b/deployment/Dockerfile.ubuntu.armhf
@@ -18,7 +18,7 @@ RUN apt-get update -yqq \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/13b9d84c-a35b-4ffe-8f62-447a01403d64/1f9ae31daa0f7d98513e7551246899f2/dotnet-sdk-5.0.400-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 4edd843841..ecf7d2b364 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
index cf42153393..8f2eb9ef58 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
index 2f95f5c01c..6f3c532cd0 100644
--- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
+++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
@@ -10,7 +10,7 @@
-
+
From 312e2685f8e1c734680ea9872fd31b27694d607d Mon Sep 17 00:00:00 2001
From: Rich Lander
Date: Sun, 15 Aug 2021 10:30:12 -0700
Subject: [PATCH 047/118] Update
MediaBrowser.Controller/Entities/IHasMediaSources.cs
Co-authored-by: Bond-009
---
MediaBrowser.Controller/Entities/IHasMediaSources.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
index b11dac381c..90d9bdd2d3 100644
--- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs
+++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the media sources.
///
/// true to enable path substitution, false to not.
- /// A lits of media sources.
+ /// A list of media sources.
List GetMediaSources(bool enablePathSubstitution);
List GetMediaStreams();
From e7a3552aae556a5afa58207db353f10de9021274 Mon Sep 17 00:00:00 2001
From: Rich Lander
Date: Sun, 15 Aug 2021 10:32:18 -0700
Subject: [PATCH 048/118] Update per feedback
---
MediaBrowser.Controller/Entities/UserItemData.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs
index 9179eae939..50ba9ef308 100644
--- a/MediaBrowser.Controller/Entities/UserItemData.cs
+++ b/MediaBrowser.Controller/Entities/UserItemData.cs
@@ -12,13 +12,13 @@ namespace MediaBrowser.Controller.Entities
///
public class UserItemData
{
+ public const double MinLikeValue = 6.5;
+
///
/// The _rating.
///
private double? _rating;
- public const double MinLikeValue = 6.5;
-
///
/// Gets or sets the user id.
///
From 19824bff94a9f557c3fb1616e1b5031fd125a53a Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 15 Aug 2021 17:20:07 +0200
Subject: [PATCH 049/118] Minor improvements
---
.../Channels/ChannelManager.cs | 2 +-
.../Collections/CollectionManager.cs | 32 +++---
.../Localization/LocalizationManager.cs | 63 +++++------
MediaBrowser.Common/Plugins/BasePluginOfT.cs | 4 +-
.../BaseItemManager/BaseItemManager.cs | 8 +-
.../BaseItemManager/IBaseItemManager.cs | 4 +-
.../Channels/ChannelItemResult.cs | 9 +-
.../Collections/CollectionCreationOptions.cs | 2 +-
.../CollectionModifiedEventArgs.cs | 2 -
.../Collections/ICollectionManager.cs | 8 +-
.../IServerConfigurationManager.cs | 2 -
.../MediaEncoding/EncodingHelper.cs | 3 -
.../Globalization/ILocalizationManager.cs | 11 +-
.../Parsers/BaseNfoParser.cs | 102 +++++++++---------
.../Parsers/EpisodeNfoParser.cs | 92 ++++++++--------
.../Localization/LocalizationManagerTests.cs | 2 +-
16 files changed, 151 insertions(+), 195 deletions(-)
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 093607dd5e..aa54510a71 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -880,7 +880,7 @@ namespace Emby.Server.Implementations.Channels
}
}
- private async Task CacheResponse(object result, string path)
+ private async Task CacheResponse(ChannelItemResult result, string path)
{
try
{
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 08acd17672..8270c2e84c 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -63,13 +61,13 @@ namespace Emby.Server.Implementations.Collections
}
///
- public event EventHandler CollectionCreated;
+ public event EventHandler? CollectionCreated;
///
- public event EventHandler ItemsAddedToCollection;
+ public event EventHandler? ItemsAddedToCollection;
///
- public event EventHandler ItemsRemovedFromCollection;
+ public event EventHandler? ItemsRemovedFromCollection;
private IEnumerable FindFolders(string path)
{
@@ -80,7 +78,7 @@ namespace Emby.Server.Implementations.Collections
.Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
}
- internal async Task EnsureLibraryFolder(string path, bool createIfNeeded)
+ internal async Task EnsureLibraryFolder(string path, bool createIfNeeded)
{
var existingFolder = FindFolders(path).FirstOrDefault();
if (existingFolder != null)
@@ -114,7 +112,7 @@ namespace Emby.Server.Implementations.Collections
return Path.Combine(_appPaths.DataPath, "collections");
}
- private Task GetCollectionsFolder(bool createIfNeeded)
+ private Task GetCollectionsFolder(bool createIfNeeded)
{
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
}
@@ -203,8 +201,7 @@ namespace Emby.Server.Implementations.Collections
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{
- var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
- if (collection == null)
+ if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
{
throw new ArgumentException("No collection exists with the supplied Id");
}
@@ -256,9 +253,7 @@ namespace Emby.Server.Implementations.Collections
///
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable itemIds)
{
- var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
-
- if (collection == null)
+ if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
{
throw new ArgumentException("No collection exists with the supplied Id");
}
@@ -312,11 +307,7 @@ namespace Emby.Server.Implementations.Collections
foreach (var item in items)
{
- if (item is not ISupportsBoxSetGrouping)
- {
- results[item.Id] = item;
- }
- else
+ if (item is ISupportsBoxSetGrouping)
{
var itemId = item.Id;
@@ -340,6 +331,7 @@ namespace Emby.Server.Implementations.Collections
}
var alreadyInResults = false;
+
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
if (item is Video video)
{
@@ -355,11 +347,13 @@ namespace Emby.Server.Implementations.Collections
}
}
- if (!alreadyInResults)
+ if (alreadyInResults)
{
- results[itemId] = item;
+ continue;
}
}
+
+ results[item.Id] = item;
}
return results.Values;
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 9808e47de2..03919197e2 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -38,10 +36,10 @@ namespace Emby.Server.Implementations.Localization
private readonly ConcurrentDictionary> _dictionaries =
new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase);
- private List _cultures;
-
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
+ private List _cultures = new List();
+
///
/// Initializes a new instance of the class.
///
@@ -72,8 +70,8 @@ namespace Emby.Server.Implementations.Localization
string countryCode = resource.Substring(RatingsPath.Length, 2);
var dict = new Dictionary(StringComparer.OrdinalIgnoreCase);
- await using var str = _assembly.GetManifestResourceStream(resource);
- using var reader = new StreamReader(str);
+ await using var stream = _assembly.GetManifestResourceStream(resource);
+ using var reader = new StreamReader(stream!); // shouldn't be null here, we just got the resource path from Assembly.GetManifestResourceNames()
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
{
if (string.IsNullOrWhiteSpace(line))
@@ -113,7 +111,8 @@ namespace Emby.Server.Implementations.Localization
{
List list = new List();
- await using var stream = _assembly.GetManifestResourceStream(CulturesPath);
+ await using var stream = _assembly.GetManifestResourceStream(CulturesPath)
+ ?? throw new InvalidOperationException($"Invalid resource path: '{CulturesPath}'");
using var reader = new StreamReader(stream);
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
{
@@ -162,7 +161,7 @@ namespace Emby.Server.Implementations.Localization
}
///
- public CultureDto FindLanguageInfo(string language)
+ public CultureDto? FindLanguageInfo(string language)
{
// TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs
for (var i = 0; i < _cultures.Count; i++)
@@ -183,9 +182,10 @@ namespace Emby.Server.Implementations.Localization
///
public IEnumerable GetCountries()
{
- using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream(CountriesPath));
-
- return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions);
+ using StreamReader reader = new StreamReader(
+ _assembly.GetManifestResourceStream(CountriesPath) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"));
+ return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions)
+ ?? throw new InvalidOperationException($"Resource contains invalid data: '{CountriesPath}'");
}
///
@@ -205,7 +205,9 @@ namespace Emby.Server.Implementations.Localization
countryCode = "us";
}
- return GetRatings(countryCode) ?? GetRatings("us");
+ return GetRatings(countryCode)
+ ?? GetRatings("us")
+ ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
}
///
@@ -213,7 +215,7 @@ namespace Emby.Server.Implementations.Localization
///
/// The country code.
/// The ratings.
- private Dictionary GetRatings(string countryCode)
+ private Dictionary? GetRatings(string countryCode)
{
_allParentalRatings.TryGetValue(countryCode, out var value);
@@ -238,7 +240,7 @@ namespace Emby.Server.Implementations.Localization
var ratingsDictionary = GetParentalRatingsDictionary();
- if (ratingsDictionary.TryGetValue(rating, out ParentalRating value))
+ if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
{
return value.Value;
}
@@ -268,20 +270,6 @@ namespace Emby.Server.Implementations.Localization
return null;
}
- ///
- public bool HasUnicodeCategory(string value, UnicodeCategory category)
- {
- foreach (var chr in value)
- {
- if (char.GetUnicodeCategory(chr) == category)
- {
- return true;
- }
- }
-
- return false;
- }
-
///
public string GetLocalizedString(string phrase)
{
@@ -347,18 +335,21 @@ namespace Emby.Server.Implementations.Localization
{
await using var stream = _assembly.GetManifestResourceStream(resourcePath);
// If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
- if (stream != null)
+ if (stream == null)
{
- var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false);
+ _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
+ return;
+ }
- foreach (var key in dict.Keys)
- {
- dictionary[key] = dict[key];
- }
+ var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false);
+ if (dict == null)
+ {
+ throw new InvalidOperationException($"Resource contains invalid data: '{stream}'");
}
- else
+
+ foreach (var key in dict.Keys)
{
- _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
+ dictionary[key] = dict[key];
}
}
diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs
index 8a6d28e0f6..afda83a7c5 100644
--- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs
+++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs
@@ -47,10 +47,10 @@ namespace MediaBrowser.Common.Plugins
var assemblyFilePath = assembly.Location;
var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
- if (!Directory.Exists(dataFolderPath) && Version != null)
+ if (Version != null && !Directory.Exists(dataFolderPath))
{
// Try again with the version number appended to the folder name.
- dataFolderPath = dataFolderPath + "_" + Version.ToString();
+ dataFolderPath += "_" + Version.ToString();
}
SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
index 97f40b5372..abfdb41d80 100644
--- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
@@ -1,6 +1,5 @@
-#nullable disable
-
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Jellyfin.Extensions;
@@ -16,7 +15,7 @@ namespace MediaBrowser.Controller.BaseItemManager
{
private readonly IServerConfigurationManager _serverConfigurationManager;
- private int _metadataRefreshConcurrency = 0;
+ private int _metadataRefreshConcurrency;
///
/// Initializes a new instance of the class.
@@ -101,7 +100,7 @@ namespace MediaBrowser.Controller.BaseItemManager
/// Called when the configuration is updated.
/// It will refresh the metadata throttler if the relevant config changed.
///
- private void OnConfigurationUpdated(object sender, EventArgs e)
+ private void OnConfigurationUpdated(object? sender, EventArgs e)
{
int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency();
if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency)
@@ -114,6 +113,7 @@ namespace MediaBrowser.Controller.BaseItemManager
///
/// Creates the metadata refresh throttler.
///
+ [MemberNotNull(nameof(MetadataRefreshThrottler))]
private void SetupMetadataThrottler()
{
MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency);
diff --git a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
index b2b36c040b..e18994214e 100644
--- a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Threading;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
@@ -34,4 +32,4 @@ namespace MediaBrowser.Controller.BaseItemManager
/// true if image fetcher is enabled, else false.
bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name);
}
-}
\ No newline at end of file
+}
diff --git a/MediaBrowser.Controller/Channels/ChannelItemResult.cs b/MediaBrowser.Controller/Channels/ChannelItemResult.cs
index 7a0addd9f9..ca7721991d 100644
--- a/MediaBrowser.Controller/Channels/ChannelItemResult.cs
+++ b/MediaBrowser.Controller/Channels/ChannelItemResult.cs
@@ -1,7 +1,6 @@
-#nullable disable
-
-#pragma warning disable CA1002, CA2227, CS1591
+#pragma warning disable CS1591
+using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Channels
@@ -10,10 +9,10 @@ namespace MediaBrowser.Controller.Channels
{
public ChannelItemResult()
{
- Items = new List();
+ Items = Array.Empty();
}
- public List Items { get; set; }
+ public IReadOnlyList Items { get; set; }
public int? TotalRecordCount { get; set; }
}
diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
index 76ad335c59..30f5f4efa2 100644
--- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
+++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
@@ -1,6 +1,6 @@
#nullable disable
-#pragma warning disable CA2227, CS1591
+#pragma warning disable CS1591
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs b/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs
index 8155cf3dbd..e538fa4b3f 100644
--- a/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs
+++ b/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs
index 49cc39f047..b8c33ee5a0 100644
--- a/MediaBrowser.Controller/Collections/ICollectionManager.cs
+++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -16,17 +14,17 @@ namespace MediaBrowser.Controller.Collections
///
/// Occurs when [collection created].
///
- event EventHandler CollectionCreated;
+ event EventHandler? CollectionCreated;
///
/// Occurs when [items added to collection].
///
- event EventHandler ItemsAddedToCollection;
+ event EventHandler? ItemsAddedToCollection;
///
/// Occurs when [items removed from collection].
///
- event EventHandler ItemsRemovedFromCollection;
+ event EventHandler? ItemsRemovedFromCollection;
///
/// Creates the collection.
diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs
index 44e2c45dd4..43ad04dbac 100644
--- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs
+++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 9bae95a272..141bb91c57 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
@@ -16,9 +15,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
-using Microsoft.Extensions.Configuration;
namespace MediaBrowser.Controller.MediaEncoding
{
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index baefeb39cf..b213e7aa00 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -1,4 +1,3 @@
-#nullable disable
using System.Collections.Generic;
using System.Globalization;
using MediaBrowser.Model.Entities;
@@ -56,19 +55,11 @@ namespace MediaBrowser.Model.Globalization
/// .
IEnumerable GetLocalizationOptions();
- ///
- /// Checks if the string contains a character with the specified unicode category.
- ///
- /// The string.
- /// The unicode category.
- /// Wether or not the string contains a character with the specified unicode category.
- bool HasUnicodeCategory(string value, UnicodeCategory category);
-
///
/// Returns the correct for the given language.
///
/// The language.
/// The correct for the given language.
- CultureDto FindLanguageInfo(string language);
+ CultureDto? FindLanguageInfo(string language);
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 2c86f9242d..1125154ac8 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -148,80 +148,76 @@ namespace MediaBrowser.XbmcMetadata.Parsers
return;
}
- using (var fileStream = File.OpenRead(metadataFile))
- using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
- {
- item.ResetPeople();
-
- // Need to handle a url after the xml data
- // http://kodi.wiki/view/NFO_files/movies
+ item.ResetPeople();
- var xml = streamReader.ReadToEnd();
+ // Need to handle a url after the xml data
+ // http://kodi.wiki/view/NFO_files/movies
- // Find last closing Tag
- // Need to do this in two steps to account for random > characters after the closing xml
- var index = xml.LastIndexOf(@"", StringComparison.Ordinal);
+ var xml = File.ReadAllText(metadataFile);
- // If closing tag exists, move to end of Tag
- if (index != -1)
- {
- index = xml.IndexOf('>', index);
- }
+ // Find last closing Tag
+ // Need to do this in two steps to account for random > characters after the closing xml
+ var index = xml.LastIndexOf(@"", StringComparison.Ordinal);
- if (index != -1)
- {
- var endingXml = xml.Substring(index);
+ // If closing tag exists, move to end of Tag
+ if (index != -1)
+ {
+ index = xml.IndexOf('>', index);
+ }
- ParseProviderLinks(item.Item, endingXml);
+ if (index != -1)
+ {
+ var endingXml = xml.AsSpan().Slice(index);
- // If the file is just an imdb url, don't go any further
- if (index == 0)
- {
- return;
- }
+ ParseProviderLinks(item.Item, endingXml);
- xml = xml.Substring(0, index + 1);
- }
- else
+ // If the file is just an imdb url, don't go any further
+ if (index == 0)
{
- // If the file is just provider urls, handle that
- ParseProviderLinks(item.Item, xml);
-
return;
}
- // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
- try
+ xml = xml.Substring(0, index + 1);
+ }
+ else
+ {
+ // If the file is just provider urls, handle that
+ ParseProviderLinks(item.Item, xml);
+
+ return;
+ }
+
+ // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
+ try
+ {
+ using (var stringReader = new StringReader(xml))
+ using (var reader = XmlReader.Create(stringReader, settings))
{
- using (var stringReader = new StringReader(xml))
- using (var reader = XmlReader.Create(stringReader, settings))
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- reader.MoveToContent();
- reader.Read();
+ cancellationToken.ThrowIfCancellationRequested();
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ if (reader.NodeType == XmlNodeType.Element)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- if (reader.NodeType == XmlNodeType.Element)
- {
- FetchDataFromXmlNode(reader, item);
- }
- else
- {
- reader.Read();
- }
+ FetchDataFromXmlNode(reader, item);
+ }
+ else
+ {
+ reader.Read();
}
}
}
- catch (XmlException)
- {
- }
+ }
+ catch (XmlException)
+ {
}
}
- protected void ParseProviderLinks(T item, string xml)
+ protected void ParseProviderLinks(T item, ReadOnlySpan xml)
{
if (ProviderIdParsers.TryFindImdbId(xml, out var imdbId))
{
diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
index 6b16075305..ca3ec79b78 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
@@ -40,72 +40,68 @@ namespace MediaBrowser.XbmcMetadata.Parsers
///
protected override void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
{
- using (var fileStream = File.OpenRead(metadataFile))
- using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
- {
- item.ResetPeople();
+ item.ResetPeople();
- var xmlFile = streamReader.ReadToEnd();
+ var xmlFile = File.ReadAllText(metadataFile);
- var srch = "";
- var index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
+ var srch = "";
+ var index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
- var xml = xmlFile;
+ var xml = xmlFile;
- if (index != -1)
- {
- xml = xmlFile.Substring(0, index + srch.Length);
- xmlFile = xmlFile.Substring(index + srch.Length);
- }
+ if (index != -1)
+ {
+ xml = xmlFile.Substring(0, index + srch.Length);
+ xmlFile = xmlFile.Substring(index + srch.Length);
+ }
- // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
- try
+ // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
+ try
+ {
+ // Extract episode details from the first episodedetails block
+ using (var stringReader = new StringReader(xml))
+ using (var reader = XmlReader.Create(stringReader, settings))
{
- // Extract episode details from the first episodedetails block
- using (var stringReader = new StringReader(xml))
- using (var reader = XmlReader.Create(stringReader, settings))
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- reader.MoveToContent();
- reader.Read();
+ cancellationToken.ThrowIfCancellationRequested();
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ if (reader.NodeType == XmlNodeType.Element)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- if (reader.NodeType == XmlNodeType.Element)
- {
- FetchDataFromXmlNode(reader, item);
- }
- else
- {
- reader.Read();
- }
+ FetchDataFromXmlNode(reader, item);
+ }
+ else
+ {
+ reader.Read();
}
}
+ }
- // Extract the last episode number from nfo
- // This is needed because XBMC metadata uses multiple episodedetails blocks instead of episodenumberend tag
- while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1)
+ // Extract the last episode number from nfo
+ // This is needed because XBMC metadata uses multiple episodedetails blocks instead of episodenumberend tag
+ while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1)
+ {
+ xml = xmlFile.Substring(0, index + srch.Length);
+ xmlFile = xmlFile.Substring(index + srch.Length);
+
+ using (var stringReader = new StringReader(xml))
+ using (var reader = XmlReader.Create(stringReader, settings))
{
- xml = xmlFile.Substring(0, index + srch.Length);
- xmlFile = xmlFile.Substring(index + srch.Length);
+ reader.MoveToContent();
- using (var stringReader = new StringReader(xml))
- using (var reader = XmlReader.Create(stringReader, settings))
+ if (reader.ReadToDescendant("episode") && int.TryParse(reader.ReadElementContentAsString(), out var num))
{
- reader.MoveToContent();
-
- if (reader.ReadToDescendant("episode") && int.TryParse(reader.ReadElementContentAsString(), out var num))
- {
- item.Item.IndexNumberEnd = Math.Max(num, item.Item.IndexNumberEnd ?? num);
- }
+ item.Item.IndexNumberEnd = Math.Max(num, item.Item.IndexNumberEnd ?? num);
}
}
}
- catch (XmlException)
- {
- }
+ }
+ catch (XmlException)
+ {
}
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index edd4b1e558..143020d436 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -66,7 +66,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
var germany = localizationManager.FindLanguageInfo(identifier);
Assert.NotNull(germany);
- Assert.Equal("ger", germany.ThreeLetterISOLanguageName);
+ Assert.Equal("ger", germany!.ThreeLetterISOLanguageName);
Assert.Equal("German", germany.DisplayName);
Assert.Equal("German", germany.Name);
Assert.Contains("deu", germany.ThreeLetterISOLanguageNames);
From 3300d2d7d3086b84e0158c8e507b2412d54ebe04 Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Tue, 27 Jul 2021 14:02:49 +0200
Subject: [PATCH 050/118] Enable people for audio files
---
MediaBrowser.Controller/Entities/Audio/Audio.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 7bf1219ec2..536668e508 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -43,7 +43,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public override bool SupportsPlayedStatus => true;
[JsonIgnore]
- public override bool SupportsPeople => false;
+ public override bool SupportsPeople => true;
[JsonIgnore]
public override bool SupportsAddingToPlaylist => true;
From 7e71c25059d373818f228cc524ac4d68f1ea0602 Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Tue, 27 Jul 2021 14:46:18 +0200
Subject: [PATCH 051/118] Add test for audio file ffprobe normalization
---
.../Probing/ProbeResultNormalizerTests.cs | 28 ++++
.../Test Data/Probing/music_metadata.json | 144 ++++++++++++++++++
2 files changed, 172 insertions(+)
create mode 100644 tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_metadata.json
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 59037c2636..690c5d1c14 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Text.Json;
using Jellyfin.Extensions.Json;
using MediaBrowser.MediaEncoding.Probing;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging.Abstractions;
@@ -91,5 +92,32 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Contains("Pop", res.Genres);
Assert.Contains("Jazz", res.Genres);
}
+
+ [Fact]
+ public void GetMediaInfo_Music_Success()
+ {
+ var bytes = File.ReadAllBytes("Test Data/Probing/music_metadata.json");
+ var internalMediaInfoResult = JsonSerializer.Deserialize(bytes, _jsonOptions);
+ MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, null, true, "Test Data/Probing/music.flac", MediaProtocol.File);
+
+ Assert.Equal("UP NO MORE", res.Name);
+ Assert.Single(res.Artists);
+ Assert.Equal("TWICE", res.Artists[0]);
+ Assert.Equal("Eyes wide open", res.Album);
+ Assert.Equal(2020, res.ProductionYear);
+ Assert.True(res.PremiereDate.HasValue);
+ Assert.Equal(DateTime.Parse("2020-10-26T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate);
+ Assert.NotEmpty(res.People);
+ Assert.Equal("Krysta Youngs", res.People[0].Name);
+ Assert.Equal(PersonType.Composer, res.People[0].Type);
+ Assert.Equal("Julia Ross", res.People[1].Name);
+ Assert.Equal(PersonType.Composer, res.People[1].Type);
+ Assert.Equal("Yiwoomin", res.People[2].Name);
+ Assert.Equal(PersonType.Composer, res.People[2].Type);
+ Assert.Equal("Ji-hyo Park", res.People[3].Name);
+ Assert.Equal(PersonType.Lyricist, res.People[3].Type);
+ Assert.NotEmpty(res.Genres);
+ Assert.Equal(new string[] { "Electronic", "Trance", "Dance", "Jazz" }, res.Genres);
+ }
}
}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_metadata.json
new file mode 100644
index 0000000000..6530629fe8
--- /dev/null
+++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_metadata.json
@@ -0,0 +1,144 @@
+{
+ "streams": [
+ {
+ "index": 0,
+ "codec_name": "flac",
+ "codec_long_name": "FLAC (Free Lossless Audio Codec)",
+ "codec_type": "audio",
+ "codec_tag_string": "[0][0][0][0]",
+ "codec_tag": "0x0000",
+ "sample_fmt": "s16",
+ "sample_rate": "44100",
+ "channels": 2,
+ "channel_layout": "stereo",
+ "bits_per_sample": 0,
+ "r_frame_rate": "0/0",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/44100",
+ "start_pts": 0,
+ "start_time": "0.000000",
+ "duration_ts": 9447984,
+ "duration": "214.240000",
+ "bits_per_raw_sample": "16",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0
+ }
+ },
+ {
+ "index": 1,
+ "codec_name": "mjpeg",
+ "codec_long_name": "Motion JPEG",
+ "profile": "Baseline",
+ "codec_type": "video",
+ "codec_tag_string": "[0][0][0][0]",
+ "codec_tag": "0x0000",
+ "width": 500,
+ "height": 500,
+ "coded_width": 500,
+ "coded_height": 500,
+ "closed_captions": 0,
+ "has_b_frames": 0,
+ "sample_aspect_ratio": "1:1",
+ "display_aspect_ratio": "1:1",
+ "pix_fmt": "yuvj420p",
+ "level": -99,
+ "color_range": "pc",
+ "color_space": "bt470bg",
+ "chroma_location": "center",
+ "refs": 1,
+ "r_frame_rate": "90000/1",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/90000",
+ "start_pts": 0,
+ "start_time": "0.000000",
+ "duration_ts": 19281600,
+ "duration": "214.240000",
+ "bits_per_raw_sample": "8",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 1,
+ "timed_thumbnails": 0
+ },
+ "tags": {
+ "comment": "Cover (front)"
+ }
+ }
+ ],
+ "format": {
+ "filename": "03 UP NO MORE.flac",
+ "nb_streams": 2,
+ "nb_programs": 0,
+ "format_name": "flac",
+ "format_long_name": "raw FLAC",
+ "start_time": "0.000000",
+ "duration": "214.240000",
+ "size": "28714641",
+ "bit_rate": "1072242",
+ "probe_score": 100,
+ "tags": {
+ "MUSICBRAINZ_RELEASEGROUPID": "aa05ff10-8589-4c9c-a0d4-6b024f4e4556",
+ "ORIGINALDATE": "2020-10-26",
+ "ORIGINALYEAR": "2020",
+ "RELEASETYPE": "album",
+ "MUSICBRAINZ_ALBUMID": "222e6610-75c9-400e-8dc3-bb61f9fc5ca7",
+ "SCRIPT": "Latn",
+ "ALBUM": "Eyes wide open",
+ "RELEASECOUNTRY": "JP",
+ "BARCODE": "190295105280",
+ "LABEL": "JYP Entertainment",
+ "RELEASESTATUS": "official",
+ "DATE": "2020-10-26",
+ "MUSICBRAINZ_ALBUMARTISTID": "8da127cc-c432-418f-b356-ef36210d82ac",
+ "album_artist": "TWICE",
+ "ALBUMARTISTSORT": "TWICE",
+ "TOTALDISCS": "1",
+ "TOTALTRACKS": "13",
+ "MEDIA": "Digital Media",
+ "disc": "1",
+ "MUSICBRAINZ_TRACKID": "7d1a1044-b564-480d-9df3-22f9656fdb97",
+ "TITLE": "UP NO MORE",
+ "ISRC": "US5TA2000136",
+ "PERFORMER": "Yiwoomin (electric piano);Yiwoomin (synthesizer);Yiwoomin (bass);Yiwoomin (guitar);TWICE;Tzu-yu Chou (vocals);Momo Hirai (vocals);Na-yeon Im (vocals);Da-hyun Kim (vocals);Sana Minatozaki (vocals);Mina Myoui (vocals);Ji-hyo Park (vocals);Chae-young Son (vocals);Jeong-yeon Yoo (vocals);Perrie (background vocals)",
+ "MIXER": "Bong Won Shin",
+ "ARRANGER": "Krysta Youngs;Julia Ross;Yiwoomin",
+ "MUSICBRAINZ_WORKID": "02b37083-0337-4721-9f17-bf31971043e8",
+ "LANGUAGE": "kor;eng",
+ "WORK": "Up No More",
+ "COMPOSER": "Krysta Youngs;Julia Ross;Yiwoomin",
+ "COMPOSERSORT": "Krysta Youngs;Ross, Julia;Yiwoomin",
+ "LYRICIST": "Ji-hyo Park",
+ "MUSICBRAINZ_ARTISTID": "8da127cc-c432-418f-b356-ef36210d82ac",
+ "ARTIST": "TWICE",
+ "ARTISTSORT": "TWICE",
+ "ARTISTS": "TWICE",
+ "MUSICBRAINZ_RELEASETRACKID": "ad49b840-da9e-4e7c-924b-29fdee187052",
+ "track": "3",
+ "GENRE": "Electronic;Trance;Dance;Jazz",
+ "WEBSITE": "http://twice.jype.com/;http://www.twicejapan.com/",
+ "ACOUSTID_ID": "aae2e972-108c-4d0c-8e31-9d078283e3dc",
+ "MOOD": "Not acoustic;Not aggressive;Electronic;Happy;Party;Not relaxed;Not sad",
+ "TRACKTOTAL": "13",
+ "DISCTOTAL": "1"
+ }
+ }
+}
From 24083d2e38c17e005c536339d555355b9155485f Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Tue, 27 Jul 2021 16:25:46 +0200
Subject: [PATCH 052/118] Address comments
---
.../Probing/ProbeResultNormalizerTests.cs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 690c5d1c14..4d6b7a5140 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -107,7 +107,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal(2020, res.ProductionYear);
Assert.True(res.PremiereDate.HasValue);
Assert.Equal(DateTime.Parse("2020-10-26T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate);
- Assert.NotEmpty(res.People);
+ Assert.Equal(4, res.People.Length);
Assert.Equal("Krysta Youngs", res.People[0].Name);
Assert.Equal(PersonType.Composer, res.People[0].Type);
Assert.Equal("Julia Ross", res.People[1].Name);
@@ -116,8 +116,11 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal(PersonType.Composer, res.People[2].Type);
Assert.Equal("Ji-hyo Park", res.People[3].Name);
Assert.Equal(PersonType.Lyricist, res.People[3].Type);
- Assert.NotEmpty(res.Genres);
- Assert.Equal(new string[] { "Electronic", "Trance", "Dance", "Jazz" }, res.Genres);
+ Assert.Equal(4, res.Genres.Length);
+ Assert.Contains("Electronic", res.Genres);
+ Assert.Contains("Trance", res.Genres);
+ Assert.Contains("Dance", res.Genres);
+ Assert.Contains("Jazz", res.Genres);
}
}
}
From f35a527608fb3f6efde3f76042e0e701768eb3f2 Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Tue, 27 Jul 2021 17:09:23 +0200
Subject: [PATCH 053/118] Add performers to the ffprobe normalization for audio
---
.../Probing/ProbeResultNormalizer.cs | 21 +++++++++++++++++++
.../Probing/ProbeResultNormalizerTests.cs | 5 ++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 875ee6f04c..20dead0771 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -7,6 +7,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Xml;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Library;
@@ -1111,6 +1112,26 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
+ if (tags.TryGetValue("performer", out var performer) && !string.IsNullOrWhiteSpace(performer))
+ {
+ foreach (var person in Split(performer, false))
+ {
+ Regex pattern = new Regex(@"(?.*) \((?.*)\)");
+ Match match = pattern.Match(person);
+
+ // If the performer doesn't have any instrument/role associated, it won't match. In that case, chances are it's simply a band name, so we skip it.
+ if (match.Success)
+ {
+ people.Add(new BaseItemPerson
+ {
+ Name = match.Groups["name"].Value,
+ Type = PersonType.Actor,
+ Role = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(match.Groups["instrument"].Value)
+ });
+ }
+ }
+ }
+
// Check for writer some music is tagged that way as alternative to composer/lyricist
if (tags.TryGetValue("writer", out var writer) && !string.IsNullOrWhiteSpace(writer))
{
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 4d6b7a5140..928f275d7e 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -107,7 +107,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal(2020, res.ProductionYear);
Assert.True(res.PremiereDate.HasValue);
Assert.Equal(DateTime.Parse("2020-10-26T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate);
- Assert.Equal(4, res.People.Length);
+ Assert.Equal(18, res.People.Length);
Assert.Equal("Krysta Youngs", res.People[0].Name);
Assert.Equal(PersonType.Composer, res.People[0].Type);
Assert.Equal("Julia Ross", res.People[1].Name);
@@ -116,6 +116,9 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal(PersonType.Composer, res.People[2].Type);
Assert.Equal("Ji-hyo Park", res.People[3].Name);
Assert.Equal(PersonType.Lyricist, res.People[3].Type);
+ Assert.Equal("Yiwoomin", res.People[4].Name);
+ Assert.Equal(PersonType.Actor, res.People[4].Type);
+ Assert.Equal("Electric Piano", res.People[4].Role);
Assert.Equal(4, res.Genres.Length);
Assert.Contains("Electronic", res.Genres);
Assert.Contains("Trance", res.Genres);
From c9b1cd1d8cfa3764eff3dfe97baac10ce229d2e9 Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Tue, 27 Jul 2021 23:52:05 +0200
Subject: [PATCH 054/118] Add some new music-related person types and parse
from ffprobe
---
.../Probing/ProbeResultNormalizer.cs | 34 ++++++++++++++++-
MediaBrowser.Model/Entities/PersonType.cs | 38 ++++++++++++++-----
.../Probing/ProbeResultNormalizerTests.cs | 2 +-
3 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 20dead0771..aa6674468b 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -1132,7 +1132,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- // Check for writer some music is tagged that way as alternative to composer/lyricist
+ // In cases where there isn't sufficient information as to which role a writer performed on a recording, tagging software uses the "writer" tag.
if (tags.TryGetValue("writer", out var writer) && !string.IsNullOrWhiteSpace(writer))
{
foreach (var person in Split(writer, false))
@@ -1141,6 +1141,38 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
+ if (tags.TryGetValue("arranger", out var arranger) && !string.IsNullOrWhiteSpace(arranger))
+ {
+ foreach (var person in Split(arranger, false))
+ {
+ people.Add(new BaseItemPerson { Name = person, Type = PersonType.Arranger });
+ }
+ }
+
+ if (tags.TryGetValue("engineer", out var engineer) && !string.IsNullOrWhiteSpace(engineer))
+ {
+ foreach (var person in Split(engineer, false))
+ {
+ people.Add(new BaseItemPerson { Name = person, Type = PersonType.Engineer });
+ }
+ }
+
+ if (tags.TryGetValue("mixer", out var mixer) && !string.IsNullOrWhiteSpace(mixer))
+ {
+ foreach (var person in Split(mixer, false))
+ {
+ people.Add(new BaseItemPerson { Name = person, Type = PersonType.Mixer });
+ }
+ }
+
+ if (tags.TryGetValue("remixer", out var remixer) && !string.IsNullOrWhiteSpace(remixer))
+ {
+ foreach (var person in Split(remixer, false))
+ {
+ people.Add(new BaseItemPerson { Name = person, Type = PersonType.Remixer });
+ }
+ }
+
audio.People = people.ToArray();
// Set album artist
diff --git a/MediaBrowser.Model/Entities/PersonType.cs b/MediaBrowser.Model/Entities/PersonType.cs
index 81db9c6131..7b780f7d7b 100644
--- a/MediaBrowser.Model/Entities/PersonType.cs
+++ b/MediaBrowser.Model/Entities/PersonType.cs
@@ -1,42 +1,42 @@
namespace MediaBrowser.Model.Entities
{
///
- /// Struct PersonType.
+ /// Types of persons.
///
- public class PersonType
+ public static class PersonType
{
///
- /// The actor.
+ /// A person whose profession is acting on the stage, in films, or on television.
///
public const string Actor = "Actor";
///
- /// The director.
+ /// A person who supervises the actors and other staff in a film, play, or similar production.
///
public const string Director = "Director";
///
- /// The composer.
+ /// A person who writes music, especially as a professional occupation.
///
public const string Composer = "Composer";
///
- /// The writer.
+ /// A writer of a book, article, or document. Can also be used as a generic term for music writer if there is a lack of specificity.
///
public const string Writer = "Writer";
///
- /// The guest star.
+ /// A well-known actor or other performer who appears in a work in which they do not have a regular role.
///
public const string GuestStar = "GuestStar";
///
- /// The producer.
+ /// A person responsible for the financial and managerial aspects of the making of a film or broadcast or for staging a play, opera, etc.
///
public const string Producer = "Producer";
///
- /// The conductor.
+ /// A person who directs the performance of an orchestra or choir.
///
public const string Conductor = "Conductor";
@@ -44,5 +44,25 @@ namespace MediaBrowser.Model.Entities
/// The lyricist.
///
public const string Lyricist = "Lyricist";
+
+ ///
+ /// A person who writes the words to a song or musical.
+ ///
+ public const string Arranger = "Arranger";
+
+ ///
+ /// An audio engineer who performed a general engineering role.
+ ///
+ public const string Engineer = "Engineer";
+
+ ///
+ /// An engineer responsible for using a mixing console to mix a recorded track into a single piece of music suitable for release.
+ ///
+ public const string Mixer = "Mixer";
+
+ ///
+ /// A person who remixed a recording by taking one or more other tracks, substantially altering them and mixing them together with other material.
+ ///
+ public const string Remixer = "Remixer";
}
}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 928f275d7e..fcb85a3acf 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -107,7 +107,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal(2020, res.ProductionYear);
Assert.True(res.PremiereDate.HasValue);
Assert.Equal(DateTime.Parse("2020-10-26T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate);
- Assert.Equal(18, res.People.Length);
+ Assert.Equal(22, res.People.Length);
Assert.Equal("Krysta Youngs", res.People[0].Name);
Assert.Equal(PersonType.Composer, res.People[0].Type);
Assert.Equal("Julia Ross", res.People[1].Name);
From 8594ee7a2275403d0569529cd1e1237e10cc7d77 Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Wed, 28 Jul 2021 23:25:45 +0200
Subject: [PATCH 055/118] Fix documentation for lyricist and arranger
---
MediaBrowser.Model/Entities/PersonType.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/MediaBrowser.Model/Entities/PersonType.cs b/MediaBrowser.Model/Entities/PersonType.cs
index 7b780f7d7b..b985507f0c 100644
--- a/MediaBrowser.Model/Entities/PersonType.cs
+++ b/MediaBrowser.Model/Entities/PersonType.cs
@@ -41,12 +41,12 @@ namespace MediaBrowser.Model.Entities
public const string Conductor = "Conductor";
///
- /// The lyricist.
+ /// A person who writes the words to a song or musical.
///
public const string Lyricist = "Lyricist";
///
- /// A person who writes the words to a song or musical.
+ /// A person who adapts a musical composition for performance.
///
public const string Arranger = "Arranger";
From d82c2e423766566883be850d60e94cc36656f0e8 Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Mon, 2 Aug 2021 03:48:45 +0200
Subject: [PATCH 056/118] Address comments
---
.../Probing/ProbeResultNormalizer.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index aa6674468b..cdfdbc5dad 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -28,7 +28,9 @@ namespace MediaBrowser.MediaEncoding.Probing
private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' };
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+ private readonly Regex _performerPattern = new (@"(?.*) \((?.*)\)");
+
+ private readonly CultureInfo _usCulture = new ("en-US");
private readonly ILogger _logger;
private readonly ILocalizationManager _localization;
@@ -1116,8 +1118,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
foreach (var person in Split(performer, false))
{
- Regex pattern = new Regex(@"(?.*) \((?.*)\)");
- Match match = pattern.Match(person);
+ Match match = _performerPattern.Match(person);
// If the performer doesn't have any instrument/role associated, it won't match. In that case, chances are it's simply a band name, so we skip it.
if (match.Success)
From 7f52cda03c9731fcbd57fd8c9ed8806c6965d054 Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Sat, 7 Aug 2021 21:58:19 +0200
Subject: [PATCH 057/118] Make performer regex static
---
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index cdfdbc5dad..e83e533c92 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.MediaEncoding.Probing
private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' };
- private readonly Regex _performerPattern = new (@"(?.*) \((?.*)\)");
+ private static readonly Regex _performerPattern = new (@"(?.*) \((?.*)\)");
private readonly CultureInfo _usCulture = new ("en-US");
private readonly ILogger _logger;
From 10fbb4d48d772e287cc3a03045b22e1225681935 Mon Sep 17 00:00:00 2001
From: Rob
Date: Sun, 15 Aug 2021 15:23:18 -0700
Subject: [PATCH 058/118] Update
tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
Thanks for this suggestion. I will try to keep this in mind going forward with future PRs
Co-authored-by: Claus Vium
---
.../Sorting/AiredEpisodeOrderComparerTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
index ff7999612b..7d7a110b2d 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -13,7 +13,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
{
[Theory]
[ClassData(typeof(EpisodeBadData))]
- public void AiredEpisodeOrderCompareErrorTest(BaseItem x, BaseItem y)
+ public void Compare_GivenNull_ThrowsArgumentNullException(BaseItem x, BaseItem y)
{
var cmp = new AiredEpisodeOrderComparer();
Assert.Throws(() => cmp.Compare(x, y));
From 9220682fd11e1dc9872959eb1dcf2f1dac38066c Mon Sep 17 00:00:00 2001
From: Rob
Date: Sun, 15 Aug 2021 15:26:00 -0700
Subject: [PATCH 059/118] Update
tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
Co-authored-by: Bond-009
---
.../Sorting/AiredEpisodeOrderComparerTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
index 7d7a110b2d..61cec81124 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -37,7 +37,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
public IEnumerator GetEnumerator()
{
yield return new object?[] { null, new Episode() };
- yield return new object?[] { new Episode() };
+ yield return new object?[] { new Episode(), null };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
From 0f9e95b4c5ad15c6779c3241bc581313a234b357 Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Sun, 15 Aug 2021 15:31:58 -0700
Subject: [PATCH 060/118] Breaking up the test theory so episodes and the
results are on their own lines.
---
.../Sorting/AiredEpisodeOrderComparerTests.cs | 140 +++++++++++++++---
1 file changed, 120 insertions(+), 20 deletions(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
index 61cec81124..2733c55acc 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -47,34 +47,134 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
{
public IEnumerator GetEnumerator()
{
- yield return new object?[] { new Movie(), new Movie(), 0 };
- yield return new object?[] { new Movie(), new Episode(), 1 };
+ yield return new object?[]
+ {
+ new Movie(),
+ new Movie(),
+ 0
+ };
+ yield return new object?[]
+ {
+ new Movie(),
+ new Episode(),
+ 1
+ };
// Good cases
- yield return new object?[] { new Episode(), new Episode(), 0 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 0 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 2, IndexNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1 };
+ yield return new object?[]
+ {
+ new Episode(),
+ new Episode(),
+ 0
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ 0
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ 1
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 2, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ 1
+ };
// Good Specials
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 0 };
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
+ 0
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
+ 1
+ };
// Specials to Episodes
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
+ 1
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
+ 1
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
+ 1
+ };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, 1 };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
+ 1
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
+ 1
+ };
- yield return new object?[] { new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, 1 };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 },
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ 1
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 3, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 },
+ 1
+ };
- yield return new object?[] { new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1 };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 3, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
+ 1
+ };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 0 };
- yield return new object?[] { new Episode { ParentIndexNumber = 1, IndexNumber = 3 }, new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, 1 };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 },
+ 1
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
+ 1
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
+ 0
+ };
+ yield return new object?[]
+ {
+ new Episode { ParentIndexNumber = 1, IndexNumber = 3 },
+ new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
+ 1
+ };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
From 6ba7db3d55b2172e3344a040019a0bf25943a35b Mon Sep 17 00:00:00 2001
From: ankenyr
Date: Sun, 15 Aug 2021 15:48:01 -0700
Subject: [PATCH 061/118] Removing if statement and always testing reverse of x
and y.
---
.../Sorting/AiredEpisodeOrderComparerTests.cs | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
index 2733c55acc..d9b206f663 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs
@@ -26,10 +26,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
var cmp = new AiredEpisodeOrderComparer();
Assert.Equal(expected, cmp.Compare(x, y));
- if (expected == 1)
- {
- Assert.Equal(-expected, cmp.Compare(y, x));
- }
+ Assert.Equal(-expected, cmp.Compare(y, x));
}
private class EpisodeBadData : IEnumerable
From 33bf8e34d1c4e5d6d89e21a38d23d2f801079397 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Sun, 15 Aug 2021 18:57:14 -0600
Subject: [PATCH 062/118] Update Dockerfile to use debian:bullseye-slim
---
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index 0859fdc4c8..3190fec5c5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& npm ci --no-audit --unsafe-perm \
&& mv dist /dist
-FROM debian:buster-slim as app
+FROM debian:bullseye-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
From b504d1f724c7786a0f71437aacbdbe04bd831d0a Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Sun, 15 Aug 2021 19:32:45 -0600
Subject: [PATCH 063/118] Also update arm, arm64 Dockerfile
---
Dockerfile.arm | 2 +-
Dockerfile.arm64 | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Dockerfile.arm b/Dockerfile.arm
index cc0c79c94f..dcd006ff83 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -14,7 +14,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& mv dist /dist
FROM multiarch/qemu-user-static:x86_64-arm as qemu
-FROM arm32v7/debian:buster-slim as app
+FROM arm32v7/debian:bullseye-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 64367a32da..7311c6b9ff 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -14,7 +14,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& mv dist /dist
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
-FROM arm64v8/debian:buster-slim as app
+FROM arm64v8/debian:bullseye-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
From e3c2a8a3bef7043a2c347472bb01d8b31fe00fbc Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Mon, 16 Aug 2021 08:52:16 +0200
Subject: [PATCH 064/118] Update
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
---
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index de178ad5b1..5941613cf9 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
try
{
var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false);
- var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase)));
+ var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
list.AddRange(newChannels);
From 05103a483eaf9fa3f2ab21d1c3f4032fc285e5bf Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 16 Aug 2021 12:00:51 +0000
Subject: [PATCH 065/118] Bump Serilog.Settings.Configuration from 3.1.0 to
3.2.0
Bumps [Serilog.Settings.Configuration](https://github.com/serilog/serilog-settings-configuration) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/serilog/serilog-settings-configuration/releases)
- [Changelog](https://github.com/serilog/serilog-settings-configuration/blob/dev/CHANGES.md)
- [Commits](https://github.com/serilog/serilog-settings-configuration/compare/v3.1.0...v3.2.0)
---
updated-dependencies:
- dependency-name: Serilog.Settings.Configuration
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
Jellyfin.Server/Jellyfin.Server.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 4ad39c60a9..ea64663bd7 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -39,7 +39,7 @@
-
+
From 4077cb2648fad935a3c591536a24473c6a1efd3f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 16 Aug 2021 12:00:55 +0000
Subject: [PATCH 066/118] Bump FsCheck.Xunit from 2.15.3 to 2.16.0
Bumps [FsCheck.Xunit](https://github.com/fsharp/FsCheck) from 2.15.3 to 2.16.0.
- [Release notes](https://github.com/fsharp/FsCheck/releases)
- [Changelog](https://github.com/fscheck/FsCheck/blob/master/FsCheck%20Release%20Notes.md)
- [Commits](https://github.com/fsharp/FsCheck/compare/2.15.3...2.16.0)
---
updated-dependencies:
- dependency-name: FsCheck.Xunit
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 2 +-
.../Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj | 2 +-
tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj | 2 +-
.../Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
index e4350c3369..2eb983e878 100644
--- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
+++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
index 9272d5eef9..9dcb80baba 100644
--- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
+++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
@@ -17,7 +17,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
index 06ff22c7e0..06e8050a4d 100644
--- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
+++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
index 2c6e2e5f66..1199c9649a 100644
--- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
+++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
@@ -16,7 +16,7 @@
-
+
From ee37784a35c28c36659eb2a98367e5968a847162 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 16 Aug 2021 12:01:04 +0000
Subject: [PATCH 067/118] Bump Microsoft.NET.Test.Sdk from 16.10.0 to 16.11.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.10.0 to 16.11.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v16.10.0...v16.11.0)
---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +-
tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 2 +-
.../Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj | 2 +-
tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 2 +-
.../Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj | 2 +-
.../Jellyfin.MediaEncoding.Tests.csproj | 2 +-
tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj | 2 +-
tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 2 +-
.../Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj | 2 +-
tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj | 2 +-
.../Jellyfin.Server.Implementations.Tests.csproj | 2 +-
.../Jellyfin.Server.Integration.Tests.csproj | 2 +-
tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +-
.../Jellyfin.XbmcMetadata.Tests.csproj | 2 +-
14 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index ecf7d2b364..0c36e81cca 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
index e4350c3369..299d3c0f33 100644
--- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
+++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
index 5b269a4b20..a5778b59c8 100644
--- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
+++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
index 713f6423c9..5a48631c29 100644
--- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
+++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
index 9272d5eef9..6e5ff31b54 100644
--- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
+++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
@@ -7,7 +7,7 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
index a6a948e2b7..7ea5039138 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
+++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
index 06ff22c7e0..f5aa1549fd 100644
--- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
+++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
index 510c8f60a3..a4ebab141e 100644
--- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
+++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
index 2c6e2e5f66..2b7e86742e 100644
--- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
+++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj
index 195fc8801d..d9e33617bc 100644
--- a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj
+++ b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
index 387f259ce3..9b6ab7bdf5 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
index 8f2eb9ef58..592b444c99 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
index 6f3c532cd0..f249be674c 100644
--- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
+++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
index 78837bba67..e085907583 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
@@ -13,7 +13,7 @@
-
+
From f23ef1f1b9982f68d03e7095593ee56667769071 Mon Sep 17 00:00:00 2001
From: cvium
Date: Tue, 17 Aug 2021 13:38:28 +0200
Subject: [PATCH 068/118] Use ProgressiveFileStream for LiveRecordings endpoint
---
Jellyfin.Api/Controllers/LiveTvController.cs | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 24ee833ef7..47ebe9f579 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -1172,7 +1172,7 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesVideoFile]
- public async Task GetLiveRecordingFile([FromRoute, Required] string recordingId)
+ public ActionResult GetLiveRecordingFile([FromRoute, Required] string recordingId)
{
var path = _liveTvManager.GetEmbyTvActiveRecordingPath(recordingId);
@@ -1181,11 +1181,8 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- await using var memoryStream = new MemoryStream();
- await new ProgressiveFileCopier(path, null, _transcodingJobHelper, CancellationToken.None)
- .WriteToAsync(memoryStream, CancellationToken.None)
- .ConfigureAwait(false);
- return File(memoryStream, MimeTypes.GetMimeType(path));
+ var stream = new ProgressiveFileStream(path, null, _transcodingJobHelper);
+ return new FileStreamResult(stream, MimeTypes.GetMimeType(path));
}
///
From 12a7fda0a6216c506853941eeabaca6367e0a8a8 Mon Sep 17 00:00:00 2001
From: cvium
Date: Tue, 17 Aug 2021 19:18:26 +0200
Subject: [PATCH 069/118] Add timeout to ProgressiveFileStream
---
Jellyfin.Api/Helpers/ProgressiveFileStream.cs | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
index 824870c7ef..499dbe84d6 100644
--- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
+++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
@@ -16,6 +17,7 @@ namespace Jellyfin.Api.Helpers
private readonly FileStream _fileStream;
private readonly TranscodingJobDto? _job;
private readonly TranscodingJobHelper _transcodingJobHelper;
+ private readonly int _timeoutMs;
private readonly bool _allowAsyncFileRead;
private int _bytesWritten;
private bool _disposed;
@@ -26,10 +28,12 @@ namespace Jellyfin.Api.Helpers
/// The path to the transcoded file.
/// The transcoding job information.
/// The transcoding job helper.
- public ProgressiveFileStream(string filePath, TranscodingJobDto? job, TranscodingJobHelper transcodingJobHelper)
+ /// The timeout duration in milliseconds.
+ public ProgressiveFileStream(string filePath, TranscodingJobDto? job, TranscodingJobHelper transcodingJobHelper, int timeoutMs = 30000)
{
_job = job;
_transcodingJobHelper = transcodingJobHelper;
+ _timeoutMs = timeoutMs;
_bytesWritten = 0;
var fileOptions = FileOptions.SequentialScan;
@@ -81,6 +85,7 @@ namespace Jellyfin.Api.Helpers
{
int totalBytesRead = 0;
int remainingBytesToRead = count;
+ var stopwatch = Stopwatch.StartNew();
int newOffset = offset;
while (remainingBytesToRead > 0)
@@ -111,8 +116,8 @@ namespace Jellyfin.Api.Helpers
}
else
{
- // If the job is null it's a live stream and will require user action to close
- if (_job?.HasExited ?? false)
+ // If the job is null it's a live stream and will require user action to close, but don't keep it open indefinitely
+ if (_job?.HasExited ?? stopwatch.ElapsedMilliseconds > _timeoutMs)
{
break;
}
From 9d445cf7fbe9f4ca58bee116d242c5661e26cefa Mon Sep 17 00:00:00 2001
From: FancyNerd92
Date: Tue, 17 Aug 2021 01:52:51 +0000
Subject: [PATCH 070/118] Translated using Weblate (Greek) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/el/
---
Emby.Server.Implementations/Localization/Core/el.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 834d5e75f2..697063f262 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -1,5 +1,5 @@
{
- "Albums": "Άλμπουμς",
+ "Albums": "Άλμπουμ",
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
"Application": "Εφαρμογή",
"Artists": "Καλλιτέχνες",
@@ -15,7 +15,7 @@
"Favorites": "Αγαπημένα",
"Folders": "Φάκελοι",
"Genres": "Είδη",
- "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
+ "HeaderAlbumArtists": "Άλμπουμ Καλλιτέχνη",
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
From 88b2b9a82acb35614a106a7f141d1c557685821c Mon Sep 17 00:00:00 2001
From: Henry Moritz
Date: Tue, 17 Aug 2021 02:20:40 +0000
Subject: [PATCH 071/118] Translated using Weblate (Spanish (Mexico))
Translation: Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/
---
.../Localization/Core/es-MX.json | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 5d7ed243f4..432814dac1 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -15,7 +15,7 @@
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del álbum",
+ "HeaderAlbumArtists": "Artistas del Álbum",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "TV en vivo",
"HeaderNextUp": "A continuación",
"HeaderRecordingGroups": "Grupos de grabación",
- "HomeVideos": "Videos caseros",
+ "HomeVideos": "Videos Caseros",
"Inherit": "Heredar",
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
@@ -39,7 +39,7 @@
"MixedContent": "Contenido mezclado",
"Movies": "Películas",
"Music": "Música",
- "MusicVideos": "Videos musicales",
+ "MusicVideos": "Videos Musicales",
"NameInstallFailed": "Falló la instalación de {0}",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada desconocida",
@@ -49,7 +49,7 @@
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
- "NotificationOptionInstallationFailed": "Falla de instalación",
+ "NotificationOptionInstallationFailed": "Fallo en la instalación",
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
"NotificationOptionPluginError": "Falla de complemento",
"NotificationOptionPluginInstalled": "Complemento instalado",
@@ -69,7 +69,7 @@
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciado",
- "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
+ "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Programas",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
@@ -94,9 +94,9 @@
"VersionNumber": "Versión {0}",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
- "TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
+ "TaskRefreshChannelsDescription": "Actualiza la información de los canales de Internet.",
"TaskRefreshChannels": "Actualizar canales",
- "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.",
+ "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día de antigüedad.",
"TaskCleanTranscode": "Limpiar directorio de transcodificado",
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
"TaskUpdatePlugins": "Actualizar complementos",
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Limpiar registro de actividades",
"Undefined": "Sin definir",
"Forced": "Forzado",
- "Default": "Predeterminado"
+ "Default": "Predeterminado",
+ "TaskOptimizeDatabase": "Optimizar base de datos",
+ "TaskOptimizeDatabaseDescription": "Compacta la base de datos y trunca el espacio libre. Puede mejorar el rendimiento si se realiza esta tarea después de escanear la biblioteca o después de realizar otros cambios que impliquen modificar la base de datos."
}
From 61aa992628a4ac1c825e63aab56b751106424742 Mon Sep 17 00:00:00 2001
From: Adam Bokor
Date: Mon, 16 Aug 2021 11:28:11 +0000
Subject: [PATCH 072/118] Translated using Weblate (Hungarian) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/
---
Emby.Server.Implementations/Localization/Core/hu.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 255d5427a6..85ab1511a4 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -15,7 +15,7 @@
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
- "HeaderAlbumArtists": "Album előadók",
+ "HeaderAlbumArtists": "Előadó albumai",
"HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc előadók",
From c842a2efa2fe0868f7f1dc34bc73e861dbe7b1ec Mon Sep 17 00:00:00 2001
From: millallo
Date: Sun, 15 Aug 2021 07:22:35 +0000
Subject: [PATCH 073/118] Translated using Weblate (Italian) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/
---
Emby.Server.Implementations/Localization/Core/it.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 0bde76e744..5e28cf09fb 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -15,7 +15,7 @@
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
- "HeaderAlbumArtists": "Artisti degli Album",
+ "HeaderAlbumArtists": "Artisti dell'Album",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Diretta TV",
"HeaderNextUp": "Prossimo",
"HeaderRecordingGroups": "Gruppi di Registrazione",
- "HomeVideos": "Video personali",
+ "HomeVideos": "Video Personali",
"Inherit": "Eredita",
"ItemAddedWithName": "{0} è stato aggiunto alla libreria",
"ItemRemovedWithName": "{0} è stato rimosso dalla libreria",
@@ -39,7 +39,7 @@
"MixedContent": "Contenuto misto",
"Movies": "Film",
"Music": "Musica",
- "MusicVideos": "Video musicali",
+ "MusicVideos": "Video Musicali",
"NameInstallFailed": "{0} installazione fallita",
"NameSeasonNumber": "Stagione {0}",
"NameSeasonUnknown": "Stagione sconosciuta",
From a311116f51cd4387d5b8904164e89b19da24ae2b Mon Sep 17 00:00:00 2001
From: WWWesten
Date: Sat, 14 Aug 2021 22:10:45 +0000
Subject: [PATCH 074/118] Translated using Weblate (Kazakh) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kk/
---
Emby.Server.Implementations/Localization/Core/kk.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index 1b4a18deb5..d28564a7c6 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -15,7 +15,7 @@
"Favorites": "Tañdaulylar",
"Folders": "Qaltalar",
"Genres": "Janrlar",
- "HeaderAlbumArtists": "Älbom oryndauşylary",
+ "HeaderAlbumArtists": "Oryndauşynyñ älbomy",
"HeaderContinueWatching": "Qaraudy jalğastyru",
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",
From 2e5cb8d8b184f7d50c05e9607f7751a993212639 Mon Sep 17 00:00:00 2001
From: WWWesten
Date: Sat, 14 Aug 2021 22:11:51 +0000
Subject: [PATCH 075/118] Translated using Weblate (Russian) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/
---
Emby.Server.Implementations/Localization/Core/ru.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 248f06c4b2..cd016b51b4 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Эфир",
"HeaderNextUp": "Очередное",
"HeaderRecordingGroups": "Группы записей",
- "HomeVideos": "Домашнее видео",
+ "HomeVideos": "Домашние видео",
"Inherit": "Наследуемое",
"ItemAddedWithName": "{0} - добавлено в медиатеку",
"ItemRemovedWithName": "{0} - изъято из медиатеки",
From 0c54708e1c77cd25e42d79d6d91553d442b96a69 Mon Sep 17 00:00:00 2001
From: nextlooper42
Date: Sat, 14 Aug 2021 12:21:23 +0000
Subject: [PATCH 076/118] Translated using Weblate (Slovak) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sk/
---
Emby.Server.Implementations/Localization/Core/sk.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 37da7d5ab3..ad90bd8134 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -39,7 +39,7 @@
"MixedContent": "Zmiešaný obsah",
"Movies": "Filmy",
"Music": "Hudba",
- "MusicVideos": "Hudobné videoklipy",
+ "MusicVideos": "Hudobné videá",
"NameInstallFailed": "Inštalácia {0} zlyhala",
"NameSeasonNumber": "Séria {0}",
"NameSeasonUnknown": "Neznáma séria",
From 2a8cb5d795d62d056b65fd6947f98bde9d2b4012 Mon Sep 17 00:00:00 2001
From: aqweg
Date: Sun, 15 Aug 2021 15:39:06 +0000
Subject: [PATCH 077/118] Translated using Weblate (Afrikaans) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/af/
---
.../Localization/Core/af.json | 22 ++++++++++---------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json
index 4f21c66bc4..18f17dda95 100644
--- a/Emby.Server.Implementations/Localization/Core/af.json
+++ b/Emby.Server.Implementations/Localization/Core/af.json
@@ -2,24 +2,24 @@
"Artists": "Kunstenare",
"Channels": "Kanale",
"Folders": "Lêergidse",
- "Favorites": "Gunstellinge",
+ "Favorites": "Gunstelinge",
"HeaderFavoriteShows": "Gunsteling Vertonings",
"ValueSpecialEpisodeName": "Spesiale - {0}",
- "HeaderAlbumArtists": "Album Kunstenaars",
+ "HeaderAlbumArtists": "Kunstenaars se Album",
"Books": "Boeke",
"HeaderNextUp": "Volgende",
"Movies": "Flieks",
"Shows": "Televisie Reekse",
"HeaderContinueWatching": "Kyk Verder",
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
- "Photos": "Fotos",
+ "Photos": "Foto's",
"Playlists": "Snitlyste",
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
"HeaderFavoriteAlbums": "Gunsteling Albums",
"Sync": "Sinkroniseer",
"HeaderFavoriteSongs": "Gunsteling Liedjies",
"Songs": "Liedjies",
- "DeviceOnlineWithName": "{0} gekoppel is",
+ "DeviceOnlineWithName": "{0} is gekoppel",
"DeviceOfflineWithName": "{0} is ontkoppel",
"Collections": "Versamelings",
"Inherit": "Ontvang",
@@ -71,7 +71,7 @@
"NameSeasonUnknown": "Seisoen Onbekend",
"NameSeasonNumber": "Seisoen {0}",
"NameInstallFailed": "{0} installering het misluk",
- "MusicVideos": "Musiek videos",
+ "MusicVideos": "Musiek Videos",
"Music": "Musiek",
"MixedContent": "Gemengde inhoud",
"MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
@@ -79,15 +79,15 @@
"MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
"MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
"Latest": "Nuutste",
- "LabelRunningTimeValue": "Lopende tyd: {0}",
+ "LabelRunningTimeValue": "Werktyd: {0}",
"LabelIpAddressValue": "IP adres: {0}",
"ItemRemovedWithName": "{0} is uit versameling verwyder",
- "ItemAddedWithName": "{0} is in die versameling",
- "HomeVideos": "Tuis opnames",
+ "ItemAddedWithName": "{0} is by die versameling gevoeg",
+ "HomeVideos": "Tuis Videos",
"HeaderRecordingGroups": "Groep Opnames",
"Genres": "Genres",
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
- "ChapterNameValue": "Hoofstuk",
+ "ChapterNameValue": "Hoofstuk {0}",
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
"Albums": "Albums",
@@ -117,5 +117,7 @@
"Forced": "Geforseer",
"Default": "Oorspronklik",
"TaskCleanActivityLogDescription": "Verwyder aktiwiteitsaantekeninge ouer as die opgestelde ouderdom.",
- "TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon"
+ "TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon",
+ "TaskOptimizeDatabaseDescription": "Komprimeer databasis en verkort vrye ruimte. As hierdie taak uitgevoer word nadat die media versameling geskandeer is of ander veranderings aangebring is wat databasisaanpassings impliseer, kan dit die prestasie verbeter.",
+ "TaskOptimizeDatabase": "Optimaliseer databasis"
}
From 6de2a519f2adcadb9125fd19bb37608adfd645f5 Mon Sep 17 00:00:00 2001
From: hoanghuy309
Date: Mon, 16 Aug 2021 16:22:07 +0000
Subject: [PATCH 078/118] Translated using Weblate (Vietnamese) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/
---
Emby.Server.Implementations/Localization/Core/vi.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 20ab1dd7db..3d69e418b9 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -3,7 +3,7 @@
"Favorites": "Yêu Thích",
"Folders": "Thư Mục",
"Genres": "Thể Loại",
- "HeaderAlbumArtists": "Tuyển Tập Nghệ sĩ",
+ "HeaderAlbumArtists": "Album Nghệ sĩ",
"HeaderContinueWatching": "Xem Tiếp",
"HeaderLiveTV": "TV Trực Tiếp",
"Movies": "Phim",
@@ -82,7 +82,7 @@
"NameSeasonUnknown": "Không Rõ Mùa",
"NameSeasonNumber": "Phần {0}",
"NameInstallFailed": "{0} cài đặt thất bại",
- "MusicVideos": "Video Nhạc",
+ "MusicVideos": "Videos Nhạc",
"Music": "Nhạc",
"MixedContent": "Nội dung hỗn hợp",
"MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
From 6438410111c6022a2e1da53b04211757996ece90 Mon Sep 17 00:00:00 2001
From: Oatavandi
Date: Sat, 14 Aug 2021 15:54:24 +0000
Subject: [PATCH 079/118] Translated using Weblate (Malayalam) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ml/
---
Emby.Server.Implementations/Localization/Core/ml.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json
index 435f9b6305..09ef349136 100644
--- a/Emby.Server.Implementations/Localization/Core/ml.json
+++ b/Emby.Server.Implementations/Localization/Core/ml.json
@@ -103,7 +103,7 @@
"ValueSpecialEpisodeName": "പ്രത്യേക - {0}",
"Collections": "ശേഖരങ്ങൾ",
"Folders": "ഫോൾഡറുകൾ",
- "HeaderAlbumArtists": "ആൽബം ആർട്ടിസ്റ്റുകൾ",
+ "HeaderAlbumArtists": "കലാകാരന്റെ ആൽബം",
"Sync": "സമന്വയിപ്പിക്കുക",
"Movies": "സിനിമകൾ",
"Photos": "ഫോട്ടോകൾ",
From 468283bfb2cd7120b53e32cea67fd0c055d38812 Mon Sep 17 00:00:00 2001
From: cvium
Date: Wed, 18 Aug 2021 20:11:28 +0200
Subject: [PATCH 080/118] Move test to TypedBaseItem folder to avoid conflicts
---
.../{BaseItem => TypedBaseItem}/BaseItemKindTests.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
rename tests/Jellyfin.Server.Implementations.Tests/{BaseItem => TypedBaseItem}/BaseItemKindTests.cs (93%)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/BaseItem/BaseItemKindTests.cs b/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs
similarity index 93%
rename from tests/Jellyfin.Server.Implementations.Tests/BaseItem/BaseItemKindTests.cs
rename to tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs
index 061e81f30a..63d97b3cb2 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/BaseItem/BaseItemKindTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs
@@ -8,13 +8,13 @@ using Jellyfin.Data.Enums;
using Xunit;
using Xunit.Sdk;
-namespace Jellyfin.Server.Implementations.Tests.BaseItem
+namespace Jellyfin.Server.Implementations.Tests.TypedBaseItem
{
public class BaseItemKindTests
{
[Theory]
[ClassData(typeof(GetBaseItemDescendants))]
- public void BaseItemKindEnumTest(Type baseItemType)
+ public void EnumParse_GivenValidBaseItemType_ReturnsEnumValue(Type baseItemType)
{
var enumValue = Enum.Parse(baseItemType.Name);
Assert.True(Enum.IsDefined(typeof(BaseItemKind), enumValue));
@@ -22,7 +22,7 @@ namespace Jellyfin.Server.Implementations.Tests.BaseItem
[Theory]
[ClassData(typeof(GetBaseItemDescendants))]
- public void GetBaseKindEnumTest(Type baseItemDescendantType)
+ public void GetBaseItemKind_WhenCalledAfterDefaultCtor_DoesNotThrow(Type baseItemDescendantType)
{
var defaultConstructor = baseItemDescendantType.GetConstructor(Type.EmptyTypes);
var instance = (MediaBrowser.Controller.Entities.BaseItem)defaultConstructor!.Invoke(null);
From 50b3d74c953ea80af2cd2a276cf0475c5e907f27 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Sat, 21 Aug 2021 17:31:06 -0600
Subject: [PATCH 081/118] Switch to TheoryData and clean up tests
---
.../TypedBaseItem/BaseItemKindTests.cs | 94 +++++++------------
1 file changed, 33 insertions(+), 61 deletions(-)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs b/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs
index 63d97b3cb2..31f33c6825 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs
@@ -1,27 +1,45 @@
using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Reflection;
using Jellyfin.Data.Enums;
using Xunit;
-using Xunit.Sdk;
namespace Jellyfin.Server.Implementations.Tests.TypedBaseItem
{
public class BaseItemKindTests
{
+ public static TheoryData BaseItemKind_TestData()
+ {
+ var data = new TheoryData();
+
+ var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
+ foreach (var assembly in loadedAssemblies)
+ {
+ if (IsProjectAssemblyName(assembly.FullName))
+ {
+ var baseItemTypes = assembly.GetTypes()
+ .Where(targetType => targetType.IsClass
+ && !targetType.IsAbstract
+ && targetType.IsSubclassOf(typeof(MediaBrowser.Controller.Entities.BaseItem)));
+ foreach (var baseItemType in baseItemTypes)
+ {
+ data.Add(baseItemType);
+ }
+ }
+ }
+
+ return data;
+ }
+
[Theory]
- [ClassData(typeof(GetBaseItemDescendants))]
- public void EnumParse_GivenValidBaseItemType_ReturnsEnumValue(Type baseItemType)
+ [MemberData(nameof(BaseItemKind_TestData))]
+ public void EnumParse_GivenValidBaseItemType_ReturnsEnumValue(Type baseItemDescendantType)
{
- var enumValue = Enum.Parse(baseItemType.Name);
+ var enumValue = Enum.Parse(baseItemDescendantType.Name);
Assert.True(Enum.IsDefined(typeof(BaseItemKind), enumValue));
}
[Theory]
- [ClassData(typeof(GetBaseItemDescendants))]
+ [MemberData(nameof(BaseItemKind_TestData))]
public void GetBaseItemKind_WhenCalledAfterDefaultCtor_DoesNotThrow(Type baseItemDescendantType)
{
var defaultConstructor = baseItemDescendantType.GetConstructor(Type.EmptyTypes);
@@ -30,62 +48,16 @@ namespace Jellyfin.Server.Implementations.Tests.TypedBaseItem
Assert.Null(exception);
}
- private class GetBaseItemDescendants : IEnumerable
+ private static bool IsProjectAssemblyName(string? name)
{
- private static bool IsProjectAssemblyName(string? name)
+ if (name == null)
{
- if (name == null)
- {
- return false;
- }
-
- return name.StartsWith("Jellyfin", StringComparison.OrdinalIgnoreCase)
- || name.StartsWith("Emby", StringComparison.OrdinalIgnoreCase)
- || name.StartsWith("MediaBrowser", StringComparison.OrdinalIgnoreCase);
- }
-
- public IEnumerator GetEnumerator()
- {
- var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
- foreach (var assembly in loadedAssemblies)
- {
- if (IsProjectAssemblyName(assembly.FullName))
- {
- var baseItemTypes = assembly.GetTypes()
- .Where(targetType => targetType.IsClass
- && !targetType.IsAbstract
- && targetType.IsSubclassOf(typeof(MediaBrowser.Controller.Entities.BaseItem)));
- foreach (var baseItemType in baseItemTypes)
- {
- yield return new object?[] { baseItemType };
- }
- }
- }
-
- var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- if (path == null)
- {
- throw new NullException("Assembly location is null");
- }
-
- foreach (string dll in Directory.GetFiles(path, "*.dll"))
- {
- var assembly = Assembly.LoadFile(dll);
- if (IsProjectAssemblyName(assembly.FullName))
- {
- var baseItemTypes = assembly.GetTypes()
- .Where(targetType => targetType.IsClass
- && !targetType.IsAbstract
- && targetType.IsSubclassOf(typeof(MediaBrowser.Controller.Entities.BaseItem)));
- foreach (var baseItemType in baseItemTypes)
- {
- yield return new object?[] { baseItemType };
- }
- }
- }
+ return false;
}
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ return name.StartsWith("Jellyfin", StringComparison.OrdinalIgnoreCase)
+ || name.StartsWith("Emby", StringComparison.OrdinalIgnoreCase)
+ || name.StartsWith("MediaBrowser", StringComparison.OrdinalIgnoreCase);
}
}
}
From 7c7ad820c231426845a8c8d74c72c657eb0cf0cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Woli=C5=84ski?=
Date: Sun, 22 Aug 2021 01:17:46 +0000
Subject: [PATCH 082/118] Translated using Weblate (Polish) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/
---
Emby.Server.Implementations/Localization/Core/pl.json | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index 275bdec6e6..e8a32a13e8 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -15,7 +15,7 @@
"Favorites": "Ulubione",
"Folders": "Foldery",
"Genres": "Gatunki",
- "HeaderAlbumArtists": "Wykonawcy albumów",
+ "HeaderAlbumArtists": "Album artysty",
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteAlbums": "Ulubione albumy",
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Telewizja",
"HeaderNextUp": "Do obejrzenia",
"HeaderRecordingGroups": "Grupy nagrań",
- "HomeVideos": "Nagrania prywatne",
+ "HomeVideos": "Nagrania domowe",
"Inherit": "Dziedzicz",
"ItemAddedWithName": "{0} zostało dodane do biblioteki",
"ItemRemovedWithName": "{0} zostało usunięte z biblioteki",
@@ -119,5 +119,6 @@
"Undefined": "Nieustalony",
"Forced": "Wymuszony",
"Default": "Domyślne",
- "TaskOptimizeDatabase": "Optymalizuj bazę danych"
+ "TaskOptimizeDatabase": "Optymalizuj bazę danych",
+ "TaskOptimizeDatabaseDescription": "Kompaktuje bazę danych i obcina wolne miejsce. Uruchomienie tego zadania po przeskanowaniu biblioteki lub dokonaniu innych zmian, które pociągają za sobą modyfikacje bazy danych, może poprawić wydajność."
}
From 98b72019e697d0709991904610bbee3dd50f5d13 Mon Sep 17 00:00:00 2001
From: wolong gl
Date: Sun, 22 Aug 2021 06:42:03 +0000
Subject: [PATCH 083/118] Translated using Weblate (Chinese (Simplified))
Translation: Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/
---
Emby.Server.Implementations/Localization/Core/zh-CN.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index faa9c40e27..71adc0f6ee 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -7,7 +7,7 @@
"Books": "书籍",
"CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
"Channels": "频道",
- "ChapterNameValue": "第 {0} 集",
+ "ChapterNameValue": "章节 {0}",
"Collections": "合集",
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
@@ -108,8 +108,8 @@
"TaskCleanLogs": "清理日志目录",
"TaskRefreshLibraryDescription": "扫描你的媒体库以获取新文件并刷新元数据。",
"TaskRefreshLibrary": "扫描媒体库",
- "TaskRefreshChapterImagesDescription": "为包含剧集的视频提取缩略图。",
- "TaskRefreshChapterImages": "提取剧集图片",
+ "TaskRefreshChapterImagesDescription": "为包含章节的视频提取缩略图。",
+ "TaskRefreshChapterImages": "提取章节图片",
"TaskCleanCacheDescription": "删除系统不再需要的缓存文件。",
"TaskCleanCache": "清理缓存目录",
"TasksApplicationCategory": "应用程序",
From efed4728e6ce613727257776d990a0bd60b85dae Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 23 Aug 2021 12:00:54 +0000
Subject: [PATCH 084/118] Bump prometheus-net from 4.2.0 to 5.0.1
Bumps [prometheus-net](https://github.com/prometheus-net/prometheus-net) from 4.2.0 to 5.0.1.
- [Release notes](https://github.com/prometheus-net/prometheus-net/releases)
- [Changelog](https://github.com/prometheus-net/prometheus-net/blob/master/History)
- [Commits](https://github.com/prometheus-net/prometheus-net/compare/v4.2.0...v5.0.1)
---
updated-dependencies:
- dependency-name: prometheus-net
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
Jellyfin.Server/Jellyfin.Server.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index ea64663bd7..11342adba2 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -35,7 +35,7 @@
-
+
From 74d22d2f9afbee54c88e65866d7fbba258f62557 Mon Sep 17 00:00:00 2001
From: Kaiay
Date: Tue, 24 Aug 2021 21:02:30 +0000
Subject: [PATCH 085/118] Translated using Weblate (Chinese (Hong Kong))
Translation: Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/
---
Emby.Server.Implementations/Localization/Core/zh-HK.json | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 3dad21dcbc..1cc97bc27e 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -13,7 +13,7 @@
"DeviceOnlineWithName": "{0} 已經連接",
"FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗",
"Favorites": "我的最愛",
- "Folders": "檔案夾",
+ "Folders": "資料夾",
"Genres": "風格",
"HeaderAlbumArtists": "專輯藝人",
"HeaderContinueWatching": "繼續觀看",
@@ -39,7 +39,7 @@
"MixedContent": "混合內容",
"Movies": "電影",
"Music": "音樂",
- "MusicVideos": "音樂視頻",
+ "MusicVideos": "音樂影片",
"NameInstallFailed": "{0} 安裝失敗",
"NameSeasonNumber": "第 {0} 季",
"NameSeasonUnknown": "未知季數",
@@ -117,5 +117,8 @@
"TaskCleanActivityLog": "清理活動記錄",
"Undefined": "未定義",
"Forced": "強制",
- "Default": "預設"
+ "Default": "預設",
+ "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。",
+ "TaskOptimizeDatabase": "最佳化數據庫",
+ "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。"
}
From d3527334bc449df0d16fbf5554236fc42df23372 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?=
Date: Wed, 25 Aug 2021 07:33:23 +0000
Subject: [PATCH 086/118] Translated using Weblate (Czech) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/
---
Emby.Server.Implementations/Localization/Core/cs.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 62b2b6328c..4f1d231a4a 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -15,7 +15,7 @@
"Favorites": "Oblíbené",
"Folders": "Složky",
"Genres": "Žánry",
- "HeaderAlbumArtists": "Umělci alba",
+ "HeaderAlbumArtists": "Album umělce",
"HeaderContinueWatching": "Pokračovat ve sledování",
"HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti",
From 6c7b9d4044a63b7b6bdef5638642c7dec7c7b401 Mon Sep 17 00:00:00 2001
From: Weevild
Date: Tue, 24 Aug 2021 19:33:42 +0000
Subject: [PATCH 087/118] Translated using Weblate (Swedish) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/
---
Emby.Server.Implementations/Localization/Core/sv.json | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 88b182f8d9..6c772c6a24 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -15,7 +15,7 @@
"Favorites": "Favoriter",
"Folders": "Mappar",
"Genres": "Genrer",
- "HeaderAlbumArtists": "Albumartister",
+ "HeaderAlbumArtists": "Artistens album",
"HeaderContinueWatching": "Fortsätt kolla",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Nästa",
"HeaderRecordingGroups": "Inspelningsgrupper",
- "HomeVideos": "Hemvideor",
+ "HomeVideos": "Hemmavideor",
"Inherit": "Ärv",
"ItemAddedWithName": "{0} lades till i biblioteket",
"ItemRemovedWithName": "{0} togs bort från biblioteket",
@@ -119,5 +119,6 @@
"Undefined": "odefinierad",
"Forced": "Tvingad",
"Default": "Standard",
- "TaskOptimizeDatabase": "Optimera databasen"
+ "TaskOptimizeDatabase": "Optimera databasen",
+ "TaskOptimizeDatabaseDescription": "Komprimerar databasen och trunkerar ledigt utrymme. Prestandan kan förbättras genom att köra denna task efter att du har skannat biblioteket eller gjort andra förändringar som indikerar att databasen har modifierats."
}
From 3ebede56fe2f7e063d59498a5c38c2037ecca031 Mon Sep 17 00:00:00 2001
From: lsousa
Date: Wed, 25 Aug 2021 15:32:17 +0000
Subject: [PATCH 088/118] Translated using Weblate (Portuguese) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/
---
Emby.Server.Implementations/Localization/Core/pt.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index b435672adf..474dacd7cc 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -117,5 +117,6 @@
"Undefined": "Indefinido",
"Forced": "Forçado",
"Default": "Predefinição",
- "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado."
+ "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado.",
+ "TaskOptimizeDatabase": "Otimizar base de dados"
}
From 80dc92238a8976002467aeb0ac6ab714b3be03bc Mon Sep 17 00:00:00 2001
From: Changho Park
Date: Thu, 26 Aug 2021 01:10:08 +0000
Subject: [PATCH 089/118] Translated using Weblate (Korean) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/
---
Emby.Server.Implementations/Localization/Core/ko.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 409b4d26b3..a37de07488 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -15,7 +15,7 @@
"Favorites": "즐겨찾기",
"Folders": "폴더",
"Genres": "장르",
- "HeaderAlbumArtists": "앨범 아티스트",
+ "HeaderAlbumArtists": "아티스트의 앨범",
"HeaderContinueWatching": "계속 시청하기",
"HeaderFavoriteAlbums": "즐겨찾는 앨범",
"HeaderFavoriteArtists": "즐겨찾는 아티스트",
From c2bcceaa212e8e94f9987d37e0c53d4ac58f6a0f Mon Sep 17 00:00:00 2001
From: SeanPai
Date: Tue, 24 Aug 2021 09:55:28 +0000
Subject: [PATCH 090/118] Translated using Weblate (Chinese (Traditional))
Translation: Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/
---
Emby.Server.Implementations/Localization/Core/zh-TW.json | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index c3b223f631..585d81450a 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -24,7 +24,7 @@
"HeaderFavoriteSongs": "最愛歌曲",
"HeaderLiveTV": "電視直播",
"HeaderNextUp": "接下來",
- "HomeVideos": "自製影片",
+ "HomeVideos": "家庭影片",
"ItemAddedWithName": "{0} 已新增至媒體庫",
"ItemRemovedWithName": "{0} 已從媒體庫移除",
"LabelIpAddressValue": "IP 位址:{0}",
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "清除活動紀錄",
"Undefined": "未定義的",
"Forced": "強制",
- "Default": "原本"
+ "Default": "原本",
+ "TaskOptimizeDatabaseDescription": "縮小資料庫並釋放可用空間。在掃描資料庫或進行資料庫相關的更動後使用此功能會增加效能。",
+ "TaskOptimizeDatabase": "最佳化資料庫"
}
From 5bcd1648cb4dfe617599ec2b2fe5e5f8a461ec61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vin=C3=ADcius=20Costa?=
Date: Tue, 24 Aug 2021 00:04:37 +0000
Subject: [PATCH 091/118] Translated using Weblate (Portuguese (Brazil))
Translation: Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/
---
Emby.Server.Implementations/Localization/Core/pt-BR.json | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 323dcced07..be71289b1b 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Limpar Registro de Atividades",
"Undefined": "Indefinido",
"Forced": "Forçado",
- "Default": "Padrão"
+ "Default": "Padrão",
+ "TaskOptimizeDatabaseDescription": "Compactar base de dados e liberar espaço livre. Executar esta tarefa após realizar mudanças que impliquem em modificações da base de dados pode trazer melhorias de desempenho.",
+ "TaskOptimizeDatabase": "Otimizar base de dados"
}
From 59b8056addad8ec33b3785bc748cb4e97c4910da Mon Sep 17 00:00:00 2001
From: wolong gl
Date: Wed, 25 Aug 2021 06:29:01 +0000
Subject: [PATCH 092/118] Translated using Weblate (Chinese (Simplified))
Translation: Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/
---
Emby.Server.Implementations/Localization/Core/zh-CN.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 71adc0f6ee..f9df627245 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -15,8 +15,8 @@
"Favorites": "我的最爱",
"Folders": "文件夹",
"Genres": "风格",
- "HeaderAlbumArtists": "专辑作家",
- "HeaderContinueWatching": "继续观影",
+ "HeaderAlbumArtists": "专辑艺术家",
+ "HeaderContinueWatching": "继续观看",
"HeaderFavoriteAlbums": "收藏的专辑",
"HeaderFavoriteArtists": "最爱的艺术家",
"HeaderFavoriteEpisodes": "最爱的剧集",
From 41712e16becca7c9f483503eb7ce6a9fabb1c88a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 27 Aug 2021 15:32:56 +0000
Subject: [PATCH 093/118] Bump prometheus-net.AspNetCore from 4.2.0 to 5.0.1
Bumps [prometheus-net.AspNetCore](https://github.com/prometheus-net/prometheus-net) from 4.2.0 to 5.0.1.
- [Release notes](https://github.com/prometheus-net/prometheus-net/releases)
- [Changelog](https://github.com/prometheus-net/prometheus-net/blob/master/History)
- [Commits](https://github.com/prometheus-net/prometheus-net/compare/v4.2.0...v5.0.1)
---
updated-dependencies:
- dependency-name: prometheus-net.AspNetCore
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
Jellyfin.Server/Jellyfin.Server.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 11342adba2..c4d3d8a1f3 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -36,7 +36,7 @@
-
+
From e88367fe70f8d5f2a0cec35792739fe23000a818 Mon Sep 17 00:00:00 2001
From: TheFeelTrain
Date: Fri, 27 Aug 2021 11:28:28 +0000
Subject: [PATCH 094/118] Translated using Weblate (Japanese) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/
---
Emby.Server.Implementations/Localization/Core/ja.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
index 0d90ad31cc..c689bc58a5 100644
--- a/Emby.Server.Implementations/Localization/Core/ja.json
+++ b/Emby.Server.Implementations/Localization/Core/ja.json
@@ -15,7 +15,7 @@
"Favorites": "お気に入り",
"Folders": "フォルダー",
"Genres": "ジャンル",
- "HeaderAlbumArtists": "アルバムアーティスト",
+ "HeaderAlbumArtists": "アーティストのアルバム",
"HeaderContinueWatching": "視聴を続ける",
"HeaderFavoriteAlbums": "お気に入りのアルバム",
"HeaderFavoriteArtists": "お気に入りのアーティスト",
From 645825db36f2de9771aa5d0ebcdab25855d18b5d Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sat, 28 Aug 2021 17:32:09 +0200
Subject: [PATCH 095/118] Enable nullable for more files
---
.../Collections/CollectionManager.cs | 2 +-
.../Library/LibraryManager.cs | 5 +-
.../LiveTv/EmbyTV/DirectRecorder.cs | 8 ++--
.../LiveTv/EmbyTV/EmbyTV.cs | 47 ++++++++-----------
.../LiveTv/EmbyTV/EncodedRecorder.cs | 5 --
.../LiveTv/EmbyTV/EpgChannelData.cs | 1 -
.../LiveTv/EmbyTV/IRecorder.cs | 2 +-
.../LiveTv/EmbyTV/TimerManager.cs | 12 ++---
.../LiveTv/Listings/SchedulesDirect.cs | 2 +-
.../Controllers/LibraryStructureController.cs | 4 +-
.../Configuration/MediaPathInfo.cs | 8 +++-
.../Configuration/NfoConfigurationFactory.cs | 4 +-
12 files changed, 42 insertions(+), 58 deletions(-)
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 8270c2e84c..b00a519229 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -95,7 +95,7 @@ namespace Emby.Server.Implementations.Collections
var libraryOptions = new LibraryOptions
{
- PathInfos = new[] { new MediaPathInfo { Path = path } },
+ PathInfos = new[] { new MediaPathInfo(path) },
EnableRealtimeMonitor = false,
SaveLocalMetadata = true
};
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 13fb8b2fd5..0cde8101c5 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -3173,10 +3173,7 @@ namespace Emby.Server.Implementations.Library
{
if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
{
- list.Add(new MediaPathInfo
- {
- Path = location
- });
+ list.Add(new MediaPathInfo(location));
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 3fcadf5b1b..bb3d635d12 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -33,7 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return targetFile;
}
- public Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+ public Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
if (directStreamProvider != null)
{
@@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private async Task RecordFromDirectStreamProvider(IDirectStreamProvider directStreamProvider, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
- Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
+ Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None))
@@ -71,7 +69,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogInformation("Opened recording stream from tuner provider");
- Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
+ Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 7970631201..f2b9f3cb90 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -159,8 +159,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
var recordingFolders = GetRecordingFolders().ToArray();
- var virtualFolders = _libraryManager.GetVirtualFolders()
- .ToList();
+ var virtualFolders = _libraryManager.GetVirtualFolders();
var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
@@ -177,7 +176,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
continue;
}
- var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray();
+ var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo(i)).ToArray();
var libraryOptions = new LibraryOptions
{
@@ -210,7 +209,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var path in pathsToRemove)
{
- await RemovePathFromLibrary(path).ConfigureAwait(false);
+ await RemovePathFromLibraryAsync(path).ConfigureAwait(false);
}
}
catch (Exception ex)
@@ -219,13 +218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private async Task RemovePathFromLibrary(string path)
+ private async Task RemovePathFromLibraryAsync(string path)
{
_logger.LogDebug("Removing path from library: {0}", path);
var requiresRefresh = false;
- var virtualFolders = _libraryManager.GetVirtualFolders()
- .ToList();
+ var virtualFolders = _libraryManager.GetVirtualFolders();
foreach (var virtualFolder in virtualFolders)
{
@@ -460,7 +458,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (!string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId))
{
var tunerChannelId = tunerChannel.TunerChannelId;
- if (tunerChannelId.IndexOf(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase) != -1)
+ if (tunerChannelId.Contains(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase))
{
tunerChannelId = tunerChannelId.Replace(".json.schedulesdirect.org", string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart('I');
}
@@ -620,8 +618,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (existingTimer != null)
{
- if (existingTimer.Status == RecordingStatus.Cancelled ||
- existingTimer.Status == RecordingStatus.Completed)
+ if (existingTimer.Status == RecordingStatus.Cancelled
+ || existingTimer.Status == RecordingStatus.Completed)
{
existingTimer.Status = RecordingStatus.New;
existingTimer.IsManual = true;
@@ -913,18 +911,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var epgChannel = await GetEpgChannelFromTunerChannel(provider.Item1, provider.Item2, channel, cancellationToken).ConfigureAwait(false);
- List programs;
-
if (epgChannel == null)
{
_logger.LogDebug("EPG channel not found for tuner channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
- programs = new List();
+ continue;
}
- else
- {
- programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
+
+ List programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
.ConfigureAwait(false)).ToList();
- }
// Replace the value that came from the provider with a normalized value
foreach (var program in programs)
@@ -940,7 +934,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- return new List();
+ return Enumerable.Empty();
}
private List> GetListingProviders()
@@ -1292,7 +1286,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogInformation("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
- _logger.LogInformation("Writing file to path: " + recordPath);
+ _logger.LogInformation("Writing file to: {Path}", recordPath);
Action onStarted = async () =>
{
@@ -1417,13 +1411,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void TriggerRefresh(string path)
{
- _logger.LogInformation("Triggering refresh on {path}", path);
+ _logger.LogInformation("Triggering refresh on {Path}", path);
var item = GetAffectedBaseItem(Path.GetDirectoryName(path));
if (item != null)
{
- _logger.LogInformation("Refreshing recording parent {path}", item.Path);
+ _logger.LogInformation("Refreshing recording parent {Path}", item.Path);
_providerManager.QueueRefresh(
item.Id,
@@ -1458,7 +1452,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (item.GetType() == typeof(Folder) && string.Equals(item.Path, parentPath, StringComparison.OrdinalIgnoreCase))
{
var parentItem = item.GetParent();
- if (parentItem != null && !(parentItem is AggregateFolder))
+ if (parentItem != null && parentItem is not AggregateFolder)
{
item = parentItem;
}
@@ -1512,8 +1506,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
DeleteLibraryItemsForTimers(timersToDelete);
- var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder;
- if (librarySeries == null)
+ if (_libraryManager.FindByPath(seriesPath, true) is not Folder librarySeries)
{
return;
}
@@ -1667,7 +1660,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- process.Exited += Process_Exited;
+ process.Exited += OnProcessExited;
process.Start();
}
catch (Exception ex)
@@ -1681,7 +1674,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return arguments.Replace("{path}", path, StringComparison.OrdinalIgnoreCase);
}
- private void Process_Exited(object sender, EventArgs e)
+ private void OnProcessExited(object sender, EventArgs e)
{
using (var process = (Process)sender)
{
@@ -2239,7 +2232,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var enabledTimersForSeries = new List();
foreach (var timer in allTimers)
{
- var existingTimer = _timerProvider.GetTimer(timer.Id)
+ var existingTimer = _timerProvider.GetTimer(timer.Id)
?? (string.IsNullOrWhiteSpace(timer.ProgramId)
? null
: _timerProvider.GetTimerByProgramId(timer.ProgramId));
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 93781cb7ba..e10bc76470 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -319,11 +319,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
}
- catch (ObjectDisposedException)
- {
- // TODO Investigate and properly fix.
- // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
- }
catch (Exception ex)
{
_logger.LogError(ex, "Error reading ffmpeg recording log");
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
index 0ec52a9598..20a8213a77 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
@@ -8,7 +8,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
internal class EpgChannelData
{
-
private readonly Dictionary _channelsById;
private readonly Dictionary _channelsByNumber;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
index 4712724d67..dfe3517b22 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
///
/// Records the specified media source.
///
- Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
+ Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
string GetOutputPath(MediaSourceInfo mediaSource, string targetFile);
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 6c52a9a73d..a861e6ae44 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -23,7 +21,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
}
- public event EventHandler> TimerFired;
+ public event EventHandler>? TimerFired;
public void RestartTimers()
{
@@ -145,9 +143,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private void TimerCallback(object state)
+ private void TimerCallback(object? state)
{
- var timerId = (string)state;
+ var timerId = (string?)state ?? throw new ArgumentNullException(nameof(state));
var timer = GetAll().FirstOrDefault(i => string.Equals(i.Id, timerId, StringComparison.OrdinalIgnoreCase));
if (timer != null)
@@ -156,12 +154,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- public TimerInfo GetTimer(string id)
+ public TimerInfo? GetTimer(string id)
{
return GetAll().FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
}
- public TimerInfo GetTimerByProgramId(string programId)
+ public TimerInfo? GetTimerByProgramId(string programId)
{
return GetAll().FirstOrDefault(r => string.Equals(r.ProgramId, programId, StringComparison.OrdinalIgnoreCase));
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index b7639a51ce..d029d56c87 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -14,8 +14,8 @@ using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common;
using Jellyfin.Extensions.Json;
+using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Cryptography;
diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs
index be9127dd39..ec1170411c 100644
--- a/Jellyfin.Api/Controllers/LibraryStructureController.cs
+++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs
@@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers
if (paths != null && paths.Length > 0)
{
- libraryOptions.PathInfos = paths.Select(i => new MediaPathInfo { Path = i }).ToArray();
+ libraryOptions.PathInfos = paths.Select(i => new MediaPathInfo(i)).ToArray();
}
await _libraryManager.AddVirtualFolder(name, collectionType, libraryOptions, refreshLibrary).ConfigureAwait(false);
@@ -212,7 +212,7 @@ namespace Jellyfin.Api.Controllers
try
{
- var mediaPath = mediaPathDto.PathInfo ?? new MediaPathInfo { Path = mediaPathDto.Path };
+ var mediaPath = mediaPathDto.PathInfo ?? new MediaPathInfo(mediaPathDto.Path ?? throw new ArgumentException("PathInfo and Path can't both be null."));
_libraryManager.AddMediaPath(mediaPathDto.Name, mediaPath);
}
diff --git a/MediaBrowser.Model/Configuration/MediaPathInfo.cs b/MediaBrowser.Model/Configuration/MediaPathInfo.cs
index 4f311c58f0..d096defcb8 100644
--- a/MediaBrowser.Model/Configuration/MediaPathInfo.cs
+++ b/MediaBrowser.Model/Configuration/MediaPathInfo.cs
@@ -1,12 +1,16 @@
-#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
{
public class MediaPathInfo
{
+ public MediaPathInfo(string path)
+ {
+ Path = path;
+ }
+
public string Path { get; set; }
- public string NetworkPath { get; set; }
+ public string? NetworkPath { get; set; }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationFactory.cs b/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationFactory.cs
index 8325bfdbd5..c73989e761 100644
--- a/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationFactory.cs
+++ b/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationFactory.cs
@@ -15,8 +15,8 @@ namespace MediaBrowser.XbmcMetadata.Configuration
{
new ConfigurationStore
{
- ConfigurationType = typeof(XbmcMetadataOptions),
- Key = "xbmcmetadata"
+ ConfigurationType = typeof(XbmcMetadataOptions),
+ Key = "xbmcmetadata"
}
};
}
From 909573f2f142ca2f5e1e54093c820b9f9ebb6168 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sat, 28 Aug 2021 19:30:57 +0200
Subject: [PATCH 096/118] Add tests for LibraryStructureController
---
.../MediaStructureControllerTests.cs | 83 +++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
new file mode 100644
index 0000000000..9f189aaab9
--- /dev/null
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
@@ -0,0 +1,83 @@
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Net.Mime;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Jellyfin.Api.Models.LibraryStructureDto;
+using Jellyfin.Extensions.Json;
+using Xunit;
+
+namespace Jellyfin.Server.Integration.Tests.Controllers
+{
+ public sealed class MediaStructureControllerTests : IClassFixture
+ {
+ private readonly JellyfinApplicationFactory _factory;
+ private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
+ private static string? _accessToken;
+
+ public MediaStructureControllerTests(JellyfinApplicationFactory factory)
+ {
+ _factory = factory;
+ }
+
+ [Fact]
+ public async Task AddMediaPath_PathDoesntExist_ReturnsNotFound()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
+
+ var data = new MediaPathDto()
+ {
+ Name = "Test",
+ Path = "/this/path/doesnt/exist"
+ };
+
+ using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions));
+ postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
+ var response = await client.PostAsync("Library/VirtualFolders/Paths", postContent).ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task UpdateMediaPath_EmptyName_ReturnsBadRequest()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
+
+ var data = new UpdateMediaPathRequestDto()
+ {
+ Name = string.Empty,
+ };
+
+ using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions));
+ postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
+ var response = await client.PostAsync("Library/VirtualFolders/Paths/Update", postContent).ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task RemoveMediaPath_EmptyName_ReturnsBadRequest()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
+
+ var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=").ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task RemoveMediaPath_PathDoesntExist_ReturnsNotFound()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
+
+ var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=none&path=%2Fthis%2Fpath%2Fdoesnt%2Fexist").ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+ }
+}
From 69ba539c700c502f189c9067c31860c889aed1f7 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sat, 28 Aug 2021 20:12:25 +0200
Subject: [PATCH 097/118] Add more tests for LibraryStructureController
---
.../MediaStructureControllerTests.cs | 41 ++++++++++++++++++-
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
index 9f189aaab9..5855f0266f 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
@@ -21,6 +22,42 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
_factory = factory;
}
+ [Fact]
+ public async Task RenameVirtualFolder_WhiteSpaceName_ReturnsBadRequest()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
+
+ using var postContent = new ByteArrayContent(Array.Empty());
+ var response = await client.PostAsync("Library/VirtualFolders/Name?name=+&newName=test", postContent).ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task RenameVirtualFolder_WhiteSpaceNewName_ReturnsBadRequest()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
+
+ using var postContent = new ByteArrayContent(Array.Empty());
+ var response = await client.PostAsync("Library/VirtualFolders/Name?name=test&newName=+", postContent).ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task RenameVirtualFolder_NameDoesntExist_ReturnsNotFound()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
+
+ using var postContent = new ByteArrayContent(Array.Empty());
+ var response = await client.PostAsync("Library/VirtualFolders/Name?name=doesnt+exist&newName=test", postContent).ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
[Fact]
public async Task AddMediaPath_PathDoesntExist_ReturnsNotFound()
{
@@ -59,12 +96,12 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
}
[Fact]
- public async Task RemoveMediaPath_EmptyName_ReturnsBadRequest()
+ public async Task RemoveMediaPath_WhiteSpaceName_ReturnsBadRequest()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
- var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=").ConfigureAwait(false);
+ var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=+").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
From 0f7c875076e29f0c86c536840c71cec29fab08fb Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sat, 28 Aug 2021 20:34:04 +0200
Subject: [PATCH 098/118] Fix test
---
.../Controllers/MediaStructureControllerTests.cs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
index 5855f0266f..8af43050f6 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
@@ -7,6 +7,7 @@ using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Api.Models.LibraryStructureDto;
using Jellyfin.Extensions.Json;
+using MediaBrowser.Model.Configuration;
using Xunit;
namespace Jellyfin.Server.Integration.Tests.Controllers
@@ -78,14 +79,18 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
}
[Fact]
- public async Task UpdateMediaPath_EmptyName_ReturnsBadRequest()
+ public async Task UpdateMediaPath_WhiteSpaceName_ReturnsBadRequest()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var data = new UpdateMediaPathRequestDto()
{
- Name = string.Empty,
+ Name = " ",
+ PathInfo = new MediaPathInfo
+ {
+ Path = "test"
+ }
};
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions));
From cba07b1ca6862ef8450021b0e60ce2f366ff0d33 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Sat, 28 Aug 2021 16:32:50 -0600
Subject: [PATCH 099/118] Remove more and more warnings
---
Emby.Dlna/ContentDirectory/ServerItem.cs | 2 +-
Emby.Dlna/Didl/DidlBuilder.cs | 2 +-
.../ApplicationHost.cs | 3 +-
.../Channels/ChannelManager.cs | 6 +-
.../Data/BaseSqliteRepository.cs | 2 +-
.../Data/SqliteItemRepository.cs | 9 +-
.../Data/SqliteUserDataRepository.cs | 8 +-
Emby.Server.Implementations/Dto/DtoService.cs | 41 +-
.../EntryPoints/LibraryChangedNotifier.cs | 4 +-
.../EntryPoints/UdpServerEntryPoint.cs | 3 +
.../Security/AuthorizationContext.cs | 2 +
.../HttpServer/WebSocketConnection.cs | 4 +-
.../IStartupOptions.cs | 2 +-
.../Images/CollectionFolderImageProvider.cs | 12 +-
.../Library/LibraryManager.cs | 8 +-
.../Library/Resolvers/Audio/AudioResolver.cs | 12 +-
.../Library/Resolvers/BaseVideoResolver.cs | 26 +-
...deoResolver.cs => GenericVideoResolver.cs} | 0
.../Library/Resolvers/ItemResolver.cs | 2 +-
.../Library/Resolvers/Movies/MovieResolver.cs | 4 +-
.../Library/Resolvers/PlaylistResolver.cs | 3 +-
.../Library/Validators/StudiosValidator.cs | 9 +-
.../LiveTv/EmbyTV/EmbyTV.cs | 2 +-
.../LiveTv/EmbyTV/EpgChannelData.cs | 1 -
.../LiveTv/Listings/SchedulesDirect.cs | 620 ++++--------------
.../SchedulesDirectDtos/BroadcasterDto.cs | 36 +
.../SchedulesDirectDtos/CaptionDto.cs | 24 +
.../Listings/SchedulesDirectDtos/CastDto.cs | 48 ++
.../SchedulesDirectDtos/ChannelDto.cs | 31 +
.../SchedulesDirectDtos/ContentRatingDto.cs | 24 +
.../Listings/SchedulesDirectDtos/CrewDto.cs | 42 ++
.../Listings/SchedulesDirectDtos/DayDto.cs | 39 ++
.../SchedulesDirectDtos/Description1000Dto.cs | 24 +
.../SchedulesDirectDtos/Description100Dto.cs | 24 +
.../DescriptionsProgramDto.cs | 25 +
.../SchedulesDirectDtos/EventDetailsDto.cs | 18 +
.../SchedulesDirectDtos/GracenoteDto.cs | 24 +
.../SchedulesDirectDtos/HeadendsDto.cs | 37 ++
.../SchedulesDirectDtos/ImageDataDto.cs | 69 ++
.../Listings/SchedulesDirectDtos/LineupDto.cs | 42 ++
.../SchedulesDirectDtos/LineupsDto.cs | 37 ++
.../Listings/SchedulesDirectDtos/LogoDto.cs | 36 +
.../Listings/SchedulesDirectDtos/MapDto.cs | 48 ++
.../SchedulesDirectDtos/MetadataDto.cs | 30 +
.../MetadataProgramsDto.cs | 18 +
.../MetadataScheduleDto.cs | 42 ++
.../Listings/SchedulesDirectDtos/MovieDto.cs | 31 +
.../SchedulesDirectDtos/MultipartDto.cs | 24 +
.../SchedulesDirectDtos/ProgramDetailsDto.cs | 157 +++++
.../SchedulesDirectDtos/ProgramDto.cs | 91 +++
.../SchedulesDirectDtos/QualityRatingDto.cs | 42 ++
.../Listings/SchedulesDirectDtos/RatingDto.cs | 24 +
.../SchedulesDirectDtos/RecommendationDto.cs | 24 +
.../RequestScheduleForChannelDto.cs | 25 +
.../SchedulesDirectDtos/ShowImagesDto.cs | 22 +
.../SchedulesDirectDtos/StationDto.cs | 67 ++
.../Listings/SchedulesDirectDtos/TitleDto.cs | 18 +
.../Listings/SchedulesDirectDtos/TokenDto.cs | 36 +
.../LiveTv/LiveTvManager.cs | 6 +-
.../TunerHosts/HdHomerun/HdHomerunManager.cs | 1 +
.../Playlists/PlaylistManager.cs | 4 +-
.../ScheduledTasks/ScheduledTaskWorker.cs | 3 +-
.../Sorting/ArtistComparer.cs | 2 +-
.../Controllers/ItemUpdateController.cs | 10 +-
Jellyfin.Api/Controllers/ItemsController.cs | 4 +-
Jellyfin.Api/Controllers/LibraryController.cs | 2 +-
Jellyfin.Api/Controllers/TvShowsController.cs | 8 +-
Jellyfin.Api/Helpers/StreamingHelpers.cs | 4 -
MediaBrowser.Controller/Entities/BaseItem.cs | 4 +-
MediaBrowser.Controller/Entities/Folder.cs | 10 +-
MediaBrowser.Controller/Entities/TV/Series.cs | 2 +-
.../MediaEncoding/EncodingHelper.cs | 12 +-
.../MediaEncoding/EncodingJobInfo.cs | 6 +-
.../Images/LocalImageProvider.cs | 2 +-
.../Savers/BaseXmlSaver.cs | 8 +-
MediaBrowser.Model/Dlna/MediaFormatProfile.cs | 2 +-
.../Dlna/ResolutionNormalizer.cs | 4 -
MediaBrowser.Model/Dlna/StreamBuilder.cs | 33 +-
MediaBrowser.Model/Dlna/StreamInfo.cs | 2 +-
MediaBrowser.Model/Entities/MediaStream.cs | 2 +-
MediaBrowser.Model/IO/IFileSystem.cs | 4 +-
MediaBrowser.Providers/Manager/ImageSaver.cs | 2 +-
.../Manager/ItemImageProvider.cs | 4 +-
.../Manager/MetadataService.cs | 6 +-
.../Manager/ProviderManager.cs | 10 +-
.../Manager/ProviderUtils.cs | 2 +-
.../Savers/BaseNfoSaver.cs | 6 +-
.../Savers/MovieNfoSaver.cs | 2 +-
.../Savers/SeasonNfoSaver.cs | 2 +-
89 files changed, 1554 insertions(+), 681 deletions(-)
rename Emby.Server.Implementations/Library/Resolvers/{VideoResolver.cs => GenericVideoResolver.cs} (100%)
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs
create mode 100644 Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs
diff --git a/Emby.Dlna/ContentDirectory/ServerItem.cs b/Emby.Dlna/ContentDirectory/ServerItem.cs
index 34244000c1..ff30e6e4af 100644
--- a/Emby.Dlna/ContentDirectory/ServerItem.cs
+++ b/Emby.Dlna/ContentDirectory/ServerItem.cs
@@ -17,7 +17,7 @@ namespace Emby.Dlna.ContentDirectory
{
Item = item;
- if (item is IItemByName && !(item is Folder))
+ if (item is IItemByName && item is not Folder)
{
StubType = Dlna.ContentDirectory.StubType.Folder;
}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 2982ce97e1..c000784997 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -748,7 +748,7 @@ namespace Emby.Dlna.Didl
AddValue(writer, "upnp", "publisher", studio, NsUpnp);
}
- if (!(item is Folder))
+ if (item is not Folder)
{
if (filter.Contains("dc:description"))
{
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index bf7ddace2d..b640f06c6e 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -456,6 +456,7 @@ namespace Emby.Server.Implementations
///
/// Runs the startup tasks.
///
+ /// The cancellation token.
/// .
public async Task RunStartupTasksAsync(CancellationToken cancellationToken)
{
@@ -469,7 +470,7 @@ namespace Emby.Server.Implementations
_mediaEncoder.SetFFmpegPath();
- Logger.LogInformation("ServerId: {0}", SystemId);
+ Logger.LogInformation("ServerId: {ServerId}", SystemId);
var entryPoints = GetExports();
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index aa54510a71..41d1f9b392 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Channels
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
- return !(channel is IDisableMediaSourceDisplay);
+ return channel is not IDisableMediaSourceDisplay;
}
///
@@ -1079,11 +1079,11 @@ namespace Emby.Server.Implementations.Channels
// was used for status
// if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
- //{
+ // {
// item.ExternalEtag = info.Etag;
// forceUpdate = true;
// _logger.LogDebug("Forcing update due to ExternalEtag {0}", item.Name);
- //}
+ // }
if (!internalChannelId.Equals(item.ChannelId))
{
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 6f23a0888e..01c9fbca81 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Data
protected virtual int? CacheSize => null;
///
- /// Gets the journal mode.
+ /// Gets the journal mode. .
///
/// The journal mode.
protected virtual string JournalMode => "TRUNCATE";
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 2cb10765ff..ab1b55164b 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -72,9 +72,16 @@ namespace Emby.Server.Implementations.Data
_mediaAttachmentInsertPrefix = queryPrefixText.ToString();
}
+
///
/// Initializes a new instance of the class.
///
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// config is null.
public SqliteItemRepository(
IServerConfigurationManager config,
IServerApplicationHost appHost,
@@ -4879,7 +4886,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
foreach (var t in _knownTypes)
{
- dict[t.Name] = t.FullName ;
+ dict[t.Name] = t.FullName;
}
dict["Program"] = typeof(LiveTvProgram).FullName;
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index ef9af1dcd0..613d07d775 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -174,7 +174,6 @@ namespace Emby.Server.Implementations.Data
/// The key.
/// The user data.
/// The cancellation token.
- /// Task.
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -319,8 +318,8 @@ namespace Emby.Server.Implementations.Data
///
/// Return all user-data associated with the given user.
///
- ///
- ///
+ /// The internal user id.
+ /// The list of user item data.
public List GetAllUserData(long internalUserId)
{
if (internalUserId <= 0)
@@ -349,7 +348,8 @@ namespace Emby.Server.Implementations.Data
///
/// Read a row from the specified reader into the provided userData object.
///
- ///
+ /// The list of result set values.
+ /// The user item data.
private UserItemData ReadRow(IReadOnlyList reader)
{
var userData = new UserItemData();
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 7411239a1e..7c2d02037c 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -807,7 +807,7 @@ namespace Emby.Server.Implementations.Dto
dto.MediaType = item.MediaType;
- if (!(item is LiveTvProgram))
+ if (item is not LiveTvProgram)
{
dto.LocationType = item.LocationType;
}
@@ -928,9 +928,9 @@ namespace Emby.Server.Implementations.Dto
}
// if (options.ContainsField(ItemFields.MediaSourceCount))
- //{
+ // {
// Songs always have one
- //}
+ // }
}
if (item is IHasArtist hasArtist)
@@ -938,10 +938,10 @@ namespace Emby.Server.Implementations.Dto
dto.Artists = hasArtist.Artists;
// var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
- //{
+ // {
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
- //});
+ // });
// dto.ArtistItems = artistItems.Items
// .Select(i =>
@@ -958,7 +958,7 @@ namespace Emby.Server.Implementations.Dto
// Include artists that are not in the database yet, e.g., just added via metadata editor
// var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
dto.ArtistItems = hasArtist.Artists
- //.Except(foundArtists, new DistinctNameComparer())
+ // .Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
// This should not be necessary but we're seeing some cases of it
@@ -990,10 +990,10 @@ namespace Emby.Server.Implementations.Dto
dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
// var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
- //{
+ // {
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
- //});
+ // });
// dto.AlbumArtists = artistItems.Items
// .Select(i =>
@@ -1008,7 +1008,7 @@ namespace Emby.Server.Implementations.Dto
// .ToList();
dto.AlbumArtists = hasAlbumArtist.AlbumArtists
- //.Except(foundArtists, new DistinctNameComparer())
+ // .Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
// This should not be necessary but we're seeing some cases of it
@@ -1035,8 +1035,7 @@ namespace Emby.Server.Implementations.Dto
}
// Add video info
- var video = item as Video;
- if (video != null)
+ if (item is Video video)
{
dto.VideoType = video.VideoType;
dto.Video3DFormat = video.Video3DFormat;
@@ -1075,9 +1074,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.MediaStreams))
{
// Add VideoInfo
- var iHasMediaSources = item as IHasMediaSources;
-
- if (iHasMediaSources != null)
+ if (item is IHasMediaSources)
{
MediaStream[] mediaStreams;
@@ -1146,7 +1143,7 @@ namespace Emby.Server.Implementations.Dto
// TODO maybe remove the if statement entirely
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
- episodeSeries = episodeSeries ?? episode.Series;
+ episodeSeries ??= episode.Series;
if (episodeSeries != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
@@ -1159,7 +1156,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.SeriesStudio))
{
- episodeSeries = episodeSeries ?? episode.Series;
+ episodeSeries ??= episode.Series;
if (episodeSeries != null)
{
dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault();
@@ -1172,7 +1169,7 @@ namespace Emby.Server.Implementations.Dto
{
dto.AirDays = series.AirDays;
dto.AirTime = series.AirTime;
- dto.Status = series.Status.HasValue ? series.Status.Value.ToString() : null;
+ dto.Status = series.Status?.ToString();
}
// Add SeasonInfo
@@ -1185,7 +1182,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.SeriesStudio))
{
- series = series ?? season.Series;
+ series ??= season.Series;
if (series != null)
{
dto.SeriesStudio = series.Studios.FirstOrDefault();
@@ -1196,7 +1193,7 @@ namespace Emby.Server.Implementations.Dto
// TODO maybe remove the if statement entirely
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
- series = series ?? season.Series;
+ series ??= season.Series;
if (series != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
@@ -1283,7 +1280,7 @@ namespace Emby.Server.Implementations.Dto
var parent = currentItem.DisplayParent ?? currentItem.GetOwner() ?? currentItem.GetParent();
- if (parent == null && !(originalItem is UserRootFolder) && !(originalItem is UserView) && !(originalItem is AggregateFolder) && !(originalItem is ICollectionFolder) && !(originalItem is Channel))
+ if (parent == null && originalItem is not UserRootFolder && originalItem is not UserView && originalItem is not AggregateFolder && originalItem is not ICollectionFolder && originalItem is not Channel)
{
parent = _libraryManager.GetCollectionFolders(originalItem).FirstOrDefault();
}
@@ -1317,7 +1314,7 @@ namespace Emby.Server.Implementations.Dto
var imageTags = dto.ImageTags;
while (((!(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && logoLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && artLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && thumbLimit > 0) || parent is Series) &&
- (parent = parent ?? (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null)
+ (parent ??= (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null)
{
if (parent == null)
{
@@ -1348,7 +1345,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
+ if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && parent is not ICollectionFolder && parent is not UserView)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 5bb4100ba9..df48346e3c 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -149,7 +149,7 @@ namespace Emby.Server.Implementations.EntryPoints
private static bool EnableRefreshMessage(BaseItem item)
{
- if (!(item is Folder folder))
+ if (item is not Folder folder)
{
return false;
}
@@ -403,7 +403,7 @@ namespace Emby.Server.Implementations.EntryPoints
return false;
}
- if (item is IItemByName && !(item is MusicArtist))
+ if (item is IItemByName && item is not MusicArtist)
{
return false;
}
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 2e72b18f57..feaccf9fa1 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -37,6 +37,9 @@ namespace Emby.Server.Implementations.EntryPoints
///
/// Initializes a new instance of the class.
///
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
public UdpServerEntryPoint(
ILogger logger,
IServerApplicationHost appHost,
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index b2625a68c9..badc6ce6c6 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -74,6 +74,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
auth.TryGetValue("Token", out token);
}
+#pragma warning disable CA1508 // string.IsNullOrEmpty(token) is always false.
if (string.IsNullOrEmpty(token))
{
token = headers["X-Emby-Token"];
@@ -111,6 +112,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
// Request doesn't contain a token.
return authInfo;
}
+#pragma warning restore CA1508
authInfo.HasToken = true;
var result = _authRepo.Get(new AuthenticationInfoQuery
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index 5d38ea0ca6..7010a6fb08 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.HttpServer
public event EventHandler? Closed;
///
- /// Gets or sets the remote end point.
+ /// Gets the remote end point.
///
public IPAddress? RemoteEndPoint { get; }
@@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.HttpServer
public DateTime LastKeepAliveDate { get; set; }
///
- /// Gets or sets the query string.
+ /// Gets the query string.
///
/// The query string.
public IQueryCollection QueryString { get; }
diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs
index a430b9e720..1d97882db5 100644
--- a/Emby.Server.Implementations/IStartupOptions.cs
+++ b/Emby.Server.Implementations/IStartupOptions.cs
@@ -10,7 +10,7 @@ namespace Emby.Server.Implementations
string? FFmpegPath { get; }
///
- /// Gets the value of the --service command line option.
+ /// Gets a value value indicating whether to run as service by the --service command line option.
///
bool IsService { get; }
diff --git a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
index ff5f26ce09..0229fbae79 100644
--- a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
@@ -30,27 +30,27 @@ namespace Emby.Server.Implementations.Images
string[] includeItemTypes;
- if (string.Equals(viewType, CollectionType.Movies))
+ if (string.Equals(viewType, CollectionType.Movies, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Movie" };
}
- else if (string.Equals(viewType, CollectionType.TvShows))
+ else if (string.Equals(viewType, CollectionType.TvShows, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Series" };
}
- else if (string.Equals(viewType, CollectionType.Music))
+ else if (string.Equals(viewType, CollectionType.Music, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "MusicAlbum" };
}
- else if (string.Equals(viewType, CollectionType.Books))
+ else if (string.Equals(viewType, CollectionType.Books, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Book", "AudioBook" };
}
- else if (string.Equals(viewType, CollectionType.BoxSets))
+ else if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "BoxSet" };
}
- else if (string.Equals(viewType, CollectionType.HomeVideos) || string.Equals(viewType, CollectionType.Photos))
+ else if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.Ordinal) || string.Equals(viewType, CollectionType.Photos, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Video", "Photo" };
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 13fb8b2fd5..6a2983d9ba 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -287,14 +287,14 @@ namespace Emby.Server.Implementations.Library
if (item is IItemByName)
{
- if (!(item is MusicArtist))
+ if (item is not MusicArtist)
{
return;
}
}
else if (!item.IsFolder)
{
- if (!(item is Video) && !(item is LiveTvChannel))
+ if (item is not Video && item is not LiveTvChannel)
{
return;
}
@@ -866,7 +866,7 @@ namespace Emby.Server.Implementations.Library
{
var path = Person.GetPath(name);
var id = GetItemByNameId(path);
- if (!(GetItemById(id) is Person item))
+ if (GetItemById(id) is not Person item)
{
item = new Person
{
@@ -2118,7 +2118,7 @@ namespace Emby.Server.Implementations.Library
public LibraryOptions GetLibraryOptions(BaseItem item)
{
- if (!(item is CollectionFolder collectionFolder))
+ if (item is not CollectionFolder collectionFolder)
{
// List.Find is more performant than FirstOrDefault due to enumerator allocation
collectionFolder = GetCollectionFolders(item)
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index e893d63350..fd9747b4b7 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -21,11 +21,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
///
public class AudioResolver : ItemResolver, IMultiItemResolver
{
- private readonly ILibraryManager LibraryManager;
+ private readonly ILibraryManager _libraryManager;
public AudioResolver(ILibraryManager libraryManager)
{
- LibraryManager = libraryManager;
+ _libraryManager = libraryManager;
}
///
@@ -88,13 +88,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
var files = args.FileSystemChildren
- .Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
+ .Where(i => !_libraryManager.IgnoreFile(i, args.Parent))
.ToList();
return FindAudio(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
}
- if (LibraryManager.IsAudioFile(args.Path))
+ if (_libraryManager.IsAudioFile(args.Path))
{
var extension = Path.GetExtension(args.Path);
@@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
// For conflicting extensions, give priority to videos
- if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path))
+ if (isMixedCollectionType && _libraryManager.IsVideoFile(args.Path))
{
return null;
}
@@ -182,7 +182,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
}
- var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
+ var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var resolver = new AudioBookListResolver(namingOptions);
var resolverResult = resolver.Resolve(files).ToList();
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index cdb492022b..e003328089 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
///
/// Resolves a Path into a Video or Video subclass.
///
- ///
+ /// The type of item to resolve.
public abstract class BaseVideoResolver : MediaBrowser.Controller.Resolvers.ItemResolver
where T : Video, new()
{
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
break;
}
- if (IsBluRayDirectory(child.FullName, filename, args.DirectoryService))
+ if (IsBluRayDirectory(filename))
{
videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions);
@@ -279,25 +279,13 @@ namespace Emby.Server.Implementations.Library.Resolvers
}
///
- /// Determines whether [is blu ray directory] [the specified directory name].
+ /// Determines whether [is bluray directory] [the specified directory name].
///
- protected bool IsBluRayDirectory(string fullPath, string directoryName, IDirectoryService directoryService)
+ /// The directory name.
+ /// Whether the directory is a bluray directory.
+ protected bool IsBluRayDirectory(string directoryName)
{
- if (!string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return true;
- // var blurayExtensions = new[]
- //{
- // ".mts",
- // ".m2ts",
- // ".bdmv",
- // ".mpls"
- //};
-
- // return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
+ return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
similarity index 100%
rename from Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
rename to Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
index fa45ccf840..3f29ab191f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
@@ -9,7 +9,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
///
/// Class ItemResolver.
///
- ///
+ /// The type of BaseItem.
public abstract class ItemResolver : IItemResolver
where T : BaseItem, new()
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 889e29a6ba..8b55a77449 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -400,7 +400,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return movie;
}
- if (IsBluRayDirectory(child.FullName, filename, directoryService))
+ if (IsBluRayDirectory(filename))
{
var movie = new T
{
@@ -481,7 +481,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return true;
}
- if (subfolders.Any(s => IsBluRayDirectory(s.FullName, s.Name, directoryService)))
+ if (subfolders.Any(s => IsBluRayDirectory(s.Name)))
{
videoTypes.Add(VideoType.BluRay);
return true;
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index ecd44be477..2c4ead7198 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -18,7 +18,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
///
public class PlaylistResolver : FolderResolver
{
- private string[] _musicPlaylistCollectionTypes = new string[] {
+ private string[] _musicPlaylistCollectionTypes =
+ {
string.Empty,
CollectionType.Music
};
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index 9a8c5f39d5..16bdf720ce 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -87,12 +87,15 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities)
{
- _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
+ _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
- _libraryManager.DeleteItem(item, new DeleteOptions
+ _libraryManager.DeleteItem(
+ item,
+ new DeleteOptions
{
DeleteFileLocation = false
- }, false);
+ },
+ false);
}
progress.Report(100);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 7970631201..a6be407450 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1458,7 +1458,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (item.GetType() == typeof(Folder) && string.Equals(item.Path, parentPath, StringComparison.OrdinalIgnoreCase))
{
var parentItem = item.GetParent();
- if (parentItem != null && !(parentItem is AggregateFolder))
+ if (parentItem != null && parentItem is not AggregateFolder)
{
item = parentItem;
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
index 0ec52a9598..20a8213a77 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
@@ -8,7 +8,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
internal class EpgChannelData
{
-
private readonly Dictionary _channelsById;
private readonly Dictionary _channelsByNumber;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index b7639a51ce..bcfc4011c5 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -14,6 +14,7 @@ using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos;
using MediaBrowser.Common;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Net;
@@ -96,12 +97,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
_logger.LogInformation("Channel Station ID is: {ChannelID}", channelId);
- var requestList = new List()
+ var requestList = new List()
{
- new ScheduleDirect.RequestScheduleForChannel()
+ new RequestScheduleForChannelDto()
{
- stationID = channelId,
- date = dates
+ StationId = channelId,
+ Date = dates
}
};
@@ -113,61 +114,61 @@ namespace Emby.Server.Implementations.LiveTv.Listings
options.Headers.TryAddWithoutValidation("token", token);
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var dailySchedules = await JsonSerializer.DeserializeAsync>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var dailySchedules = await JsonSerializer.DeserializeAsync>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
programRequestOptions.Headers.TryAddWithoutValidation("token", token);
- var programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct();
+ var programsID = dailySchedules.SelectMany(d => d.Programs.Select(s => s.ProgramId)).Distinct();
programRequestOptions.Content = new StringContent("[\"" + string.Join("\", \"", programsID) + "\"]", Encoding.UTF8, MediaTypeNames.Application.Json);
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var programDetails = await JsonSerializer.DeserializeAsync>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- var programDict = programDetails.ToDictionary(p => p.programID, y => y);
+ var programDetails = await JsonSerializer.DeserializeAsync>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var programDict = programDetails.ToDictionary(p => p.ProgramId, y => y);
var programIdsWithImages = programDetails
- .Where(p => p.hasImageArtwork).Select(p => p.programID)
+ .Where(p => p.HasImageArtwork).Select(p => p.ProgramId)
.ToList();
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
var programsInfo = new List();
- foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
+ foreach (ProgramDto schedule in dailySchedules.SelectMany(d => d.Programs))
{
// _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
// " which corresponds to channel " + channelNumber + " and program id " +
- // schedule.programID + " which says it has images? " +
- // programDict[schedule.programID].hasImageArtwork);
+ // schedule.ProgramId + " which says it has images? " +
+ // programDict[schedule.ProgramId].hasImageArtwork);
if (images != null)
{
- var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
+ var imageIndex = images.FindIndex(i => i.ProgramId == schedule.ProgramId[..10]);
if (imageIndex > -1)
{
- var programEntry = programDict[schedule.programID];
+ var programEntry = programDict[schedule.ProgramId];
- var allImages = images[imageIndex].data ?? new List();
- var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
- var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
+ var allImages = images[imageIndex].Data ?? new List();
+ var imagesWithText = allImages.Where(i => string.Equals(i.Text, "yes", StringComparison.OrdinalIgnoreCase));
+ var imagesWithoutText = allImages.Where(i => string.Equals(i.Text, "no", StringComparison.OrdinalIgnoreCase));
const double DesiredAspect = 2.0 / 3;
- programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
+ programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
const double WideAspect = 16.0 / 9;
- programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
+ programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
// Don't supply the same image twice
- if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
+ if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
{
- programEntry.thumbImage = null;
+ programEntry.ThumbImage = null;
}
- programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
+ programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -176,15 +177,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
+ programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.ProgramId]));
}
return programsInfo;
}
- private static int GetSizeOrder(ScheduleDirect.ImageData image)
+ private static int GetSizeOrder(ImageDataDto image)
{
- if (int.TryParse(image.height, out int value))
+ if (int.TryParse(image.Height, out int value))
{
return value;
}
@@ -192,53 +193,53 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return 0;
}
- private static string GetChannelNumber(ScheduleDirect.Map map)
+ private static string GetChannelNumber(MapDto map)
{
- var channelNumber = map.logicalChannelNumber;
+ var channelNumber = map.LogicalChannelNumber;
if (string.IsNullOrWhiteSpace(channelNumber))
{
- channelNumber = map.channel;
+ channelNumber = map.Channel;
}
if (string.IsNullOrWhiteSpace(channelNumber))
{
- channelNumber = map.atscMajor + "." + map.atscMinor;
+ channelNumber = map.AtscMajor + "." + map.AtscMinor;
}
return channelNumber.TrimStart('0');
}
- private static bool IsMovie(ScheduleDirect.ProgramDetails programInfo)
+ private static bool IsMovie(ProgramDetailsDto programInfo)
{
- return string.Equals(programInfo.entityType, "movie", StringComparison.OrdinalIgnoreCase);
+ return string.Equals(programInfo.EntityType, "movie", StringComparison.OrdinalIgnoreCase);
}
- private ProgramInfo GetProgram(string channelId, ScheduleDirect.Program programInfo, ScheduleDirect.ProgramDetails details)
+ private ProgramInfo GetProgram(string channelId, ProgramDto programInfo, ProgramDetailsDto details)
{
- var startAt = GetDate(programInfo.airDateTime);
- var endAt = startAt.AddSeconds(programInfo.duration);
+ var startAt = GetDate(programInfo.AirDateTime);
+ var endAt = startAt.AddSeconds(programInfo.Duration);
var audioType = ProgramAudio.Stereo;
- var programId = programInfo.programID ?? string.Empty;
+ var programId = programInfo.ProgramId ?? string.Empty;
string newID = programId + "T" + startAt.Ticks + "C" + channelId;
- if (programInfo.audioProperties != null)
+ if (programInfo.AudioProperties != null)
{
- if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
+ if (programInfo.AudioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.Atmos;
}
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
+ else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.DolbyDigital;
}
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
+ else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.DolbyDigital;
}
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
+ else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.Stereo;
}
@@ -249,9 +250,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
string episodeTitle = null;
- if (details.episodeTitle150 != null)
+ if (details.EpisodeTitle150 != null)
{
- episodeTitle = details.episodeTitle150;
+ episodeTitle = details.EpisodeTitle150;
}
var info = new ProgramInfo
@@ -260,22 +261,22 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Id = newID,
StartDate = startAt,
EndDate = endAt,
- Name = details.titles[0].title120 ?? "Unknown",
+ Name = details.Titles[0].Title120 ?? "Unknown",
OfficialRating = null,
CommunityRating = null,
EpisodeTitle = episodeTitle,
Audio = audioType,
// IsNew = programInfo.@new ?? false,
- IsRepeat = programInfo.@new == null,
- IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
- ImageUrl = details.primaryImage,
- ThumbImageUrl = details.thumbImage,
- IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
- IsSports = string.Equals(details.entityType, "sports", StringComparison.OrdinalIgnoreCase),
+ IsRepeat = programInfo.New == null,
+ IsSeries = string.Equals(details.EntityType, "episode", StringComparison.OrdinalIgnoreCase),
+ ImageUrl = details.PrimaryImage,
+ ThumbImageUrl = details.ThumbImage,
+ IsKids = string.Equals(details.Audience, "children", StringComparison.OrdinalIgnoreCase),
+ IsSports = string.Equals(details.EntityType, "sports", StringComparison.OrdinalIgnoreCase),
IsMovie = IsMovie(details),
- Etag = programInfo.md5,
- IsLive = string.Equals(programInfo.liveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
- IsPremiere = programInfo.premiere || (programInfo.isPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
+ Etag = programInfo.Md5,
+ IsLive = string.Equals(programInfo.LiveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
+ IsPremiere = programInfo.Premiere || (programInfo.IsPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
};
var showId = programId;
@@ -298,15 +299,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
info.ShowId = showId;
- if (programInfo.videoProperties != null)
+ if (programInfo.VideoProperties != null)
{
- info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
- info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
+ info.IsHD = programInfo.VideoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
+ info.Is3D = programInfo.VideoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
}
- if (details.contentRating != null && details.contentRating.Count > 0)
+ if (details.ContentRating != null && details.ContentRating.Count > 0)
{
- info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-", StringComparison.Ordinal)
+ info.OfficialRating = details.ContentRating[0].Code.Replace("TV", "TV-", StringComparison.Ordinal)
.Replace("--", "-", StringComparison.Ordinal);
var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
@@ -316,15 +317,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- if (details.descriptions != null)
+ if (details.Descriptions != null)
{
- if (details.descriptions.description1000 != null && details.descriptions.description1000.Count > 0)
+ if (details.Descriptions.Description1000 != null && details.Descriptions.Description1000.Count > 0)
{
- info.Overview = details.descriptions.description1000[0].description;
+ info.Overview = details.Descriptions.Description1000[0].Description;
}
- else if (details.descriptions.description100 != null && details.descriptions.description100.Count > 0)
+ else if (details.Descriptions.Description100 != null && details.Descriptions.Description100.Count > 0)
{
- info.Overview = details.descriptions.description100[0].description;
+ info.Overview = details.Descriptions.Description100[0].Description;
}
}
@@ -334,18 +335,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId;
- if (details.metadata != null)
+ if (details.Metadata != null)
{
- foreach (var metadataProgram in details.metadata)
+ foreach (var metadataProgram in details.Metadata)
{
var gracenote = metadataProgram.Gracenote;
if (gracenote != null)
{
- info.SeasonNumber = gracenote.season;
+ info.SeasonNumber = gracenote.Season;
- if (gracenote.episode > 0)
+ if (gracenote.Episode > 0)
{
- info.EpisodeNumber = gracenote.episode;
+ info.EpisodeNumber = gracenote.Episode;
}
break;
@@ -354,25 +355,25 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- if (!string.IsNullOrWhiteSpace(details.originalAirDate))
+ if (!string.IsNullOrWhiteSpace(details.OriginalAirDate))
{
- info.OriginalAirDate = DateTime.Parse(details.originalAirDate, CultureInfo.InvariantCulture);
+ info.OriginalAirDate = DateTime.Parse(details.OriginalAirDate, CultureInfo.InvariantCulture);
info.ProductionYear = info.OriginalAirDate.Value.Year;
}
- if (details.movie != null)
+ if (details.Movie != null)
{
- if (!string.IsNullOrEmpty(details.movie.year)
- && int.TryParse(details.movie.year, out int year))
+ if (!string.IsNullOrEmpty(details.Movie.Year)
+ && int.TryParse(details.Movie.Year, out int year))
{
info.ProductionYear = year;
}
}
- if (details.genres != null)
+ if (details.Genres != null)
{
- info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
- info.IsNews = details.genres.Contains("news", StringComparer.OrdinalIgnoreCase);
+ info.Genres = details.Genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
+ info.IsNews = details.Genres.Contains("news", StringComparer.OrdinalIgnoreCase);
if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
{
@@ -395,11 +396,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return date;
}
- private string GetProgramImage(string apiUrl, IEnumerable images, bool returnDefaultImage, double desiredAspect)
+ private string GetProgramImage(string apiUrl, IEnumerable images, bool returnDefaultImage, double desiredAspect)
{
var match = images
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
- .ThenByDescending(GetSizeOrder)
+ .ThenByDescending(i => GetSizeOrder(i))
.FirstOrDefault();
if (match == null)
@@ -407,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return null;
}
- var uri = match.uri;
+ var uri = match.Uri;
if (string.IsNullOrWhiteSpace(uri))
{
@@ -423,19 +424,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- private static double GetAspectRatio(ScheduleDirect.ImageData i)
+ private static double GetAspectRatio(ImageDataDto i)
{
int width = 0;
int height = 0;
- if (!string.IsNullOrWhiteSpace(i.width))
+ if (!string.IsNullOrWhiteSpace(i.Width))
{
- int.TryParse(i.width, out width);
+ _ = int.TryParse(i.Width, out width);
}
- if (!string.IsNullOrWhiteSpace(i.height))
+ if (!string.IsNullOrWhiteSpace(i.Height))
{
- int.TryParse(i.height, out height);
+ _ = int.TryParse(i.Height, out height);
}
if (height == 0 || width == 0)
@@ -448,14 +449,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return result;
}
- private async Task> GetImageForPrograms(
+ private async Task> GetImageForPrograms(
ListingsProviderInfo info,
IReadOnlyList programIds,
CancellationToken cancellationToken)
{
if (programIds.Count == 0)
{
- return new List();
+ return new List();
}
StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13));
@@ -479,13 +480,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- return await JsonSerializer.DeserializeAsync>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ return await JsonSerializer.DeserializeAsync>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting image info from schedules direct");
- return new List();
+ return new List();
}
}
@@ -508,18 +509,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var root = await JsonSerializer.DeserializeAsync>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
if (root != null)
{
- foreach (ScheduleDirect.Headends headend in root)
+ foreach (HeadendsDto headend in root)
{
- foreach (ScheduleDirect.Lineup lineup in headend.lineups)
+ foreach (LineupDto lineup in headend.Lineups)
{
lineups.Add(new NameIdPair
{
- Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
- Id = lineup.uri.Substring(18)
+ Name = string.IsNullOrWhiteSpace(lineup.Name) ? lineup.Lineup : lineup.Name,
+ Id = lineup.Uri[18..]
});
}
}
@@ -649,14 +650,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- if (string.Equals(root.message, "OK", StringComparison.Ordinal))
+ var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ if (string.Equals(root.Message, "OK", StringComparison.Ordinal))
{
- _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
- return root.token;
+ _logger.LogInformation("Authenticated with Schedules Direct token: {Token}", root.Token);
+ return root.Token;
}
- throw new Exception("Could not authenticate with Schedules Direct Error: " + root.message);
+ throw new Exception("Could not authenticate with Schedules Direct Error: " + root.Message);
}
private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
@@ -705,9 +706,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpResponse.EnsureSuccessStatusCode();
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
using var response = httpResponse.Content;
- var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+ return root.Lineups.Any(i => string.Equals(info.ListingsId, i.Lineup, StringComparison.OrdinalIgnoreCase));
}
catch (HttpRequestException ex)
{
@@ -777,35 +778,35 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
+ var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.Map.Count);
_logger.LogInformation("Mapping Stations to Channel");
- var allStations = root.stations ?? new List();
+ var allStations = root.Stations ?? new List();
- var map = root.map;
+ var map = root.Map;
var list = new List(map.Count);
foreach (var channel in map)
{
var channelNumber = GetChannelNumber(channel);
- var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase))
- ?? new ScheduleDirect.Station
+ var station = allStations.Find(item => string.Equals(item.StationId, channel.StationId, StringComparison.OrdinalIgnoreCase))
+ ?? new StationDto
{
- stationID = channel.stationID
+ StationId = channel.StationId
};
var channelInfo = new ChannelInfo
{
- Id = station.stationID,
- CallSign = station.callsign,
+ Id = station.StationId,
+ CallSign = station.Callsign,
Number = channelNumber,
- Name = string.IsNullOrWhiteSpace(station.name) ? channelNumber : station.name
+ Name = string.IsNullOrWhiteSpace(station.Name) ? channelNumber : station.Name
};
- if (station.logo != null)
+ if (station.Logo != null)
{
- channelInfo.ImageUrl = station.logo.URL;
+ channelInfo.ImageUrl = station.Logo.Url;
}
list.Add(channelInfo);
@@ -818,402 +819,5 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
}
-
- public class ScheduleDirect
- {
- public class Token
- {
- public int code { get; set; }
-
- public string message { get; set; }
-
- public string serverID { get; set; }
-
- public string token { get; set; }
- }
-
- public class Lineup
- {
- public string lineup { get; set; }
-
- public string name { get; set; }
-
- public string transport { get; set; }
-
- public string location { get; set; }
-
- public string uri { get; set; }
- }
-
- public class Lineups
- {
- public int code { get; set; }
-
- public string serverID { get; set; }
-
- public string datetime { get; set; }
-
- public List lineups { get; set; }
- }
-
- public class Headends
- {
- public string headend { get; set; }
-
- public string transport { get; set; }
-
- public string location { get; set; }
-
- public List lineups { get; set; }
- }
-
- public class Map
- {
- public string stationID { get; set; }
-
- public string channel { get; set; }
-
- public string logicalChannelNumber { get; set; }
-
- public int uhfVhf { get; set; }
-
- public int atscMajor { get; set; }
-
- public int atscMinor { get; set; }
- }
-
- public class Broadcaster
- {
- public string city { get; set; }
-
- public string state { get; set; }
-
- public string postalcode { get; set; }
-
- public string country { get; set; }
- }
-
- public class Logo
- {
- public string URL { get; set; }
-
- public int height { get; set; }
-
- public int width { get; set; }
-
- public string md5 { get; set; }
- }
-
- public class Station
- {
- public string stationID { get; set; }
-
- public string name { get; set; }
-
- public string callsign { get; set; }
-
- public List broadcastLanguage { get; set; }
-
- public List descriptionLanguage { get; set; }
-
- public Broadcaster broadcaster { get; set; }
-
- public string affiliate { get; set; }
-
- public Logo logo { get; set; }
-
- public bool? isCommercialFree { get; set; }
- }
-
- public class Metadata
- {
- public string lineup { get; set; }
-
- public string modified { get; set; }
-
- public string transport { get; set; }
- }
-
- public class Channel
- {
- public List
diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
index 10ec31b838..72cd9aa450 100644
--- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
+++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
@@ -17,7 +17,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
index 5371853bc0..e9b7b18509 100644
--- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
+++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
index b2a6fdcf27..dd593c9e74 100644
--- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
+++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
@@ -16,7 +16,7 @@
-
+
From 4c5f7207e32676b242951cadb233e80e1856df9a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 30 Aug 2021 12:00:56 +0000
Subject: [PATCH 102/118] Bump prometheus-net.DotNetRuntime from 4.1.0 to 4.2.0
Bumps [prometheus-net.DotNetRuntime](https://github.com/djluck/prometheus-net.DotNetRuntime) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/djluck/prometheus-net.DotNetRuntime/releases)
- [Commits](https://github.com/djluck/prometheus-net.DotNetRuntime/compare/4.1.0...4.2.0)
---
updated-dependencies:
- dependency-name: prometheus-net.DotNetRuntime
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index e0f841d529..927aa0c20c 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -30,7 +30,7 @@
-
+
From e2fdab4be45d214e2b65c63671902a00e05741ed Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 30 Aug 2021 12:01:04 +0000
Subject: [PATCH 103/118] Bump SQLitePCLRaw.bundle_e_sqlite3 from 2.0.4 to
2.0.5
Bumps [SQLitePCLRaw.bundle_e_sqlite3](https://github.com/ericsink/SQLitePCL.raw) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/ericsink/SQLitePCL.raw/releases)
- [Commits](https://github.com/ericsink/SQLitePCL.raw/commits)
---
updated-dependencies:
- dependency-name: SQLitePCLRaw.bundle_e_sqlite3
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
Jellyfin.Server/Jellyfin.Server.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index c4d3d8a1f3..a57666cd62 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -44,7 +44,7 @@
-
+
From ecb4b8e0aacef56331e7eadd1f82839b0f6e1e00 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Mon, 30 Aug 2021 07:11:34 -0600
Subject: [PATCH 104/118] Apply suggestions from code review
Co-authored-by: Bond-009
---
Emby.Server.Implementations/Data/SqliteItemRepository.cs | 1 -
.../Library/Validators/StudiosValidator.cs | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index ab1b55164b..108ea783d4 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -72,7 +72,6 @@ namespace Emby.Server.Implementations.Data
_mediaAttachmentInsertPrefix = queryPrefixText.ToString();
}
-
///
/// Initializes a new instance of the class.
///
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index 16bdf720ce..7a22f851b4 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -87,7 +87,7 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities)
{
- _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
+ _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
From 963ab2dab6a8c4dafadae61312b36ed5fbb1f323 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 4 Apr 2021 23:02:28 +0200
Subject: [PATCH 105/118] Simplify the way we choose our ffmpeg
* no longer search $PATH
* no longer require a full path
* don't fall back
---
.../ApplicationHost.cs | 1 -
.../MediaEncoding/IMediaEncoder.cs | 6 -
.../Encoder/EncoderValidator.cs | 4 +-
.../Encoder/MediaEncoder.cs | 107 ++++++------------
MediaBrowser.Model/System/SystemInfo.cs | 1 +
.../EncoderValidatorTests.cs | 8 +-
6 files changed, 38 insertions(+), 89 deletions(-)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index bf7ddace2d..0b5322f394 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1099,7 +1099,6 @@ namespace Emby.Server.Implementations
ServerName = FriendlyName,
LocalAddress = GetSmartApiUrl(source),
SupportsLibraryMonitor = true,
- EncoderLocation = _mediaEncoder.EncoderLocation,
SystemArchitecture = RuntimeInformation.OSArchitecture,
PackageName = _startupOptions.PackageName
};
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index ff24560703..63308fa098 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -10,7 +10,6 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.System;
namespace MediaBrowser.Controller.MediaEncoding
{
@@ -19,11 +18,6 @@ namespace MediaBrowser.Controller.MediaEncoding
///
public interface IMediaEncoder : ITranscoderSupport
{
- ///
- /// Gets location of the discovered FFmpeg tool.
- ///
- FFmpegLocation EncoderLocation { get; }
-
///
/// Gets the encoder path.
///
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index f782e65bd1..ef831ab828 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -12,8 +12,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
public class EncoderValidator
{
- private const string DefaultEncoderPath = "ffmpeg";
-
private static readonly string[] _requiredDecoders = new[]
{
"h264",
@@ -106,7 +104,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly string _encoderPath;
- public EncoderValidator(ILogger logger, string encoderPath = DefaultEncoderPath)
+ public EncoderValidator(ILogger logger, string encoderPath)
{
_logger = logger;
_encoderPath = encoderPath;
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 412a953216..f8ba78e463 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -23,7 +23,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
@@ -69,7 +68,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string _ffmpegPath = string.Empty;
private string _ffprobePath;
- private int threads;
+ private int _threads;
public MediaEncoder(
ILogger logger,
@@ -89,9 +88,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
///
public string EncoderPath => _ffmpegPath;
- ///
- public FFmpegLocation EncoderLocation { get; private set; }
-
///
/// Run at startup or if the user removes a Custom path from transcode page.
/// Sets global variables FFmpegPath.
@@ -100,20 +96,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
public void SetFFmpegPath()
{
// 1) Custom path stored in config/encoding xml file under tag takes precedence
- if (!ValidatePath(_configurationManager.GetEncodingOptions().EncoderAppPath, FFmpegLocation.Custom))
+ var ffmpegPath = _configurationManager.GetEncodingOptions().EncoderAppPath;
+ if (string.IsNullOrEmpty(ffmpegPath))
{
// 2) Check if the --ffmpeg CLI switch has been given
- if (!ValidatePath(_startupOptionFFmpegPath, FFmpegLocation.SetByArgument))
+ ffmpegPath = _startupOptionFFmpegPath;
+ if (string.IsNullOrEmpty(ffmpegPath))
{
- // 3) Search system $PATH environment variable for valid FFmpeg
- if (!ValidatePath(ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System))
- {
- EncoderLocation = FFmpegLocation.NotFound;
- _ffmpegPath = null;
- }
+ // 3) Check "ffmpeg"
+ ffmpegPath = "ffmpeg";
}
}
+ if (!ValidatePath(ffmpegPath))
+ {
+ _ffmpegPath = null;
+ }
+
// Write the FFmpeg path to the config/encoding.xml file as so it appears in UI
var config = _configurationManager.GetEncodingOptions();
config.EncoderAppPathDisplay = _ffmpegPath ?? string.Empty;
@@ -131,10 +130,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
SetAvailableDecoders(validator.GetDecoders());
SetAvailableEncoders(validator.GetEncoders());
SetAvailableHwaccels(validator.GetHwaccels());
- threads = EncodingHelper.GetNumberOfThreads(null, _configurationManager.GetEncodingOptions(), null);
+ _threads = EncodingHelper.GetNumberOfThreads(null, _configurationManager.GetEncodingOptions(), null);
}
- _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty);
+ _logger.LogInformation("FFmpeg: {FfmpegPath}", _ffmpegPath ?? string.Empty);
}
///
@@ -153,15 +152,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
throw new ArgumentException("Unexpected pathType value");
}
- else if (string.IsNullOrWhiteSpace(path))
+
+ if (string.IsNullOrWhiteSpace(path))
{
// User had cleared the custom path in UI
newPath = string.Empty;
}
- else if (File.Exists(path))
- {
- newPath = path;
- }
else if (Directory.Exists(path))
{
// Given path is directory, so resolve down to filename
@@ -169,7 +165,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
else
{
- throw new ResourceNotFoundException();
+ newPath = path;
}
// Write the new ffmpeg path to the xml as
@@ -184,37 +180,26 @@ namespace MediaBrowser.MediaEncoding.Encoder
///
/// Validates the supplied FQPN to ensure it is a ffmpeg utility.
- /// If checks pass, global variable FFmpegPath and EncoderLocation are updated.
+ /// If checks pass, global variable FFmpegPath is updated.
///
/// FQPN to test.
- /// Location (External, Custom, System) of tool.
/// true if the version validation succeeded; otherwise, false.
- private bool ValidatePath(string path, FFmpegLocation location)
+ private bool ValidatePath(string path)
{
- bool rc = false;
-
- if (!string.IsNullOrEmpty(path))
+ if (string.IsNullOrEmpty(path))
{
- if (File.Exists(path))
- {
- rc = new EncoderValidator(_logger, path).ValidateVersion();
-
- if (!rc)
- {
- _logger.LogWarning("FFmpeg: {Location}: Failed version check: {Path}", location, path);
- }
+ return false;
+ }
- _ffmpegPath = path;
- EncoderLocation = location;
- return true;
- }
- else
- {
- _logger.LogWarning("FFmpeg: {Location}: File not found: {Path}", location, path);
- }
+ bool rc = new EncoderValidator(_logger, path).ValidateVersion();
+ if (!rc)
+ {
+ _logger.LogWarning("FFmpeg: Failed version check: {Path}", path);
+ return false;
}
- return rc;
+ _ffmpegPath = path;
+ return true;
}
private string GetEncoderPathFromDirectory(string path, string filename, bool recursive = false)
@@ -235,34 +220,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
- ///
- /// Search the system $PATH environment variable looking for given filename.
- ///
- /// The filename.
- /// The full path to the file.
- private string ExistsOnSystemPath(string fileName)
- {
- var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true);
- if (!string.IsNullOrEmpty(inJellyfinPath))
- {
- return inJellyfinPath;
- }
-
- var values = Environment.GetEnvironmentVariable("PATH");
-
- foreach (var path in values.Split(Path.PathSeparator))
- {
- var candidatePath = GetEncoderPathFromDirectory(path, fileName);
-
- if (!string.IsNullOrEmpty(candidatePath))
- {
- return candidatePath;
- }
- }
-
- return null;
- }
-
public void SetAvailableEncoders(IEnumerable list)
{
_encoders = list.ToList();
@@ -394,7 +351,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = extractChapters
? "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_chapters -show_format"
: "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_format";
- args = string.Format(CultureInfo.InvariantCulture, args, probeSizeArgument, inputPath, threads).Trim();
+ args = string.Format(CultureInfo.InvariantCulture, args, probeSizeArgument, inputPath, _threads).Trim();
var process = new Process
{
@@ -615,7 +572,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
- var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads);
+ var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, _threads);
if (offset.HasValue)
{
@@ -728,7 +685,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
Directory.CreateDirectory(targetDirectory);
var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
- var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads);
+ var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, _threads);
if (!string.IsNullOrWhiteSpace(container))
{
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index e45b2f33a6..a82c1c8c0c 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -133,6 +133,7 @@ namespace MediaBrowser.Model.System
[Obsolete("This should be handled by the package manager")]
public bool HasUpdateAvailable { get; set; }
+ [Obsolete("This isn't set correctly anymore")]
public FFmpegLocation EncoderLocation { get; set; }
public Architecture SystemArchitecture { get; set; }
diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
index 39fd8afda1..cc429b442d 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
@@ -9,12 +9,13 @@ namespace Jellyfin.MediaEncoding.Tests
{
public class EncoderValidatorTests
{
+ private readonly EncoderValidator _encoderValidator = new EncoderValidator(new NullLogger(), "ffmpeg");
+
[Theory]
[ClassData(typeof(GetFFmpegVersionTestData))]
public void GetFFmpegVersionTest(string versionOutput, Version? version)
{
- var val = new EncoderValidator(new NullLogger());
- Assert.Equal(version, val.GetFFmpegVersion(versionOutput));
+ Assert.Equal(version, _encoderValidator.GetFFmpegVersion(versionOutput));
}
[Theory]
@@ -28,8 +29,7 @@ namespace Jellyfin.MediaEncoding.Tests
[InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)]
public void ValidateVersionInternalTest(string versionOutput, bool valid)
{
- var val = new EncoderValidator(new NullLogger());
- Assert.Equal(valid, val.ValidateVersionInternal(versionOutput));
+ Assert.Equal(valid, _encoderValidator.ValidateVersionInternal(versionOutput));
}
private class GetFFmpegVersionTestData : IEnumerable
From a0ee16d38d79d385f967a3164e0f2b095ef5deaa Mon Sep 17 00:00:00 2001
From: Mark Titorenkov
Date: Tue, 31 Aug 2021 15:12:09 +0300
Subject: [PATCH 106/118] Update M3U Channel Name Precedence
Sets the ExtInf display name to have a higher precedence than the `tvg-name` attribute for channel names.
Usually `namInExtInf` is a more descriptive and human readable name if both it and `tvg-name` are available. `tvg-name` is more likely to be an internal identifier such as just the channel number with a prefix in my provider's case.
---
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 16ff98a7d8..d28c39e213 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -295,11 +295,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- attributes.TryGetValue("tvg-name", out string name);
+ string name = nameInExtInf;
if (string.IsNullOrWhiteSpace(name))
{
- name = nameInExtInf;
+ attributes.TryGetValue("tvg-name", out name);
}
if (string.IsNullOrWhiteSpace(name))
From 07f64102ddc5b19a0c2e3e9cae308336dad18c5a Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 1 Sep 2021 14:00:06 +0200
Subject: [PATCH 107/118] Fix build
---
.../Controllers/MediaStructureControllerTests.cs | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
index 8af43050f6..19d8381ea5 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs
@@ -87,10 +87,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
var data = new UpdateMediaPathRequestDto()
{
Name = " ",
- PathInfo = new MediaPathInfo
- {
- Path = "test"
- }
+ PathInfo = new MediaPathInfo("test")
};
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions));
From 55c8fb7814c3c364b55b879cd5b080442d6f387d Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 1 Sep 2021 14:18:23 +0200
Subject: [PATCH 108/118] Ignore Omnisharp crash logs
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index 252210e57c..c2ae76c1e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -278,3 +278,6 @@ web/
web-src.*
MediaBrowser.WebDashboard/jellyfin-web
apiclient/generated
+
+# Omnisharp crash logs
+mono_crash.*.json
From cf9c678406b3f412d70637da35ccb0e9f70c3a00 Mon Sep 17 00:00:00 2001
From: Mike
Date: Wed, 1 Sep 2021 18:59:59 +0200
Subject: [PATCH 109/118] Add subtitle format(codec) to stream display title
(#5853)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Michał Kurek
---
MediaBrowser.Model/Entities/MediaStream.cs | 5 ++
.../Entities/MediaStreamTests.cs | 80 +++++++++++++++++++
2 files changed, 85 insertions(+)
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 9653a8ece7..8955012d7a 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -255,6 +255,11 @@ namespace MediaBrowser.Model.Entities
attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced);
}
+ if (!string.IsNullOrEmpty(Codec))
+ {
+ attributes.Add(Codec.ToUpperInvariant());
+ }
+
if (!string.IsNullOrEmpty(Title))
{
var result = new StringBuilder(Title);
diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
index e2274e19ee..ce9ecea6a9 100644
--- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
+++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using MediaBrowser.Model.Entities;
using Xunit;
@@ -5,6 +6,85 @@ namespace Jellyfin.Model.Tests.Entities
{
public class MediaStreamTests
{
+ public static IEnumerable Get_DisplayTitle_TestData()
+ {
+ return new List
+ {
+ new object[]
+ {
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = "English",
+ Language = string.Empty,
+ IsForced = false,
+ IsDefault = false,
+ Codec = "ASS"
+ },
+ "English - Und - ASS"
+ },
+ new object[]
+ {
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = "English",
+ Language = string.Empty,
+ IsForced = false,
+ IsDefault = false,
+ Codec = string.Empty
+ },
+ "English - Und"
+ },
+ new object[]
+ {
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = "English",
+ Language = "EN",
+ IsForced = false,
+ IsDefault = false,
+ Codec = string.Empty
+ },
+ "English"
+ },
+ new object[]
+ {
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = "English",
+ Language = "EN",
+ IsForced = true,
+ IsDefault = true,
+ Codec = "SRT"
+ },
+ "English - Default - Forced - SRT"
+ },
+ new object[]
+ {
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = null,
+ Language = null,
+ IsForced = false,
+ IsDefault = false,
+ Codec = null
+ },
+ "Und"
+ }
+ };
+ }
+
+ [Theory]
+ [MemberData(nameof(Get_DisplayTitle_TestData))]
+ public void Get_DisplayTitle_should_return_valid_title(MediaStream mediaStream, string expected)
+ {
+ Assert.Equal(expected, mediaStream.DisplayTitle);
+ }
+
[Theory]
[InlineData(null, null, false, null)]
[InlineData(null, 0, false, null)]
From 2cf08dcd34db4d749a93de29e3ba13c633938e0c Mon Sep 17 00:00:00 2001
From: qsniyg
Date: Wed, 1 Sep 2021 15:52:59 -0700
Subject: [PATCH 110/118] Allow zero activity log retention days
---
.../ScheduledTasks/Tasks/CleanActivityLogTask.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
index 19600b1e64..79886cb52c 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
@@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public Task Execute(CancellationToken cancellationToken, IProgress progress)
{
var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays;
- if (!retentionDays.HasValue || retentionDays <= 0)
+ if (!retentionDays.HasValue || retentionDays < 0)
{
throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
}
From 4bed19e22fb2f548d001303a1f9a44475641aec6 Mon Sep 17 00:00:00 2001
From: Gokdag Goktepe
Date: Wed, 1 Sep 2021 07:59:23 +0000
Subject: [PATCH 111/118] Translated using Weblate (Turkish) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/
---
Emby.Server.Implementations/Localization/Core/tr.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index 771c91d59f..e661299c41 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Canlı TV",
"HeaderNextUp": "Gelecek Hafta",
"HeaderRecordingGroups": "Kayıt Grupları",
- "HomeVideos": "Ev videoları",
+ "HomeVideos": "Ana sayfa videoları",
"Inherit": "Devral",
"ItemAddedWithName": "{0} kütüphaneye eklendi",
"ItemRemovedWithName": "{0} kütüphaneden silindi",
From 286dabdc4bcff65430f0abe78fbeaaed28635e18 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Thu, 2 Sep 2021 21:28:00 +0200
Subject: [PATCH 112/118] Add SqliteItemRepository.ItemImageInfoFromValueString
as a fuzzing target
and add test cases
---
.../Data/SqliteItemRepository.cs | 12 +++++++-
.../Properties/AssemblyInfo.cs | 1 +
.../Emby.Server.Implementations.Fuzz.csproj | 7 +++++
.../Program.cs | 30 +++++++++++++++++++
.../test1.txt | 1 +
.../Data/SqliteItemRepositoryTests.cs | 3 ++
6 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 fuzz/Emby.Server.Implementations.Fuzz/Testcases/SqliteItemRepository.ItemImageInfoFromValueString/test1.txt
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 2cb10765ff..93d527a4d1 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -1135,15 +1135,25 @@ namespace Emby.Server.Implementations.Data
Path = RestorePath(path.ToString())
};
- if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks))
+ if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)
+ && ticks >= DateTime.MinValue.Ticks
+ && ticks <= DateTime.MaxValue.Ticks)
{
image.DateModified = new DateTime(ticks, DateTimeKind.Utc);
}
+ else
+ {
+ return null;
+ }
if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
{
image.Type = type;
}
+ else
+ {
+ return null;
+ }
// Optional parameters: width*height*blurhash
if (nextSegment + 1 < value.Length - 1)
diff --git a/Emby.Server.Implementations/Properties/AssemblyInfo.cs b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
index cb7972173e..41c396ac1d 100644
--- a/Emby.Server.Implementations/Properties/AssemblyInfo.cs
+++ b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
@@ -16,6 +16,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")]
+[assembly: InternalsVisibleTo("Emby.Server.Implementations.Fuzz")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
diff --git a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj
index 791cb140db..6abdb77342 100644
--- a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj
+++ b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj
@@ -12,6 +12,13 @@
+
+
+
+
+
+
+
diff --git a/fuzz/Emby.Server.Implementations.Fuzz/Program.cs b/fuzz/Emby.Server.Implementations.Fuzz/Program.cs
index a4a6f5f54d..03b2964948 100644
--- a/fuzz/Emby.Server.Implementations.Fuzz/Program.cs
+++ b/fuzz/Emby.Server.Implementations.Fuzz/Program.cs
@@ -1,5 +1,12 @@
using System;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Server.Implementations.Data;
using Emby.Server.Implementations.Library;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
+using Moq;
using SharpFuzz;
namespace Emby.Server.Implementations.Fuzz
@@ -11,6 +18,7 @@ namespace Emby.Server.Implementations.Fuzz
switch (args[0])
{
case "PathExtensions.TryReplaceSubPath": Run(PathExtensions_TryReplaceSubPath); return;
+ case "SqliteItemRepository.ItemImageInfoFromValueString": Run(SqliteItemRepository_ItemImageInfoFromValueString); return;
default: throw new ArgumentException($"Unknown fuzzing function: {args[0]}");
}
}
@@ -28,5 +36,27 @@ namespace Emby.Server.Implementations.Fuzz
_ = PathExtensions.TryReplaceSubPath(parts[0], parts[1], parts[2], out _);
}
+
+ private static void SqliteItemRepository_ItemImageInfoFromValueString(string data)
+ {
+ var sqliteItemRepository = MockSqliteItemRepository();
+ sqliteItemRepository.ItemImageInfoFromValueString(data);
+ }
+
+ private static SqliteItemRepository MockSqliteItemRepository()
+ {
+ const string VirtualMetaDataPath = "%MetadataPath%";
+ const string MetaDataPath = "/meta/data/path";
+
+ var appHost = new Mock();
+ appHost.Setup(x => x.ExpandVirtualPath(It.IsAny()))
+ .Returns((string x) => x.Replace(VirtualMetaDataPath, MetaDataPath, StringComparison.Ordinal));
+ appHost.Setup(x => x.ReverseVirtualPath(It.IsAny()))
+ .Returns((string x) => x.Replace(MetaDataPath, VirtualMetaDataPath, StringComparison.Ordinal));
+
+ IFixture fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
+ fixture.Inject(appHost);
+ return fixture.Create();
+ }
}
}
diff --git a/fuzz/Emby.Server.Implementations.Fuzz/Testcases/SqliteItemRepository.ItemImageInfoFromValueString/test1.txt b/fuzz/Emby.Server.Implementations.Fuzz/Testcases/SqliteItemRepository.ItemImageInfoFromValueString/test1.txt
new file mode 100644
index 0000000000..1b0115882f
--- /dev/null
+++ b/fuzz/Emby.Server.Implementations.Fuzz/Testcases/SqliteItemRepository.ItemImageInfoFromValueString/test1.txt
@@ -0,0 +1 @@
+/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs
index f312933fbf..a6e1dfe8f6 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs
@@ -109,6 +109,9 @@ namespace Jellyfin.Server.Implementations.Tests.Data
[InlineData("")]
[InlineData("*")]
[InlineData("https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0")]
+ [InlineData("/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*6374520964785129080*WjQbtJtSO8nhNZ%L_Io#R/oaS
Date: Fri, 3 Sep 2021 06:57:04 -0600
Subject: [PATCH 113/118] Fix issues from merge
---
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index bcfc4011c5..8125ed57df 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -15,8 +15,8 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos;
-using MediaBrowser.Common;
using Jellyfin.Extensions.Json;
+using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Cryptography;
From 611c20dba88ba83e41cb421baa12aa238af76216 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Fri, 3 Sep 2021 06:57:33 -0600
Subject: [PATCH 114/118] Fix indentation
---
.../Library/Validators/StudiosValidator.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index 7a22f851b4..8577d722e0 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -92,9 +92,9 @@ namespace Emby.Server.Implementations.Library.Validators
_libraryManager.DeleteItem(
item,
new DeleteOptions
- {
- DeleteFileLocation = false
- },
+ {
+ DeleteFileLocation = false
+ },
false);
}
From 3f2c7065756682c96c90189ab5157b96fdb92bd0 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Fri, 3 Sep 2021 07:19:50 -0600
Subject: [PATCH 115/118] Apply suggestions from code review
Co-authored-by: Claus Vium
---
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 5444e89050..1be3a1ca43 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -3306,7 +3306,7 @@ namespace MediaBrowser.Controller.MediaEncoding
inputModifier += " " + videoDecoder;
if (!IsCopyCodec(state.OutputVideoCodec)
- && videoDecoder.IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1)
+ && videoDecoder.Contains("cuvid", StringComparison.OrdinalIgnoreCase))
{
var videoStream = state.VideoStream;
var inputWidth = videoStream?.Width;
@@ -3315,7 +3315,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight);
- if (videoDecoder.IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1
+ if (videoDecoder.Contains("cuvid", StringComparison.OrdinalIgnoreCase)
&& width.HasValue
&& height.HasValue)
{
From bfb37a9ed9989f63ee8b00f41f4efef7a92fc157 Mon Sep 17 00:00:00 2001
From: Cody Robibero
Date: Fri, 3 Sep 2021 07:21:21 -0600
Subject: [PATCH 116/118] Fix typos
---
.../LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs | 2 +-
.../LiveTv/Listings/SchedulesDirectDtos/MapDto.cs | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs
index b8f27a8ac7..52e920aa61 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs
@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
///
/// Gets or sets the transport.
///
- [JsonPropertyName("transport.")]
+ [JsonPropertyName("transport")]
public string Transport { get; set; }
///
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs
index 8d45e8fff7..5140277b2c 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs
@@ -34,15 +34,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
public int UhfVhf { get; set; }
///
- /// Gets or sets the astc major.
+ /// Gets or sets the atsc major.
///
- [JsonPropertyName("astcMajor")]
+ [JsonPropertyName("atscMajor")]
public int AtscMajor { get; set; }
///
- /// Gets or sets the astc minor.
+ /// Gets or sets the atsc minor.
///
- [JsonPropertyName("astcMinor")]
+ [JsonPropertyName("atscMinor")]
public int AtscMinor { get; set; }
}
}
From 2f360151c266ff0885589338fdcdebd2a0c57fae Mon Sep 17 00:00:00 2001
From: siankatabg
Date: Thu, 2 Sep 2021 13:17:12 +0000
Subject: [PATCH 117/118] Translated using Weblate (Bulgarian) Translation:
Jellyfin/Jellyfin Translate-URL:
https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bg/
---
Emby.Server.Implementations/Localization/Core/bg-BG.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index f55287d15b..e1c9233087 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Телевизия на живо",
"HeaderNextUp": "Следва",
"HeaderRecordingGroups": "Запис групи",
- "HomeVideos": "Домашни клипове",
+ "HomeVideos": "Домашни Клипове",
"Inherit": "Наследяване",
"ItemAddedWithName": "{0} е добавено към библиотеката",
"ItemRemovedWithName": "{0} е премахнато от библиотеката",
@@ -39,7 +39,7 @@
"MixedContent": "Смесено съдържание",
"Movies": "Филми",
"Music": "Музика",
- "MusicVideos": "Музикални видеа",
+ "MusicVideos": "Музикални Видеа",
"NameInstallFailed": "{0} не можа да се инсталира",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Неразпознат сезон",
From b458f85c47e5f63e53062b8c96c564c674bb62dc Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Fri, 3 Sep 2021 16:39:03 +0200
Subject: [PATCH 118/118] Fix InvalidOperationException when serializing
MediaPathInfo
---
MediaBrowser.Model/Configuration/MediaPathInfo.cs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MediaBrowser.Model/Configuration/MediaPathInfo.cs b/MediaBrowser.Model/Configuration/MediaPathInfo.cs
index d096defcb8..a7bc435901 100644
--- a/MediaBrowser.Model/Configuration/MediaPathInfo.cs
+++ b/MediaBrowser.Model/Configuration/MediaPathInfo.cs
@@ -9,6 +9,12 @@ namespace MediaBrowser.Model.Configuration
Path = path;
}
+ // Needed for xml serialization
+ public MediaPathInfo()
+ {
+ Path = string.Empty;
+ }
+
public string Path { get; set; }
public string? NetworkPath { get; set; }