From e103d087d316bc19bf6227b21e8d9dfd091fd6a7 Mon Sep 17 00:00:00 2001 From: Max Git Date: Mon, 1 Jun 2020 07:10:15 +0200 Subject: [PATCH 001/102] Try harder at detecting FFmpeg version and enable the validation --- .../Encoder/EncoderValidator.cs | 120 ++++++++++++++---- .../Encoder/MediaEncoder.cs | 3 - .../EncoderValidatorTests.cs | 7 +- 3 files changed, 97 insertions(+), 33 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 6e036d24c1..d13d6045ea 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -12,7 +13,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { private const string DefaultEncoderPath = "ffmpeg"; - private static readonly string[] requiredDecoders = new[] + private static readonly string[] _requiredDecoders = new[] { "mpeg2video", "h264_qsv", @@ -33,7 +34,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc" }; - private static readonly string[] requiredEncoders = new[] + private static readonly string[] _requiredEncoders = new[] { "libx264", "libx265", @@ -61,7 +62,19 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_amf" }; - // Try and use the individual library versions to determine a FFmpeg version + // 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 + { + {"libavutil", 56.14}, + {"libavcodec", 58.18 }, + {"libavformat", 58.12 }, + {"libavdevice", 58.3 }, + {"libavfilter", 7.16 }, + {"libswscale", 5.1 }, + {"libswresample", 3.1}, + {"libpostproc", 55.1 } + }; + // This lookup table is to be maintained with the following command line: // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' private static readonly IReadOnlyDictionary _ffmpegVersionMap = new Dictionary @@ -123,32 +136,36 @@ namespace MediaBrowser.MediaEncoding.Encoder // Work out what the version under test is var version = GetFFmpegVersion(versionOutput); - _logger.LogInformation("Found ffmpeg version {0}", version != null ? version.ToString() : "unknown"); + _logger.LogInformation("Found ffmpeg version {Version}", version != null ? version.ToString() : "unknown"); if (version == null) { - if (MinVersion != null && MaxVersion != null) // Version is unknown + if (MaxVersion != null) // Version is unknown { if (MinVersion == MaxVersion) { - _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion); + _logger.LogWarning("FFmpeg validation: We recommend version {MinVersion}", MinVersion); } else { - _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion); + _logger.LogWarning("FFmpeg validation: We recommend a minimum of {MinVersion} and maximum of {MaxVersion}", MinVersion, MaxVersion); } } + else + { + _logger.LogWarning("FFmpeg validation: We recommend minimum version {MinVersion}", MinVersion); + } return false; } - else if (MinVersion != null && version < MinVersion) // Version is below what we recommend + else if (version < MinVersion) // Version is below what we recommend { - _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", MinVersion); + _logger.LogWarning("FFmpeg validation: The minimum recommended version is {MinVersion}", MinVersion); return false; } else if (MaxVersion != null && version > MaxVersion) // Version is above what we recommend { - _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", MaxVersion); + _logger.LogWarning("FFmpeg validation: The maximum recommended version is {MaxVersion}", MaxVersion); return false; } @@ -162,13 +179,12 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// 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 - /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions. - /// If that fails then we use one of the main libraries to determine if it's new/older than the latest - /// we have stored. + /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions. + /// If that fails then we test the libraries to determine if they're newer than our minimum versions. /// /// /// - internal static Version GetFFmpegVersion(string output) + internal Version GetFFmpegVersion(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?((?:\d+\.?)+)"); @@ -179,37 +195,87 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - // Create a reduced version string and lookup key from dictionary - var reducedVersion = GetLibrariesVersionString(output); + if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) + { + _logger.LogError("No ffmpeg library versions found"); + + return null; + } + + // First try to lookup the full version string + if (_ffmpegVersionMap.TryGetValue(versionString, out Version version)) + { + return version; + } + + // Then try to test for minimum library versions + return TestMinimumFFmpegLibraryVersions(versionMap); + } + } + + private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) + { + var allVersionsValidated = true; - // Try to lookup the string and return Key, otherwise if not found returns null - return _ffmpegVersionMap.TryGetValue(reducedVersion, out Version version) ? version : null; + foreach (var minimumVersion in _ffmpegMinimumLibraryVersions) + { + if (versionMap.TryGetValue(minimumVersion.Key, out var foundVersion)) + { + if (foundVersion >= minimumVersion.Value) + { + _logger.LogInformation("Found {Library} version {FoundVersion} ({MinimumVersion})", minimumVersion.Key, foundVersion, minimumVersion.Value); + } + else + { + _logger.LogWarning("Found {Library} version {FoundVersion} lower than recommended version {MinimumVersion}", minimumVersion.Key, foundVersion, minimumVersion.Value); + allVersionsValidated = false; + } + } + else + { + _logger.LogError("{Library} version not found", minimumVersion.Key); + allVersionsValidated = false; + } } + + return allVersionsValidated ? MinVersion : null; } /// /// Grabs the library names and major.minor version numbers from the 'ffmpeg -version' output - /// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc." /// /// + /// + /// /// - private static string GetLibrariesVersionString(string output) + private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) { - var rc = new StringBuilder(144); - foreach (Match m in Regex.Matches( + var sb = new StringBuilder(144); + + var map = new Dictionary(); + + foreach (Match match in Regex.Matches( output, @"((?lib\w+)\s+(?\d+)\.\s*(?\d+))", RegexOptions.Multiline)) { - rc.Append(m.Groups["name"]) + sb.Append(match.Groups["name"]) .Append('=') - .Append(m.Groups["major"]) + .Append(match.Groups["major"]) .Append('.') - .Append(m.Groups["minor"]) + .Append(match.Groups["minor"]) .Append(','); + + var str = $"{match.Groups["major"]}.{match.Groups["minor"]}"; + var versionNumber = double.Parse(str, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture); + + map.Add(match.Groups["name"].Value, versionNumber); } - return rc.Length == 0 ? null : rc.ToString(); + versionString = sb.ToString(); + versionMap = map as IReadOnlyDictionary; + + return sb.Length > 0; } private enum Codec @@ -236,7 +302,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return Enumerable.Empty(); } - var required = codec == Codec.Encoder ? requiredEncoders : requiredDecoders; + var required = codec == Codec.Encoder ? _requiredEncoders : _requiredDecoders; var found = Regex .Matches(output, @"^\s\S{6}\s(?[\w|-]+)\s+.+$", RegexOptions.Multiline) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 1377502dd9..2abd31a504 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -183,9 +183,6 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogWarning("FFmpeg: {Location}: Failed version check: {Path}", location, path); } - // ToDo - Enable the ffmpeg validator. At the moment any version can be used. - rc = true; - _ffmpegPath = path; EncoderLocation = location; } diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index e0f1f236c7..ae389efcd1 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -17,7 +17,7 @@ namespace Jellyfin.MediaEncoding.Tests yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, new Version(4, 0) }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -27,7 +27,8 @@ namespace Jellyfin.MediaEncoding.Tests [ClassData(typeof(GetFFmpegVersionTestData))] public void GetFFmpegVersionTest(string versionOutput, Version? version) { - Assert.Equal(version, EncoderValidator.GetFFmpegVersion(versionOutput)); + var val = new EncoderValidator(new NullLogger()); + Assert.Equal(version, val.GetFFmpegVersion(versionOutput)); } [Theory] @@ -35,7 +36,7 @@ namespace Jellyfin.MediaEncoding.Tests [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, true)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { var val = new EncoderValidator(new NullLogger()); From 480fd0a66a7d8e47542e89b4751b9cf59b5faa9b Mon Sep 17 00:00:00 2001 From: rotvel Date: Mon, 1 Jun 2020 16:00:58 +0200 Subject: [PATCH 002/102] Update MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs Co-authored-by: Cody Robibero --- .../Encoder/EncoderValidator.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index d13d6045ea..801479eede 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -65,14 +65,14 @@ namespace MediaBrowser.MediaEncoding.Encoder // 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 { - {"libavutil", 56.14}, - {"libavcodec", 58.18 }, - {"libavformat", 58.12 }, - {"libavdevice", 58.3 }, - {"libavfilter", 7.16 }, - {"libswscale", 5.1 }, - {"libswresample", 3.1}, - {"libpostproc", 55.1 } + { "libavutil", 56.14 }, + { "libavcodec", 58.18 }, + { "libavformat", 58.12 }, + { "libavdevice", 58.3 }, + { "libavfilter", 7.16 }, + { "libswscale", 5.1 }, + { "libswresample", 3.1 }, + { "libpostproc", 55.1 } }; // This lookup table is to be maintained with the following command line: From 3fbc387257da56885cf5fb00b90125deb889b453 Mon Sep 17 00:00:00 2001 From: Max Git Date: Mon, 15 Jun 2020 12:15:05 +0200 Subject: [PATCH 003/102] Fix stylecop error like on master. --- .../EncoderValidatorTests.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index ae389efcd1..a9e3cf7f07 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -9,20 +9,6 @@ namespace Jellyfin.MediaEncoding.Tests { public class EncoderValidatorTests { - private class GetFFmpegVersionTestData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, new Version(4, 0) }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - [Theory] [ClassData(typeof(GetFFmpegVersionTestData))] public void GetFFmpegVersionTest(string versionOutput, Version? version) @@ -42,5 +28,19 @@ namespace Jellyfin.MediaEncoding.Tests var val = new EncoderValidator(new NullLogger()); Assert.Equal(valid, val.ValidateVersionInternal(versionOutput)); } + + private class GetFFmpegVersionTestData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, new Version(4, 0) }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } } From 11f3a0dc58386c815b34e0579cb37c3557e366f4 Mon Sep 17 00:00:00 2001 From: Max Git Date: Mon, 15 Jun 2020 15:10:59 +0200 Subject: [PATCH 004/102] Use Version instead of double. Use correct version number for libavdevice. --- .../Encoder/EncoderValidator.cs | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 801479eede..28471b9561 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -63,16 +63,16 @@ namespace MediaBrowser.MediaEncoding.Encoder }; // 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 + private static readonly IReadOnlyDictionary _ffmpegMinimumLibraryVersions = new Dictionary { - { "libavutil", 56.14 }, - { "libavcodec", 58.18 }, - { "libavformat", 58.12 }, - { "libavdevice", 58.3 }, - { "libavfilter", 7.16 }, - { "libswscale", 5.1 }, - { "libswresample", 3.1 }, - { "libpostproc", 55.1 } + { "libavutil", new Version(56, 14) }, + { "libavcodec", new Version(58, 18) }, + { "libavformat", new Version(58, 12) }, + { "libavdevice", new Version(58, 3) }, + { "libavfilter", new Version(7, 16) }, + { "libswscale", new Version(5, 1) }, + { "libswresample", new Version(3, 1) }, + { "libpostproc", new Version(55, 1) } }; // This lookup table is to be maintained with the following command line: @@ -195,7 +195,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) + if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) { _logger.LogError("No ffmpeg library versions found"); @@ -213,7 +213,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) + private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) { var allVersionsValidated = true; @@ -248,11 +248,11 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// /// - private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) + private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) { var sb = new StringBuilder(144); - var map = new Dictionary(); + var map = new Dictionary(); foreach (Match match in Regex.Matches( output, @@ -267,13 +267,14 @@ namespace MediaBrowser.MediaEncoding.Encoder .Append(','); var str = $"{match.Groups["major"]}.{match.Groups["minor"]}"; - var versionNumber = double.Parse(str, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture); - map.Add(match.Groups["name"].Value, versionNumber); + var version = Version.Parse(str); + + map.Add(match.Groups["name"].Value, version); } versionString = sb.ToString(); - versionMap = map as IReadOnlyDictionary; + versionMap = map as IReadOnlyDictionary; return sb.Length > 0; } From ef3200e178e41cede865e0876a6d56171b1f4cce Mon Sep 17 00:00:00 2001 From: Max Git Date: Mon, 15 Jun 2020 19:50:09 +0200 Subject: [PATCH 005/102] Remove redundant cast --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 28471b9561..a055e57ec8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -274,7 +274,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } versionString = sb.ToString(); - versionMap = map as IReadOnlyDictionary; + versionMap = map; return sb.Length > 0; } From e6c197b96991a619d58e816ee56c23644c4a1de1 Mon Sep 17 00:00:00 2001 From: Max Git Date: Tue, 16 Jun 2020 01:09:41 +0200 Subject: [PATCH 006/102] Cleanup --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index a055e57ec8..be46255053 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; From cbe47325b328b70e5a49dc231a70d3709f71dbbd Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 23 Jul 2020 13:18:47 +0200 Subject: [PATCH 007/102] Make UNIX socket configurable --- .../ConfigurationOptions.cs | 4 +-- Jellyfin.Server/Program.cs | 21 ++++++++++++--- .../Extensions/ConfigurationExtensions.cs | 26 +++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index ff7ee085f8..9a4693db00 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Emby.Server.Implementations.HttpServer; -using Emby.Server.Implementations.Updates; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; namespace Emby.Server.Implementations @@ -19,7 +18,8 @@ namespace Emby.Server.Implementations { HttpListenerHost.DefaultRedirectKey, "web/index.html" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" }, - { PlaylistsAllowDuplicatesKey, bool.TrueString } + { PlaylistsAllowDuplicatesKey, bool.TrueString }, + { BindToUnixSocketKey, bool.TrueString } }; } } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 444a91c024..f3ee339d12 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -344,11 +344,24 @@ namespace Jellyfin.Server } } - // Bind to unix socket (only on OSX and Linux) - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + // Bind to unix socket (only on macOS and Linux) + if (startupConfig.UseUnixSocket() && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // TODO: allow configuration of socket path - var socketPath = $"{appPaths.DataPath}/socket.sock"; + var socketPath = startupConfig.GetUnixSocketPath(); + if (string.IsNullOrEmpty(socketPath)) + { + var xdgRuntimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR"); + if (xdgRuntimeDir == null) + { + // Fall back to config dir + socketPath = Path.Join(appPaths.ConfigurationDirectoryPath, "socket.sock"); + } + else + { + socketPath = Path.Join(xdgRuntimeDir, "jellyfin-socket"); + } + } + // Workaround for https://github.com/aspnet/AspNetCore/issues/14134 if (File.Exists(socketPath)) { diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs index c2932cc4c4..ae02c1ceeb 100644 --- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs +++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs @@ -33,6 +33,16 @@ namespace MediaBrowser.Controller.Extensions /// public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates"; + /// + /// The key for a setting that indicates whether kestrel should bind to a unix socket. + /// + public const string BindToUnixSocketKey = "kerstrel:socket"; + + /// + /// The key for the unix socket path. + /// + public const string UnixSocketPathKey = "kerstrel:socketPath"; + /// /// Gets a value indicating whether the application should host static web content from the . /// @@ -65,5 +75,21 @@ namespace MediaBrowser.Controller.Extensions /// True if playlists should allow duplicates, otherwise false. public static bool DoPlaylistsAllowDuplicates(this IConfiguration configuration) => configuration.GetValue(PlaylistsAllowDuplicatesKey); + + /// + /// Gets a value indicating whether kestrel should bind to a unix socket from the . + /// + /// The configuration to read the setting from. + /// true if kestrel should bind to a unix socket, otherwise false. + public static bool UseUnixSocket(this IConfiguration configuration) + => configuration.GetValue(BindToUnixSocketKey); + + /// + /// Gets the path for the unix socket from the . + /// + /// The configuration to read the setting from. + /// The unix socket path. + public static string GetUnixSocketPath(this IConfiguration configuration) + => configuration[FfmpegAnalyzeDurationKey]; } } From d006f4301a472038d1ee77c8c3b3c615b4a0f59a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 23 Jul 2020 13:20:10 +0200 Subject: [PATCH 008/102] Disable unix socket by default --- Emby.Server.Implementations/ConfigurationOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 9a4693db00..64ccff53b3 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -19,7 +19,7 @@ namespace Emby.Server.Implementations { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" }, { PlaylistsAllowDuplicatesKey, bool.TrueString }, - { BindToUnixSocketKey, bool.TrueString } + { BindToUnixSocketKey, bool.FalseString } }; } } From 1cc62d6afa121607abf5c68a088de790f105f917 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 24 Jul 2020 09:36:18 +0200 Subject: [PATCH 009/102] Update MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs Co-authored-by: David Mouse --- MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs index ae02c1ceeb..425b39fd9b 100644 --- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs +++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs @@ -36,12 +36,12 @@ namespace MediaBrowser.Controller.Extensions /// /// The key for a setting that indicates whether kestrel should bind to a unix socket. /// - public const string BindToUnixSocketKey = "kerstrel:socket"; + public const string BindToUnixSocketKey = "kestrel:socket"; /// /// The key for the unix socket path. /// - public const string UnixSocketPathKey = "kerstrel:socketPath"; + public const string UnixSocketPathKey = "kestrel:socketPath"; /// /// Gets a value indicating whether the application should host static web content from the . From 5c65abcd948b4b47ba8e11cd77f5ada33c4550bb Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 4 Aug 2020 18:58:14 -0400 Subject: [PATCH 010/102] Clean up TunerHost classes --- .../LiveTv/TunerHosts/BaseTunerHost.cs | 36 +++++------- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 56 +++++++++---------- .../LiveTv/TunerHosts/M3UTunerHost.cs | 8 +-- 3 files changed, 46 insertions(+), 54 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index a8d34d19c8..6b8b82bc20 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -1,10 +1,10 @@ #pragma warning disable CS1591 using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -14,7 +14,7 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; -using MediaBrowser.Model.Serialization; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.TunerHosts @@ -23,17 +23,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { protected readonly IServerConfigurationManager Config; protected readonly ILogger Logger; - protected IJsonSerializer JsonSerializer; protected readonly IFileSystem FileSystem; - private readonly ConcurrentDictionary _channelCache = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly IMemoryCache _memoryCache; - protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem) + protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IFileSystem fileSystem, IMemoryCache memoryCache) { Config = config; Logger = logger; - JsonSerializer = jsonSerializer; + _memoryCache = memoryCache; FileSystem = fileSystem; } @@ -44,23 +42,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public async Task> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken) { - ChannelCache cache = null; var key = tuner.Id; - if (enableCache && !string.IsNullOrEmpty(key) && _channelCache.TryGetValue(key, out cache)) + if (enableCache && !string.IsNullOrEmpty(key) && _memoryCache.TryGetValue(key, out List cache)) { - return cache.Channels.ToList(); + return cache; } - var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false); - var list = result.ToList(); + var list = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false); // logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list)); if (!string.IsNullOrEmpty(key) && list.Count > 0) { - cache = cache ?? new ChannelCache(); - cache.Channels = list; - _channelCache.AddOrUpdate(key, cache, (k, v) => cache); + _memoryCache.CreateEntry(key).SetValue(list); } return list; @@ -95,7 +89,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts try { Directory.CreateDirectory(Path.GetDirectoryName(channelCacheFile)); - JsonSerializer.SerializeToFile(channels, channelCacheFile); + await using var writeStream = File.OpenWrite(channelCacheFile); + await JsonSerializer.SerializeAsync(writeStream, channels, cancellationToken: cancellationToken).ConfigureAwait(false); } catch (IOException) { @@ -110,7 +105,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { try { - var channels = JsonSerializer.DeserializeFromFile>(channelCacheFile); + await using var readStream = File.OpenRead(channelCacheFile); + var channels = await JsonSerializer.DeserializeAsync>(readStream, cancellationToken: cancellationToken) + .ConfigureAwait(false); list.AddRange(channels); } catch (IOException) @@ -233,10 +230,5 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { return Config.GetConfiguration("livetv"); } - - private class ChannelCache - { - public List Channels; - } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 00420bd2ad..c61189c0ac 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -23,7 +24,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun @@ -39,14 +40,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public HdHomerunHost( IServerConfigurationManager config, ILogger logger, - IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, - IStreamHelper streamHelper) - : base(config, logger, jsonSerializer, fileSystem) + IStreamHelper streamHelper, + IMemoryCache memoryCache) + : base(config, logger, fileSystem, memoryCache) { _httpClient = httpClient; _appHost = appHost; @@ -75,18 +76,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun BufferContent = false }; - using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false)) - using (var stream = response.Content) - { - var lineup = await JsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false) ?? new List(); - - if (info.ImportFavoritesOnly) - { - lineup = lineup.Where(i => i.Favorite).ToList(); - } + using var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false); + await using var stream = response.Content; + var lineup = await JsonSerializer.DeserializeAsync>(stream, cancellationToken: cancellationToken) + .ConfigureAwait(false) ?? new List(); - return lineup.Where(i => !i.DRM).ToList(); + if (info.ImportFavoritesOnly) + { + lineup = lineup.Where(i => i.Favorite).ToList(); } + + return lineup.Where(i => !i.DRM).ToList(); } private class HdHomerunChannelInfo : ChannelInfo @@ -132,30 +132,30 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun try { - using (var response = await _httpClient.SendAsync(new HttpRequestOptions() + using var response = await _httpClient.SendAsync( + new HttpRequestOptions { - Url = string.Format("{0}/discover.json", GetApiUrl(info)), + Url = string.Format(CultureInfo.InvariantCulture, "{0}/discover.json", GetApiUrl(info)), CancellationToken = cancellationToken, BufferContent = false - }, HttpMethod.Get).ConfigureAwait(false)) - using (var stream = response.Content) - { - var discoverResponse = await JsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + }, HttpMethod.Get).ConfigureAwait(false); + await using var stream = response.Content; + var discoverResponse = await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken) + .ConfigureAwait(false); - if (!string.IsNullOrEmpty(cacheKey)) + if (!string.IsNullOrEmpty(cacheKey)) + { + lock (_modelCache) { - lock (_modelCache) - { - _modelCache[cacheKey] = discoverResponse; - } + _modelCache[cacheKey] = discoverResponse; } - - return discoverResponse; } + + return discoverResponse; } catch (HttpException ex) { - if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == System.Net.HttpStatusCode.NotFound) + if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) { var defaultValue = "HDHR"; var response = new DiscoverResponse diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index ff42a9747f..8fc29fb4a2 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -18,7 +18,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; @@ -36,13 +36,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, - IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, INetworkManager networkManager, - IStreamHelper streamHelper) - : base(config, logger, jsonSerializer, fileSystem) + IStreamHelper streamHelper, + IMemoryCache memoryCache) + : base(config, logger, fileSystem, memoryCache) { _httpClient = httpClient; _appHost = appHost; From 8373d6297ccd7f21ae16ce1b861b1ed9d45bdfec Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 7 Aug 2020 14:05:47 -0400 Subject: [PATCH 011/102] Fix MemoryCache usage --- 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 6b8b82bc20..fbcd4ef372 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts if (!string.IsNullOrEmpty(key) && list.Count > 0) { - _memoryCache.CreateEntry(key).SetValue(list); + _memoryCache.Set(key, list); } return list; From 6c92154a9bc78deeafa313c96c1fe474a5976776 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 9 Aug 2020 13:55:57 +0200 Subject: [PATCH 012/102] Update MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs Co-authored-by: David --- MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs index 425b39fd9b..4c2209b67c 100644 --- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs +++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs @@ -90,6 +90,6 @@ namespace MediaBrowser.Controller.Extensions /// The configuration to read the setting from. /// The unix socket path. public static string GetUnixSocketPath(this IConfiguration configuration) - => configuration[FfmpegAnalyzeDurationKey]; + => configuration[UnixSocketPathKey]; } } From 371a09c60bddb49298e2cf7f1968514ab4d243e4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 Aug 2020 17:38:01 +0200 Subject: [PATCH 013/102] MemoryStream optimizations --- .../AppBase/ConfigurationHelper.cs | 16 +++++++--- .../HttpServer/HttpResultFactory.cs | 5 +-- .../HttpServer/StreamWriter.cs | 4 +-- .../Serialization/MyXmlSerializer.cs | 2 +- Jellyfin.Api/Controllers/ImageController.cs | 2 +- MediaBrowser.Providers/Manager/ImageSaver.cs | 31 ++++++++++--------- .../Subtitles/SubtitleManager.cs | 2 +- 7 files changed, 36 insertions(+), 26 deletions(-) diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs index 0b681fddfc..4c9ab33a7c 100644 --- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs +++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.IO; using System.Linq; @@ -22,7 +24,7 @@ namespace Emby.Server.Implementations.AppBase { object configuration; - byte[] buffer = null; + byte[]? buffer = null; // Use try/catch to avoid the extra file system lookup using File.Exists try @@ -36,19 +38,23 @@ namespace Emby.Server.Implementations.AppBase configuration = Activator.CreateInstance(type); } - using var stream = new MemoryStream(); + using var stream = new MemoryStream(buffer?.Length ?? 0); xmlSerializer.SerializeToStream(configuration, stream); // Take the object we just got and serialize it back to bytes - var newBytes = stream.ToArray(); + byte[] newBytes = stream.GetBuffer(); + int newBytesLen = (int)stream.Length; // If the file didn't exist before, or if something has changed, re-save - if (buffer == null || !buffer.SequenceEqual(newBytes)) + if (buffer == null || !newBytes.AsSpan(0, newBytesLen).SequenceEqual(buffer)) { Directory.CreateDirectory(Path.GetDirectoryName(path)); // Save it after load in case we got new items - File.WriteAllBytes(path, newBytes); + using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + fs.Write(newBytes, 0, newBytesLen); + } } return configuration; diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 970f5119c8..688216373c 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders = new Dictionary(); } - if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string expires)) + if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out _)) { responseHeaders[HeaderNames.Expires] = "0"; } @@ -326,7 +326,8 @@ namespace Emby.Server.Implementations.HttpServer return GetHttpResult(request, ms, contentType, true, responseHeaders); } - private IHasHeaders GetCompressedResult(byte[] content, + private IHasHeaders GetCompressedResult( + byte[] content, string requestedCompressionType, IDictionary responseHeaders, bool isHeadRequest, diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs index 5afc51dbc3..00e3ab8fec 100644 --- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs +++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs @@ -95,13 +95,13 @@ namespace Emby.Server.Implementations.HttpServer if (bytes != null) { - await responseStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + await responseStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); } else { using (var src = SourceStream) { - await src.CopyToAsync(responseStream).ConfigureAwait(false); + await src.CopyToAsync(responseStream, cancellationToken).ConfigureAwait(false); } } } diff --git a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs index 2968229810..c8986461ec 100644 --- a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs +++ b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs @@ -95,7 +95,7 @@ namespace Emby.Server.Implementations.Serialization /// System.Object. public object DeserializeFromBytes(Type type, byte[] buffer) { - using (var stream = new MemoryStream(buffer)) + using (var stream = new MemoryStream(buffer, 0, buffer.Length, false, true)) { return DeserializeFromStream(type, stream); } diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 45447ae0cc..8f5c6beb30 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -969,7 +969,7 @@ namespace Jellyfin.Api.Controllers var text = await reader.ReadToEndAsync().ConfigureAwait(false); var bytes = Convert.FromBase64String(text); - return new MemoryStream(bytes) { Position = 0 }; + return new MemoryStream(bytes, 0, bytes.Length, false, true); } private ImageInfo? GetImageInfo(BaseItem item, ItemImageInfo info, int? imageIndex) diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 32b543feff..26b50784b1 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -124,13 +124,16 @@ namespace MediaBrowser.Providers.Manager var retryPaths = GetSavePaths(item, type, imageIndex, mimeType, false); // If there are more than one output paths, the stream will need to be seekable - var memoryStream = new MemoryStream(); - await using (source.ConfigureAwait(false)) + if (paths.Length > 1 && !source.CanSeek) { - await source.CopyToAsync(memoryStream).ConfigureAwait(false); - } + var memoryStream = new MemoryStream(); + await using (source.ConfigureAwait(false)) + { + await source.CopyToAsync(memoryStream).ConfigureAwait(false); + } - source = memoryStream; + source = memoryStream; + } var currentImage = GetCurrentImage(item, type, index); var currentImageIsLocalFile = currentImage != null && currentImage.IsLocalFile; @@ -140,20 +143,21 @@ namespace MediaBrowser.Providers.Manager await using (source.ConfigureAwait(false)) { - var currentPathIndex = 0; - - foreach (var path in paths) + for (int i = 0; i < paths.Length; i++) { - source.Position = 0; + if (i != 0) + { + source.Position = 0; + } + string retryPath = null; if (paths.Length == retryPaths.Length) { - retryPath = retryPaths[currentPathIndex]; + retryPath = retryPaths[i]; } - var savedPath = await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false); + var savedPath = await SaveImageToLocation(source, paths[i], retryPath, cancellationToken).ConfigureAwait(false); savedPaths.Add(savedPath); - currentPathIndex++; } } @@ -224,7 +228,6 @@ namespace MediaBrowser.Providers.Manager } } - source.Position = 0; await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false); return retryPath; } @@ -253,7 +256,7 @@ namespace MediaBrowser.Providers.Manager await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) { - await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false); + await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false); } if (_config.Configuration.SaveMetadataHidden) diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 3510b90cfc..0f7cb3f8fa 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.Subtitles CancellationToken cancellationToken) { var parts = subtitleId.Split(new[] { '_' }, 2); - var provider = GetProvider(parts.First()); + var provider = GetProvider(parts[0]); var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia; From 4a4dff19731529db710907b355cd620c828683e7 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 Aug 2020 19:48:52 +0200 Subject: [PATCH 014/102] Fix --- .../Serialization/MyXmlSerializer.cs | 8 +++++--- MediaBrowser.Model/IO/IODefaults.cs | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs index c8986461ec..27024e4e1c 100644 --- a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs +++ b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.IO; using System.Xml; using System.Xml.Serialization; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; namespace Emby.Server.Implementations.Serialization @@ -53,10 +54,11 @@ namespace Emby.Server.Implementations.Serialization /// The stream. public void SerializeToStream(object obj, Stream stream) { - using (var writer = new XmlTextWriter(stream, null)) + using (var writer = new StreamWriter(stream, null, IODefaults.StreamWriterBufferSize, true)) + using (var textWriter = new XmlTextWriter(writer)) { - writer.Formatting = Formatting.Indented; - SerializeToWriter(obj, writer); + textWriter.Formatting = Formatting.Indented; + SerializeToWriter(obj, textWriter); } } diff --git a/MediaBrowser.Model/IO/IODefaults.cs b/MediaBrowser.Model/IO/IODefaults.cs index f392dbcce2..5eaef94425 100644 --- a/MediaBrowser.Model/IO/IODefaults.cs +++ b/MediaBrowser.Model/IO/IODefaults.cs @@ -14,5 +14,10 @@ namespace MediaBrowser.Model.IO /// The default file stream buffer size. /// public const int FileStreamBufferSize = 4096; + + /// + /// The default buffer size. + /// + public const int StreamWriterBufferSize = 1024; } } From 593dbcabffd1a0c09af64f3a19ed46c97d626559 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 Aug 2020 20:48:32 +0200 Subject: [PATCH 015/102] Fix docs --- MediaBrowser.Model/IO/IODefaults.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/IO/IODefaults.cs b/MediaBrowser.Model/IO/IODefaults.cs index 5eaef94425..d9a1e67770 100644 --- a/MediaBrowser.Model/IO/IODefaults.cs +++ b/MediaBrowser.Model/IO/IODefaults.cs @@ -1,3 +1,5 @@ +using System.IO; + namespace MediaBrowser.Model.IO { /// @@ -16,7 +18,7 @@ namespace MediaBrowser.Model.IO public const int FileStreamBufferSize = 4096; /// - /// The default buffer size. + /// The default buffer size. /// public const int StreamWriterBufferSize = 1024; } From 7b864e3a3c286c9502b6ef3ff1d13754e796706a Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 9 Aug 2020 14:12:39 -0600 Subject: [PATCH 016/102] parameter isn't actually required --- Jellyfin.Api/Controllers/SessionController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 48b57bdb7f..9bdaa17833 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -413,7 +413,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult PostFullCapabilities( - [FromQuery, Required] string? id, + [FromQuery] string? id, [FromBody, Required] ClientCapabilities capabilities) { if (string.IsNullOrWhiteSpace(id)) From 460c3dd35166c9a48db83db62a3f0a8742956408 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 9 Aug 2020 17:20:14 -0600 Subject: [PATCH 017/102] convert dependent controller functions to di helper class --- .../ApplicationHost.cs | 3 + Jellyfin.Api/Controllers/AudioController.cs | 167 +---- .../Controllers/DynamicHlsController.cs | 446 +------------- .../Controllers/MediaInfoController.cs | 538 ++-------------- .../Controllers/UniversalAudioController.cs | 280 ++++----- Jellyfin.Api/Helpers/AudioHelper.cs | 193 ++++++ Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 549 +++++++++++++++++ Jellyfin.Api/Helpers/MediaInfoHelper.cs | 573 ++++++++++++++++++ 8 files changed, 1510 insertions(+), 1239 deletions(-) create mode 100644 Jellyfin.Api/Helpers/AudioHelper.cs create mode 100644 Jellyfin.Api/Helpers/DynamicHlsHelper.cs create mode 100644 Jellyfin.Api/Helpers/MediaInfoHelper.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0201ed7a34..99530bfbd2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -632,6 +632,9 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); } /// diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 4de87616c5..107db95715 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -1,93 +1,32 @@ using System; using System.Collections.Generic; -using System.Net.Http; -using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.StreamingDtos; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; namespace Jellyfin.Api.Controllers { /// /// The audio controller. /// - // TODO: In order to autheneticate this in the future, Dlna playback will require updating + // TODO: In order to authenticate this in the future, Dlna playback will require updating public class AudioController : BaseJellyfinApiController { - private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; - private readonly IUserManager _userManager; - private readonly ILibraryManager _libraryManager; - private readonly IMediaSourceManager _mediaSourceManager; - private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly IMediaEncoder _mediaEncoder; - private readonly IFileSystem _fileSystem; - private readonly ISubtitleEncoder _subtitleEncoder; - private readonly IConfiguration _configuration; - private readonly IDeviceManager _deviceManager; - private readonly TranscodingJobHelper _transcodingJobHelper; - private readonly IHttpClientFactory _httpClientFactory; + private readonly AudioHelper _audioHelper; private readonly TranscodingJobType _transcodingJobType = TranscodingJobType.Progressive; /// /// 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. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// The singleton. - /// Instance of the interface. - public AudioController( - IDlnaManager dlnaManager, - IUserManager userManger, - IAuthorizationContext authorizationContext, - ILibraryManager libraryManager, - IMediaSourceManager mediaSourceManager, - IServerConfigurationManager serverConfigurationManager, - IMediaEncoder mediaEncoder, - IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, - IDeviceManager deviceManager, - TranscodingJobHelper transcodingJobHelper, - IHttpClientFactory httpClientFactory) + /// Instance of . + public AudioController(AudioHelper audioHelper) { - _dlnaManager = dlnaManager; - _authContext = authorizationContext; - _userManager = userManger; - _libraryManager = libraryManager; - _mediaSourceManager = mediaSourceManager; - _serverConfigurationManager = serverConfigurationManager; - _mediaEncoder = mediaEncoder; - _fileSystem = fileSystem; - _subtitleEncoder = subtitleEncoder; - _configuration = configuration; - _deviceManager = deviceManager; - _transcodingJobHelper = transcodingJobHelper; - _httpClientFactory = httpClientFactory; + _audioHelper = audioHelper; } /// @@ -200,10 +139,6 @@ namespace Jellyfin.Api.Controllers [FromQuery] EncodingContext? context, [FromQuery] Dictionary? streamOptions) { - bool isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head; - - var cancellationTokenSource = new CancellationTokenSource(); - StreamingRequestDto streamingRequest = new StreamingRequestDto { Id = itemId, @@ -257,97 +192,7 @@ namespace Jellyfin.Api.Controllers StreamOptions = streamOptions }; - using var state = await StreamingHelpers.GetStreamingState( - streamingRequest, - Request, - _authContext, - _mediaSourceManager, - _userManager, - _libraryManager, - _serverConfigurationManager, - _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, - _dlnaManager, - _deviceManager, - _transcodingJobHelper, - _transcodingJobType, - cancellationTokenSource.Token) - .ConfigureAwait(false); - - if (@static.HasValue && @static.Value && state.DirectStreamProvider != null) - { - StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager); - - await new ProgressiveFileCopier(state.DirectStreamProvider, null, _transcodingJobHelper, CancellationToken.None) - { - AllowEndOfFile = false - }.WriteToAsync(Response.Body, CancellationToken.None) - .ConfigureAwait(false); - - // TODO (moved from MediaBrowser.Api): Don't hardcode contentType - return File(Response.Body, MimeTypes.GetMimeType("file.ts")!); - } - - // Static remote stream - if (@static.HasValue && @static.Value && state.InputProtocol == MediaProtocol.Http) - { - StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager); - - using var httpClient = _httpClientFactory.CreateClient(); - return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, httpClient).ConfigureAwait(false); - } - - if (@static.HasValue && @static.Value && state.InputProtocol != MediaProtocol.File) - { - return BadRequest($"Input protocol {state.InputProtocol} cannot be streamed statically"); - } - - var outputPath = state.OutputFilePath; - var outputPathExists = System.IO.File.Exists(outputPath); - - var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive); - var isTranscodeCached = outputPathExists && transcodingJob != null; - - StreamingHelpers.AddDlnaHeaders(state, Response.Headers, (@static.HasValue && @static.Value) || isTranscodeCached, startTimeTicks, Request, _dlnaManager); - - // Static stream - if (@static.HasValue && @static.Value) - { - var contentType = state.GetMimeType("." + state.OutputContainer, false) ?? state.GetMimeType(state.MediaPath); - - if (state.MediaSource.IsInfiniteStream) - { - await new ProgressiveFileCopier(state.MediaPath, null, _transcodingJobHelper, CancellationToken.None) - { - AllowEndOfFile = false - }.WriteToAsync(Response.Body, CancellationToken.None) - .ConfigureAwait(false); - - return File(Response.Body, contentType); - } - - return FileStreamResponseHelpers.GetStaticFileResult( - state.MediaPath, - contentType, - isHeadRequest, - this); - } - - // Need to start ffmpeg (because media can't be returned directly) - var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); - var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); - var ffmpegCommandLineArguments = encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath); - return await FileStreamResponseHelpers.GetTranscodedFile( - state, - isHeadRequest, - this, - _transcodingJobHelper, - ffmpegCommandLineArguments, - Request, - _transcodingJobType, - cancellationTokenSource).ConfigureAwait(false); + return await _audioHelper.GetAudioStream(this, _transcodingJobType, streamingRequest).ConfigureAwait(false); } } } diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index d581ab8cd2..1a85a07b88 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -13,7 +13,6 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.PlaybackDtos; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; @@ -22,7 +21,6 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Authorization; @@ -53,9 +51,9 @@ namespace Jellyfin.Api.Controllers private readonly IConfiguration _configuration; private readonly IDeviceManager _deviceManager; private readonly TranscodingJobHelper _transcodingJobHelper; - private readonly INetworkManager _networkManager; private readonly ILogger _logger; private readonly EncodingHelper _encodingHelper; + private readonly DynamicHlsHelper _dynamicHlsHelper; private readonly TranscodingJobType _transcodingJobType = TranscodingJobType.Hls; @@ -74,8 +72,8 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the class. - /// Instance of the interface. /// Instance of the interface. + /// Instance of . public DynamicHlsController( ILibraryManager libraryManager, IUserManager userManager, @@ -89,8 +87,8 @@ namespace Jellyfin.Api.Controllers IConfiguration configuration, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, - INetworkManager networkManager, - ILogger logger) + ILogger logger, + DynamicHlsHelper dynamicHlsHelper) { _libraryManager = libraryManager; _userManager = userManager; @@ -104,8 +102,8 @@ namespace Jellyfin.Api.Controllers _configuration = configuration; _deviceManager = deviceManager; _transcodingJobHelper = transcodingJobHelper; - _networkManager = networkManager; _logger = logger; + _dynamicHlsHelper = dynamicHlsHelper; _encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); } @@ -220,8 +218,6 @@ namespace Jellyfin.Api.Controllers [FromQuery] Dictionary streamOptions, [FromQuery] bool enableAdaptiveBitrateStreaming = true) { - var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head; - var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new HlsVideoRequestDto { Id = itemId, @@ -276,8 +272,7 @@ namespace Jellyfin.Api.Controllers EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming }; - return await GetMasterPlaylistInternal(streamingRequest, isHeadRequest, enableAdaptiveBitrateStreaming, cancellationTokenSource) - .ConfigureAwait(false); + return await _dynamicHlsHelper.GetMasterHlsPlaylist(this, _transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); } /// @@ -390,8 +385,6 @@ namespace Jellyfin.Api.Controllers [FromQuery] Dictionary streamOptions, [FromQuery] bool enableAdaptiveBitrateStreaming = true) { - var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head; - var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new HlsAudioRequestDto { Id = itemId, @@ -446,8 +439,7 @@ namespace Jellyfin.Api.Controllers EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming }; - return await GetMasterPlaylistInternal(streamingRequest, isHeadRequest, enableAdaptiveBitrateStreaming, cancellationTokenSource) - .ConfigureAwait(false); + return await _dynamicHlsHelper.GetMasterHlsPlaylist(this, _transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); } /// @@ -1118,106 +1110,6 @@ namespace Jellyfin.Api.Controllers .ConfigureAwait(false); } - private async Task GetMasterPlaylistInternal( - StreamingRequestDto streamingRequest, - bool isHeadRequest, - bool enableAdaptiveBitrateStreaming, - CancellationTokenSource cancellationTokenSource) - { - using var state = await StreamingHelpers.GetStreamingState( - streamingRequest, - Request, - _authContext, - _mediaSourceManager, - _userManager, - _libraryManager, - _serverConfigurationManager, - _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, - _dlnaManager, - _deviceManager, - _transcodingJobHelper, - _transcodingJobType, - cancellationTokenSource.Token) - .ConfigureAwait(false); - - Response.Headers.Add(HeaderNames.Expires, "0"); - if (isHeadRequest) - { - return new FileContentResult(Array.Empty(), MimeTypes.GetMimeType("playlist.m3u8")); - } - - var totalBitrate = state.OutputAudioBitrate ?? 0 + state.OutputVideoBitrate ?? 0; - - var builder = new StringBuilder(); - - builder.AppendLine("#EXTM3U"); - - var isLiveStream = state.IsSegmentedLiveStream; - - var queryString = Request.QueryString.ToString(); - - // from universal audio service - if (queryString.IndexOf("SegmentContainer", StringComparison.OrdinalIgnoreCase) == -1 && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer)) - { - queryString += "&SegmentContainer=" + state.Request.SegmentContainer; - } - - // from universal audio service - if (!string.IsNullOrWhiteSpace(state.Request.TranscodeReasons) && queryString.IndexOf("TranscodeReasons=", StringComparison.OrdinalIgnoreCase) == -1) - { - queryString += "&TranscodeReasons=" + state.Request.TranscodeReasons; - } - - // Main stream - var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8"; - - playlistUrl += queryString; - - var subtitleStreams = state.MediaSource - .MediaStreams - .Where(i => i.IsTextSubtitleStream) - .ToList(); - - var subtitleGroup = subtitleStreams.Count > 0 && (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Hls || state.VideoRequest!.EnableSubtitlesInManifest) - ? "subs" - : null; - - // If we're burning in subtitles then don't add additional subs to the manifest - if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) - { - subtitleGroup = null; - } - - if (!string.IsNullOrWhiteSpace(subtitleGroup)) - { - AddSubtitles(state, subtitleStreams, builder); - } - - AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup); - - if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming)) - { - var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0; - - // By default, vary by just 200k - var variation = GetBitrateVariation(totalBitrate); - - var newBitrate = totalBitrate - variation; - var variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); - AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup); - - variation *= 2; - newBitrate = totalBitrate - variation; - variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); - AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup); - } - - return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8")); - } - private async Task GetVariantPlaylistInternal(StreamingRequestDto streamingRequest, string name, CancellationTokenSource cancellationTokenSource) { using var state = await StreamingHelpers.GetStreamingState( @@ -1411,330 +1303,6 @@ namespace Jellyfin.Api.Controllers return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false); } - private void AddSubtitles(StreamState state, IEnumerable subtitles, StringBuilder builder) - { - var selectedIndex = state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index; - const string Format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\""; - - foreach (var stream in subtitles) - { - var name = stream.DisplayTitle; - - var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index; - var isForced = stream.IsForced; - - var url = string.Format( - CultureInfo.InvariantCulture, - "{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}", - state.Request.MediaSourceId, - stream.Index.ToString(CultureInfo.InvariantCulture), - 30.ToString(CultureInfo.InvariantCulture), - ClaimHelpers.GetToken(Request.HttpContext.User)); - - var line = string.Format( - CultureInfo.InvariantCulture, - Format, - name, - isDefault ? "YES" : "NO", - isForced ? "YES" : "NO", - url, - stream.Language ?? "Unknown"); - - builder.AppendLine(line); - } - } - - private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string? subtitleGroup) - { - builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") - .Append(bitrate.ToString(CultureInfo.InvariantCulture)) - .Append(",AVERAGE-BANDWIDTH=") - .Append(bitrate.ToString(CultureInfo.InvariantCulture)); - - AppendPlaylistCodecsField(builder, state); - - AppendPlaylistResolutionField(builder, state); - - AppendPlaylistFramerateField(builder, state); - - if (!string.IsNullOrWhiteSpace(subtitleGroup)) - { - builder.Append(",SUBTITLES=\"") - .Append(subtitleGroup) - .Append('"'); - } - - builder.Append(Environment.NewLine); - builder.AppendLine(url); - } - - /// - /// Appends a CODECS field containing formatted strings of - /// the active streams output video and audio codecs. - /// - /// - /// - /// - /// StringBuilder to append the field to. - /// StreamState of the current stream. - private void AppendPlaylistCodecsField(StringBuilder builder, StreamState state) - { - // Video - string videoCodecs = string.Empty; - int? videoCodecLevel = GetOutputVideoCodecLevel(state); - if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec) && videoCodecLevel.HasValue) - { - videoCodecs = GetPlaylistVideoCodecs(state, state.ActualOutputVideoCodec, videoCodecLevel.Value); - } - - // Audio - string audioCodecs = string.Empty; - if (!string.IsNullOrEmpty(state.ActualOutputAudioCodec)) - { - audioCodecs = GetPlaylistAudioCodecs(state); - } - - StringBuilder codecs = new StringBuilder(); - - codecs.Append(videoCodecs); - - if (!string.IsNullOrEmpty(videoCodecs) && !string.IsNullOrEmpty(audioCodecs)) - { - codecs.Append(','); - } - - codecs.Append(audioCodecs); - - if (codecs.Length > 1) - { - builder.Append(",CODECS=\"") - .Append(codecs) - .Append('"'); - } - } - - /// - /// Appends a RESOLUTION field containing the resolution of the output stream. - /// - /// - /// StringBuilder to append the field to. - /// StreamState of the current stream. - private void AppendPlaylistResolutionField(StringBuilder builder, StreamState state) - { - if (state.OutputWidth.HasValue && state.OutputHeight.HasValue) - { - builder.Append(",RESOLUTION=") - .Append(state.OutputWidth.GetValueOrDefault()) - .Append('x') - .Append(state.OutputHeight.GetValueOrDefault()); - } - } - - /// - /// Appends a FRAME-RATE field containing the framerate of the output stream. - /// - /// - /// StringBuilder to append the field to. - /// StreamState of the current stream. - private void AppendPlaylistFramerateField(StringBuilder builder, StreamState state) - { - double? framerate = null; - if (state.TargetFramerate.HasValue) - { - framerate = Math.Round(state.TargetFramerate.GetValueOrDefault(), 3); - } - else if (state.VideoStream?.RealFrameRate != null) - { - framerate = Math.Round(state.VideoStream.RealFrameRate.GetValueOrDefault(), 3); - } - - if (framerate.HasValue) - { - builder.Append(",FRAME-RATE=") - .Append(framerate.Value); - } - } - - private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming) - { - // Within the local network this will likely do more harm than good. - var ip = RequestHelpers.NormalizeIp(Request.HttpContext.Connection.RemoteIpAddress).ToString(); - if (_networkManager.IsInLocalNetwork(ip)) - { - return false; - } - - if (!enableAdaptiveBitrateStreaming) - { - return false; - } - - if (isLiveStream || string.IsNullOrWhiteSpace(state.MediaPath)) - { - // Opening live streams is so slow it's not even worth it - return false; - } - - if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) - { - return false; - } - - if (EncodingHelper.IsCopyCodec(state.OutputAudioCodec)) - { - return false; - } - - if (!state.IsOutputVideo) - { - return false; - } - - // Having problems in android - return false; - // return state.VideoRequest.VideoBitRate.HasValue; - } - - /// - /// Get the H.26X level of the output video stream. - /// - /// StreamState of the current stream. - /// H.26X level of the output video stream. - private int? GetOutputVideoCodecLevel(StreamState state) - { - string? levelString; - if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) - && state.VideoStream.Level.HasValue) - { - levelString = state.VideoStream?.Level.ToString(); - } - else - { - levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec); - } - - if (int.TryParse(levelString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedLevel)) - { - return parsedLevel; - } - - return null; - } - - /// - /// Gets a formatted string of the output audio codec, for use in the CODECS field. - /// - /// - /// - /// StreamState of the current stream. - /// Formatted audio codec string. - private string GetPlaylistAudioCodecs(StreamState state) - { - if (string.Equals(state.ActualOutputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase)) - { - string? profile = state.GetRequestedProfiles("aac").FirstOrDefault(); - return HlsCodecStringHelpers.GetAACString(profile); - } - - if (string.Equals(state.ActualOutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) - { - return HlsCodecStringHelpers.GetMP3String(); - } - - if (string.Equals(state.ActualOutputAudioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) - { - return HlsCodecStringHelpers.GetAC3String(); - } - - if (string.Equals(state.ActualOutputAudioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) - { - return HlsCodecStringHelpers.GetEAC3String(); - } - - return string.Empty; - } - - /// - /// Gets a formatted string of the output video codec, for use in the CODECS field. - /// - /// - /// - /// StreamState of the current stream. - /// Video codec. - /// Video level. - /// Formatted video codec string. - private string GetPlaylistVideoCodecs(StreamState state, string codec, int level) - { - if (level == 0) - { - // This is 0 when there's no requested H.26X level in the device profile - // and the source is not encoded in H.26X - _logger.LogError("Got invalid H.26X level when building CODECS field for HLS master playlist"); - return string.Empty; - } - - if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase)) - { - string profile = state.GetRequestedProfiles("h264").FirstOrDefault(); - return HlsCodecStringHelpers.GetH264String(profile, level); - } - - if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) - { - string profile = state.GetRequestedProfiles("h265").FirstOrDefault(); - - return HlsCodecStringHelpers.GetH265String(profile, level); - } - - return string.Empty; - } - - private int GetBitrateVariation(int bitrate) - { - // By default, vary by just 50k - var variation = 50000; - - if (bitrate >= 10000000) - { - variation = 2000000; - } - else if (bitrate >= 5000000) - { - variation = 1500000; - } - else if (bitrate >= 3000000) - { - variation = 1000000; - } - else if (bitrate >= 2000000) - { - variation = 500000; - } - else if (bitrate >= 1000000) - { - variation = 300000; - } - else if (bitrate >= 600000) - { - variation = 200000; - } - else if (bitrate >= 400000) - { - variation = 100000; - } - - return variation; - } - - private string ReplaceBitrate(string url, int oldValue, int newValue) - { - return url.Replace( - "videobitrate=" + oldValue.ToString(CultureInfo.InvariantCulture), - "videobitrate=" + newValue.ToString(CultureInfo.InvariantCulture), - StringComparison.OrdinalIgnoreCase); - } - private double[] GetSegmentLengths(StreamState state) { var result = new List(); diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index 517113074c..7faf479a5c 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -1,30 +1,18 @@ using System; using System.Buffers; using System.ComponentModel.DataAnnotations; -using System.Globalization; using System.Linq; using System.Net.Mime; -using System.Text.Json; -using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.MediaInfoDtos; using Jellyfin.Api.Models.VideoDtos; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Session; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -42,12 +30,9 @@ namespace Jellyfin.Api.Controllers private readonly IMediaSourceManager _mediaSourceManager; private readonly IDeviceManager _deviceManager; private readonly ILibraryManager _libraryManager; - private readonly INetworkManager _networkManager; - private readonly IMediaEncoder _mediaEncoder; - private readonly IUserManager _userManager; private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; - private readonly IServerConfigurationManager _serverConfigurationManager; + private readonly MediaInfoHelper _mediaInfoHelper; /// /// Initializes a new instance of the class. @@ -55,32 +40,23 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. + /// Instance of the . public MediaInfoController( IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, - INetworkManager networkManager, - IMediaEncoder mediaEncoder, - IUserManager userManager, IAuthorizationContext authContext, ILogger logger, - IServerConfigurationManager serverConfigurationManager) + MediaInfoHelper mediaInfoHelper) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; _libraryManager = libraryManager; - _networkManager = networkManager; - _mediaEncoder = mediaEncoder; - _userManager = userManager; _authContext = authContext; _logger = logger; - _serverConfigurationManager = serverConfigurationManager; + _mediaInfoHelper = mediaInfoHelper; } /// @@ -94,7 +70,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPlaybackInfo([FromRoute] Guid itemId, [FromQuery, Required] Guid? userId) { - return await GetPlaybackInfoInternal(itemId, userId).ConfigureAwait(false); + return await _mediaInfoHelper.GetPlaybackInfo( + itemId, + userId) + .ConfigureAwait(false); } /// @@ -153,7 +132,12 @@ namespace Jellyfin.Api.Controllers } } - var info = await GetPlaybackInfoInternal(itemId, userId, mediaSourceId, liveStreamId).ConfigureAwait(false); + var info = await _mediaInfoHelper.GetPlaybackInfo( + itemId, + userId, + mediaSourceId, + liveStreamId) + .ConfigureAwait(false); if (profile != null) { @@ -162,7 +146,7 @@ namespace Jellyfin.Api.Controllers foreach (var mediaSource in info.MediaSources) { - SetDeviceSpecificData( + _mediaInfoHelper.SetDeviceSpecificData( item, mediaSource, profile, @@ -179,10 +163,11 @@ namespace Jellyfin.Api.Controllers enableDirectStream, enableTranscoding, allowVideoStreamCopy, - allowAudioStreamCopy); + allowAudioStreamCopy, + Request.HttpContext.Connection.RemoteIpAddress.ToString()); } - SortMediaSources(info, maxStreamingBitrate); + _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate); } if (autoOpenLiveStream) @@ -191,21 +176,23 @@ namespace Jellyfin.Api.Controllers if (mediaSource != null && mediaSource.RequiresOpening && string.IsNullOrWhiteSpace(mediaSource.LiveStreamId)) { - var openStreamResult = await OpenMediaSource(new LiveStreamRequest - { - AudioStreamIndex = audioStreamIndex, - DeviceProfile = deviceProfile?.DeviceProfile, - EnableDirectPlay = enableDirectPlay, - EnableDirectStream = enableDirectStream, - ItemId = itemId, - MaxAudioChannels = maxAudioChannels, - MaxStreamingBitrate = maxStreamingBitrate, - PlaySessionId = info.PlaySessionId, - StartTimeTicks = startTimeTicks, - SubtitleStreamIndex = subtitleStreamIndex, - UserId = userId ?? Guid.Empty, - OpenToken = mediaSource.OpenToken - }).ConfigureAwait(false); + var openStreamResult = await _mediaInfoHelper.OpenMediaSource( + Request, + new LiveStreamRequest + { + AudioStreamIndex = audioStreamIndex, + DeviceProfile = deviceProfile?.DeviceProfile, + EnableDirectPlay = enableDirectPlay, + EnableDirectStream = enableDirectStream, + ItemId = itemId, + MaxAudioChannels = maxAudioChannels, + MaxStreamingBitrate = maxStreamingBitrate, + PlaySessionId = info.PlaySessionId, + StartTimeTicks = startTimeTicks, + SubtitleStreamIndex = subtitleStreamIndex, + UserId = userId ?? Guid.Empty, + OpenToken = mediaSource.OpenToken + }).ConfigureAwait(false); info.MediaSources = new[] { openStreamResult.MediaSource }; } @@ -215,7 +202,7 @@ namespace Jellyfin.Api.Controllers { foreach (var mediaSource in info.MediaSources) { - NormalizeMediaSourceContainer(mediaSource, profile!, DlnaProfileType.Video); + _mediaInfoHelper.NormalizeMediaSourceContainer(mediaSource, profile!, DlnaProfileType.Video); } } @@ -271,7 +258,7 @@ namespace Jellyfin.Api.Controllers EnableDirectStream = enableDirectStream, DirectPlayProtocols = openLiveStreamDto?.DirectPlayProtocols ?? new[] { MediaProtocol.Http } }; - return await OpenMediaSource(request).ConfigureAwait(false); + return await _mediaInfoHelper.OpenMediaSource(Request, request).ConfigureAwait(false); } /// @@ -324,454 +311,5 @@ namespace Jellyfin.Api.Controllers ArrayPool.Shared.Return(buffer); } } - - private async Task GetPlaybackInfoInternal( - Guid id, - Guid? userId, - string? mediaSourceId = null, - string? liveStreamId = null) - { - var user = userId.HasValue && !userId.Equals(Guid.Empty) - ? _userManager.GetUserById(userId.Value) - : null; - var item = _libraryManager.GetItemById(id); - var result = new PlaybackInfoResponse(); - - MediaSourceInfo[] mediaSources; - if (string.IsNullOrWhiteSpace(liveStreamId)) - { - // TODO (moved from MediaBrowser.Api) handle supportedLiveMediaTypes? - var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, true, CancellationToken.None).ConfigureAwait(false); - - if (string.IsNullOrWhiteSpace(mediaSourceId)) - { - mediaSources = mediaSourcesList.ToArray(); - } - else - { - mediaSources = mediaSourcesList - .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) - .ToArray(); - } - } - else - { - var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); - - mediaSources = new[] { mediaSource }; - } - - if (mediaSources.Length == 0) - { - result.MediaSources = Array.Empty(); - - result.ErrorCode ??= PlaybackErrorCode.NoCompatibleStream; - } - else - { - // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it - // Should we move this directly into MediaSourceManager? - result.MediaSources = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(mediaSources)); - - result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); - } - - return result; - } - - private void NormalizeMediaSourceContainer(MediaSourceInfo mediaSource, DeviceProfile profile, DlnaProfileType type) - { - mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, mediaSource.Path, profile, type); - } - - private void SetDeviceSpecificData( - BaseItem item, - MediaSourceInfo mediaSource, - DeviceProfile profile, - AuthorizationInfo auth, - long? maxBitrate, - long startTimeTicks, - string mediaSourceId, - int? audioStreamIndex, - int? subtitleStreamIndex, - int? maxAudioChannels, - string playSessionId, - Guid userId, - bool enableDirectPlay, - bool enableDirectStream, - bool enableTranscoding, - bool allowVideoStreamCopy, - bool allowAudioStreamCopy) - { - var streamBuilder = new StreamBuilder(_mediaEncoder, _logger); - - var options = new VideoOptions - { - MediaSources = new[] { mediaSource }, - Context = EncodingContext.Streaming, - DeviceId = auth.DeviceId, - ItemId = item.Id, - Profile = profile, - MaxAudioChannels = maxAudioChannels - }; - - if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase)) - { - options.MediaSourceId = mediaSourceId; - options.AudioStreamIndex = audioStreamIndex; - options.SubtitleStreamIndex = subtitleStreamIndex; - } - - var user = _userManager.GetUserById(userId); - - if (!enableDirectPlay) - { - mediaSource.SupportsDirectPlay = false; - } - - if (!enableDirectStream) - { - mediaSource.SupportsDirectStream = false; - } - - if (!enableTranscoding) - { - mediaSource.SupportsTranscoding = false; - } - - if (item is Audio) - { - _logger.LogInformation( - "User policy for {0}. EnableAudioPlaybackTranscoding: {1}", - user.Username, - user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)); - } - else - { - _logger.LogInformation( - "User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}", - user.Username, - user.HasPermission(PermissionKind.EnablePlaybackRemuxing), - user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding), - user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)); - } - - // Beginning of Playback Determination: Attempt DirectPlay first - if (mediaSource.SupportsDirectPlay) - { - if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) - { - mediaSource.SupportsDirectPlay = false; - } - else - { - var supportsDirectStream = mediaSource.SupportsDirectStream; - - // Dummy this up to fool StreamBuilder - mediaSource.SupportsDirectStream = true; - options.MaxBitrate = maxBitrate; - - if (item is Audio) - { - if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)) - { - options.ForceDirectPlay = true; - } - } - else if (item is Video) - { - if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding) - && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding) - && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) - { - options.ForceDirectPlay = true; - } - } - - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) - ? streamBuilder.BuildAudioItem(options) - : streamBuilder.BuildVideoItem(options); - - if (streamInfo == null || !streamInfo.IsDirectStream) - { - mediaSource.SupportsDirectPlay = false; - } - - // Set this back to what it was - mediaSource.SupportsDirectStream = supportsDirectStream; - - if (streamInfo != null) - { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } - } - } - - if (mediaSource.SupportsDirectStream) - { - if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) - { - mediaSource.SupportsDirectStream = false; - } - else - { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - - if (item is Audio) - { - if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)) - { - options.ForceDirectStream = true; - } - } - else if (item is Video) - { - if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding) - && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding) - && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) - { - options.ForceDirectStream = true; - } - } - - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) - ? streamBuilder.BuildAudioItem(options) - : streamBuilder.BuildVideoItem(options); - - if (streamInfo == null || !streamInfo.IsDirectStream) - { - mediaSource.SupportsDirectStream = false; - } - - if (streamInfo != null) - { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } - } - } - - if (mediaSource.SupportsTranscoding) - { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) - ? streamBuilder.BuildAudioItem(options) - : streamBuilder.BuildVideoItem(options); - - if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) - { - if (streamInfo != null) - { - streamInfo.PlaySessionId = playSessionId; - streamInfo.StartPositionTicks = startTimeTicks; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); - mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; - mediaSource.TranscodingContainer = streamInfo.Container; - mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - - // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } - } - else - { - if (streamInfo != null) - { - streamInfo.PlaySessionId = playSessionId; - - if (streamInfo.PlayMethod == PlayMethod.Transcode) - { - streamInfo.StartPositionTicks = startTimeTicks; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); - - if (!allowVideoStreamCopy) - { - mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - } - - if (!allowAudioStreamCopy) - { - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; - } - - mediaSource.TranscodingContainer = streamInfo.Container; - mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - } - - if (!allowAudioStreamCopy) - { - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; - } - - mediaSource.TranscodingContainer = streamInfo.Container; - mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - - // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } - } - } - - foreach (var attachment in mediaSource.MediaAttachments) - { - attachment.DeliveryUrl = string.Format( - CultureInfo.InvariantCulture, - "/Videos/{0}/{1}/Attachments/{2}", - item.Id, - mediaSource.Id, - attachment.Index); - } - } - - private async Task OpenMediaSource(LiveStreamRequest request) - { - var authInfo = _authContext.GetAuthorizationInfo(Request); - - var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false); - - var profile = request.DeviceProfile; - if (profile == null) - { - var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); - if (caps != null) - { - profile = caps.DeviceProfile; - } - } - - if (profile != null) - { - var item = _libraryManager.GetItemById(request.ItemId); - - SetDeviceSpecificData( - item, - result.MediaSource, - profile, - authInfo, - request.MaxStreamingBitrate, - request.StartTimeTicks ?? 0, - result.MediaSource.Id, - request.AudioStreamIndex, - request.SubtitleStreamIndex, - request.MaxAudioChannels, - request.PlaySessionId, - request.UserId, - request.EnableDirectPlay, - request.EnableDirectStream, - true, - true, - true); - } - else - { - if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl)) - { - result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId; - } - } - - // here was a check if (result.MediaSource != null) but Rider said it will never be null - NormalizeMediaSourceContainer(result.MediaSource, profile!, DlnaProfileType.Video); - - return result; - } - - private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken) - { - var profiles = info.GetSubtitleProfiles(_mediaEncoder, false, "-", accessToken); - mediaSource.DefaultSubtitleStreamIndex = info.SubtitleStreamIndex; - - mediaSource.TranscodeReasons = info.TranscodeReasons; - - foreach (var profile in profiles) - { - foreach (var stream in mediaSource.MediaStreams) - { - if (stream.Type == MediaStreamType.Subtitle && stream.Index == profile.Index) - { - stream.DeliveryMethod = profile.DeliveryMethod; - - if (profile.DeliveryMethod == SubtitleDeliveryMethod.External) - { - stream.DeliveryUrl = profile.Url.TrimStart('-'); - stream.IsExternalUrl = profile.IsExternalUrl; - } - } - } - } - } - - private long? GetMaxBitrate(long? clientMaxBitrate, User user) - { - var maxBitrate = clientMaxBitrate; - var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0; - - if (remoteClientMaxBitrate <= 0) - { - remoteClientMaxBitrate = _serverConfigurationManager.Configuration.RemoteClientBitrateLimit; - } - - if (remoteClientMaxBitrate > 0) - { - var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.HttpContext.Connection.RemoteIpAddress.ToString()); - - _logger.LogInformation("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.HttpContext.Connection.RemoteIpAddress.ToString(), isInLocalNetwork); - if (!isInLocalNetwork) - { - maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate); - } - } - - return maxBitrate; - } - - private void SortMediaSources(PlaybackInfoResponse result, long? maxBitrate) - { - var originalList = result.MediaSources.ToList(); - - result.MediaSources = result.MediaSources.OrderBy(i => - { - // Nothing beats direct playing a file - if (i.SupportsDirectPlay && i.Protocol == MediaProtocol.File) - { - return 0; - } - - return 1; - }) - .ThenBy(i => - { - // Let's assume direct streaming a file is just as desirable as direct playing a remote url - if (i.SupportsDirectPlay || i.SupportsDirectStream) - { - return 0; - } - - return 1; - }) - .ThenBy(i => - { - return i.Protocol switch - { - MediaProtocol.File => 0, - _ => 1, - }; - }) - .ThenBy(i => - { - if (maxBitrate.HasValue && i.Bitrate.HasValue) - { - return i.Bitrate.Value <= maxBitrate.Value ? 0 : 2; - } - - return 1; - }) - .ThenBy(originalList.IndexOf) - .ToArray(); - } } } diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 75df16aa7b..2ea4624881 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -2,17 +2,20 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; -using Jellyfin.Api.Models.VideoDtos; +using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.MediaInfo; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Controllers { @@ -23,27 +26,39 @@ namespace Jellyfin.Api.Controllers public class UniversalAudioController : BaseJellyfinApiController { private readonly IAuthorizationContext _authorizationContext; - private readonly MediaInfoController _mediaInfoController; - private readonly DynamicHlsController _dynamicHlsController; - private readonly AudioController _audioController; + private readonly IDeviceManager _deviceManager; + private readonly ILibraryManager _libraryManager; + private readonly ILogger _logger; + private readonly MediaInfoHelper _mediaInfoHelper; + private readonly AudioHelper _audioHelper; + private readonly DynamicHlsHelper _dynamicHlsHelper; /// /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the . - /// Instance of the . - /// Instance of the . + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of . + /// Instance of . + /// Instance of . public UniversalAudioController( IAuthorizationContext authorizationContext, - MediaInfoController mediaInfoController, - DynamicHlsController dynamicHlsController, - AudioController audioController) + IDeviceManager deviceManager, + ILibraryManager libraryManager, + ILogger logger, + MediaInfoHelper mediaInfoHelper, + AudioHelper audioHelper, + DynamicHlsHelper dynamicHlsHelper) { _authorizationContext = authorizationContext; - _mediaInfoController = mediaInfoController; - _dynamicHlsController = dynamicHlsController; - _audioController = audioController; + _deviceManager = deviceManager; + _libraryManager = libraryManager; + _logger = logger; + _mediaInfoHelper = mediaInfoHelper; + _audioHelper = audioHelper; + _dynamicHlsHelper = dynamicHlsHelper; } /// @@ -99,20 +114,65 @@ namespace Jellyfin.Api.Controllers var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); _authorizationContext.GetAuthorizationInfo(Request).DeviceId = deviceId; - var playbackInfoResult = await _mediaInfoController.GetPostedPlaybackInfo( - itemId, - userId, - maxStreamingBitrate, - startTimeTicks, - null, - null, - maxAudioChannels, - mediaSourceId, - null, - new DeviceProfileDto { DeviceProfile = deviceProfile }) + var authInfo = _authorizationContext.GetAuthorizationInfo(Request); + + _logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", deviceProfile); + + if (deviceProfile == null) + { + var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (caps != null) + { + deviceProfile = caps.DeviceProfile; + } + } + + var info = await _mediaInfoHelper.GetPlaybackInfo( + itemId, + userId, + mediaSourceId) .ConfigureAwait(false); - var mediaSource = playbackInfoResult.Value.MediaSources[0]; + if (deviceProfile != null) + { + // set device specific data + var item = _libraryManager.GetItemById(itemId); + + foreach (var sourceInfo in info.MediaSources) + { + _mediaInfoHelper.SetDeviceSpecificData( + item, + sourceInfo, + deviceProfile, + authInfo, + maxStreamingBitrate ?? deviceProfile.MaxStreamingBitrate, + startTimeTicks ?? 0, + mediaSourceId ?? string.Empty, + null, + null, + maxAudioChannels, + info!.PlaySessionId!, + userId ?? Guid.Empty, + true, + true, + true, + true, + true, + Request.HttpContext.Connection.RemoteIpAddress.ToString()); + } + + _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate); + } + + if (info.MediaSources != null) + { + foreach (var source in info.MediaSources) + { + _mediaInfoHelper.NormalizeMediaSourceContainer(source, deviceProfile!, DlnaProfileType.Video); + } + } + + var mediaSource = info.MediaSources![0]; if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http) { if (enableRedirection) @@ -127,129 +187,71 @@ namespace Jellyfin.Api.Controllers var isStatic = mediaSource.SupportsDirectStream; if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) { - var transcodingProfile = deviceProfile.TranscodingProfiles[0]; - // hls segment container can only be mpegts or fmp4 per ffmpeg documentation // TODO: remove this when we switch back to the segment muxer var supportedHlsContainers = new[] { "mpegts", "fmp4" }; - if (isHeadRequest) + var dynamicHlsRequestDto = new HlsAudioRequestDto { - _dynamicHlsController.Request.Method = HttpMethod.Head.Method; - } - - return await _dynamicHlsController.GetMasterHlsAudioPlaylist( - itemId, - ".m3u8", - isStatic, - null, - null, - null, - playbackInfoResult.Value.PlaySessionId, + Id = itemId, + Container = ".m3u8", + Static = isStatic, + PlaySessionId = info.PlaySessionId, // fallback to mpegts if device reports some weird value unsupported by hls - Array.Exists(supportedHlsContainers, element => element == transcodingContainer) ? transcodingContainer : "mpegts", - null, - null, - mediaSource.Id, - deviceId, - transcodingProfile.AudioCodec, - null, - null, - null, - transcodingProfile.BreakOnNonKeyFrames, - maxAudioSampleRate, - maxAudioBitDepth, - null, - isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)), - maxAudioChannels, - null, - null, - null, - null, - null, - startTimeTicks, - null, - null, - null, - null, - SubtitleDeliveryMethod.Hls, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()), - null, - null, - EncodingContext.Static, - new Dictionary()) + SegmentContainer = Array.Exists(supportedHlsContainers, element => element == transcodingContainer) ? transcodingContainer : "mpegts", + MediaSourceId = mediaSourceId, + DeviceId = deviceId, + AudioCodec = audioCodec, + EnableAutoStreamCopy = true, + AllowAudioStreamCopy = true, + AllowVideoStreamCopy = true, + BreakOnNonKeyFrames = breakOnNonKeyFrames, + AudioSampleRate = maxAudioSampleRate, + MaxAudioChannels = maxAudioChannels, + MaxAudioBitDepth = maxAudioBitDepth, + AudioChannels = isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)), + StartTimeTicks = startTimeTicks, + SubtitleMethod = SubtitleDeliveryMethod.Hls, + RequireAvc = true, + DeInterlace = true, + RequireNonAnamorphic = true, + EnableMpegtsM2TsMode = true, + TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()), + Context = EncodingContext.Static, + StreamOptions = new Dictionary(), + EnableAdaptiveBitrateStreaming = true + }; + + return await _dynamicHlsHelper.GetMasterHlsPlaylist(this, TranscodingJobType.Hls, dynamicHlsRequestDto, true) .ConfigureAwait(false); } - else + + var audioStreamingDto = new StreamingRequestDto { - if (isHeadRequest) - { - _audioController.Request.Method = HttpMethod.Head.Method; - } + Id = itemId, + Container = isStatic ? null : ("." + mediaSource.TranscodingContainer), + Static = isStatic, + PlaySessionId = info.PlaySessionId, + MediaSourceId = mediaSourceId, + DeviceId = deviceId, + AudioCodec = audioCodec, + EnableAutoStreamCopy = true, + AllowAudioStreamCopy = true, + AllowVideoStreamCopy = true, + BreakOnNonKeyFrames = breakOnNonKeyFrames, + AudioSampleRate = maxAudioSampleRate, + MaxAudioChannels = maxAudioChannels, + AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)), + MaxAudioBitDepth = maxAudioBitDepth, + AudioChannels = maxAudioChannels, + CopyTimestamps = true, + StartTimeTicks = startTimeTicks, + SubtitleMethod = SubtitleDeliveryMethod.Embed, + TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()), + Context = EncodingContext.Static + }; - return await _audioController.GetAudioStream( - itemId, - isStatic ? null : ("." + mediaSource.TranscodingContainer), - isStatic, - null, - null, - null, - playbackInfoResult.Value.PlaySessionId, - null, - null, - null, - mediaSource.Id, - deviceId, - audioCodec, - null, - null, - null, - breakOnNonKeyFrames, - maxAudioSampleRate, - maxAudioBitDepth, - isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)), - null, - maxAudioChannels, - null, - null, - null, - null, - null, - startTimeTicks, - null, - null, - null, - null, - SubtitleDeliveryMethod.Embed, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()), - null, - null, - null, - null) - .ConfigureAwait(false); - } + return await _audioHelper.GetAudioStream(this, TranscodingJobType.Progressive, audioStreamingDto).ConfigureAwait(false); } private DeviceProfile GetDeviceProfile( diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs new file mode 100644 index 0000000000..0010284322 --- /dev/null +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -0,0 +1,193 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Net; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +namespace Jellyfin.Api.Helpers +{ + /// + /// Audio helper. + /// + public class AudioHelper + { + private readonly IDlnaManager _dlnaManager; + private readonly IAuthorizationContext _authContext; + private readonly IUserManager _userManager; + private readonly ILibraryManager _libraryManager; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly IServerConfigurationManager _serverConfigurationManager; + private readonly IMediaEncoder _mediaEncoder; + private readonly IFileSystem _fileSystem; + private readonly ISubtitleEncoder _subtitleEncoder; + private readonly IConfiguration _configuration; + private readonly IDeviceManager _deviceManager; + private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly IHttpClientFactory _httpClientFactory; + + /// + /// 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. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of . + /// Instance of the interface. + public AudioHelper( + IDlnaManager dlnaManager, + IAuthorizationContext authContext, + IUserManager userManager, + ILibraryManager libraryManager, + IMediaSourceManager mediaSourceManager, + IServerConfigurationManager serverConfigurationManager, + IMediaEncoder mediaEncoder, + IFileSystem fileSystem, + ISubtitleEncoder subtitleEncoder, + IConfiguration configuration, + IDeviceManager deviceManager, + TranscodingJobHelper transcodingJobHelper, + IHttpClientFactory httpClientFactory) + { + _dlnaManager = dlnaManager; + _authContext = authContext; + _userManager = userManager; + _libraryManager = libraryManager; + _mediaSourceManager = mediaSourceManager; + _serverConfigurationManager = serverConfigurationManager; + _mediaEncoder = mediaEncoder; + _fileSystem = fileSystem; + _subtitleEncoder = subtitleEncoder; + _configuration = configuration; + _deviceManager = deviceManager; + _transcodingJobHelper = transcodingJobHelper; + _httpClientFactory = httpClientFactory; + } + + /// + /// Get audio stream. + /// + /// Requesting controller. + /// Transcoding job type. + /// Streaming controller.Request dto. + /// A containing the resulting . + public async Task GetAudioStream( + BaseJellyfinApiController controller, + TranscodingJobType transcodingJobType, + StreamingRequestDto streamingRequest) + { + bool isHeadRequest = controller.Request.Method == System.Net.WebRequestMethods.Http.Head; + var cancellationTokenSource = new CancellationTokenSource(); + + using var state = await StreamingHelpers.GetStreamingState( + streamingRequest, + controller.Request, + _authContext, + _mediaSourceManager, + _userManager, + _libraryManager, + _serverConfigurationManager, + _mediaEncoder, + _fileSystem, + _subtitleEncoder, + _configuration, + _dlnaManager, + _deviceManager, + _transcodingJobHelper, + transcodingJobType, + cancellationTokenSource.Token) + .ConfigureAwait(false); + + if (streamingRequest.Static && state.DirectStreamProvider != null) + { + StreamingHelpers.AddDlnaHeaders(state, controller.Response.Headers, true, streamingRequest.StartTimeTicks, controller.Request, _dlnaManager); + + await new ProgressiveFileCopier(state.DirectStreamProvider, null, _transcodingJobHelper, CancellationToken.None) + { + AllowEndOfFile = false + }.WriteToAsync(controller.Response.Body, CancellationToken.None) + .ConfigureAwait(false); + + // TODO (moved from MediaBrowser.Api): Don't hardcode contentType + return controller.File(controller.Response.Body, MimeTypes.GetMimeType("file.ts")!); + } + + // Static remote stream + if (streamingRequest.Static && state.InputProtocol == MediaProtocol.Http) + { + StreamingHelpers.AddDlnaHeaders(state, controller.Response.Headers, true, streamingRequest.StartTimeTicks, controller.Request, _dlnaManager); + + using var httpClient = _httpClientFactory.CreateClient(); + return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, controller, httpClient).ConfigureAwait(false); + } + + if (streamingRequest.Static && state.InputProtocol != MediaProtocol.File) + { + return controller.BadRequest($"Input protocol {state.InputProtocol} cannot be streamed statically"); + } + + var outputPath = state.OutputFilePath; + var outputPathExists = System.IO.File.Exists(outputPath); + + var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive); + var isTranscodeCached = outputPathExists && transcodingJob != null; + + StreamingHelpers.AddDlnaHeaders(state, controller.Response.Headers, streamingRequest.Static || isTranscodeCached, streamingRequest.StartTimeTicks, controller.Request, _dlnaManager); + + // Static stream + if (streamingRequest.Static) + { + var contentType = state.GetMimeType("." + state.OutputContainer, false) ?? state.GetMimeType(state.MediaPath); + + if (state.MediaSource.IsInfiniteStream) + { + await new ProgressiveFileCopier(state.MediaPath, null, _transcodingJobHelper, CancellationToken.None) + { + AllowEndOfFile = false + }.WriteToAsync(controller.Response.Body, CancellationToken.None) + .ConfigureAwait(false); + + return controller.File(controller.Response.Body, contentType); + } + + return FileStreamResponseHelpers.GetStaticFileResult( + state.MediaPath, + contentType, + isHeadRequest, + controller); + } + + // Need to start ffmpeg (because media can't be returned directly) + var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); + var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); + var ffmpegCommandLineArguments = encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath); + return await FileStreamResponseHelpers.GetTranscodedFile( + state, + isHeadRequest, + controller, + _transcodingJobHelper, + ffmpegCommandLineArguments, + controller.Request, + transcodingJobType, + cancellationTokenSource).ConfigureAwait(false); + } + } +} diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs new file mode 100644 index 0000000000..127d914306 --- /dev/null +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -0,0 +1,549 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Security.Claims; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; + +namespace Jellyfin.Api.Helpers +{ + /// + /// Dynamic hls helper. + /// + public class DynamicHlsHelper + { + private readonly ILibraryManager _libraryManager; + private readonly IUserManager _userManager; + private readonly IDlnaManager _dlnaManager; + private readonly IAuthorizationContext _authContext; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly IServerConfigurationManager _serverConfigurationManager; + private readonly IMediaEncoder _mediaEncoder; + private readonly IFileSystem _fileSystem; + private readonly ISubtitleEncoder _subtitleEncoder; + private readonly IConfiguration _configuration; + private readonly IDeviceManager _deviceManager; + private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly INetworkManager _networkManager; + private readonly ILogger _logger; + + /// + /// 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. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of . + /// Instance of the interface. + /// Instance of the interface. + public DynamicHlsHelper( + ILibraryManager libraryManager, + IUserManager userManager, + IDlnaManager dlnaManager, + IAuthorizationContext authContext, + IMediaSourceManager mediaSourceManager, + IServerConfigurationManager serverConfigurationManager, + IMediaEncoder mediaEncoder, + IFileSystem fileSystem, + ISubtitleEncoder subtitleEncoder, + IConfiguration configuration, + IDeviceManager deviceManager, + TranscodingJobHelper transcodingJobHelper, + INetworkManager networkManager, + ILogger logger) + { + _libraryManager = libraryManager; + _userManager = userManager; + _dlnaManager = dlnaManager; + _authContext = authContext; + _mediaSourceManager = mediaSourceManager; + _serverConfigurationManager = serverConfigurationManager; + _mediaEncoder = mediaEncoder; + _fileSystem = fileSystem; + _subtitleEncoder = subtitleEncoder; + _configuration = configuration; + _deviceManager = deviceManager; + _transcodingJobHelper = transcodingJobHelper; + _networkManager = networkManager; + _logger = logger; + } + + /// + /// Get master hls playlist. + /// + /// Requesting controller. + /// Transcoding job type. + /// Streaming request dto. + /// Enable adaptive bitrate streaming. + /// A containing the resulting . + public async Task GetMasterHlsPlaylist( + BaseJellyfinApiController controller, + TranscodingJobType transcodingJobType, + StreamingRequestDto streamingRequest, + bool enableAdaptiveBitrateStreaming) + { + var isHeadRequest = controller.Request.Method == WebRequestMethods.Http.Head; + var cancellationTokenSource = new CancellationTokenSource(); + return await GetMasterPlaylistInternal( + controller, + streamingRequest, + isHeadRequest, + enableAdaptiveBitrateStreaming, + transcodingJobType, + cancellationTokenSource).ConfigureAwait(false); + } + + private async Task GetMasterPlaylistInternal( + BaseJellyfinApiController controller, + StreamingRequestDto streamingRequest, + bool isHeadRequest, + bool enableAdaptiveBitrateStreaming, + TranscodingJobType transcodingJobType, + CancellationTokenSource cancellationTokenSource) + { + using var state = await StreamingHelpers.GetStreamingState( + streamingRequest, + controller.Request, + _authContext, + _mediaSourceManager, + _userManager, + _libraryManager, + _serverConfigurationManager, + _mediaEncoder, + _fileSystem, + _subtitleEncoder, + _configuration, + _dlnaManager, + _deviceManager, + _transcodingJobHelper, + transcodingJobType, + cancellationTokenSource.Token) + .ConfigureAwait(false); + + controller.Response.Headers.Add(HeaderNames.Expires, "0"); + if (isHeadRequest) + { + return new FileContentResult(Array.Empty(), MimeTypes.GetMimeType("playlist.m3u8")); + } + + var totalBitrate = state.OutputAudioBitrate ?? 0 + state.OutputVideoBitrate ?? 0; + + var builder = new StringBuilder(); + + builder.AppendLine("#EXTM3U"); + + var isLiveStream = state.IsSegmentedLiveStream; + + var queryString = controller.Request.QueryString.ToString(); + + // from universal audio service + if (queryString.IndexOf("SegmentContainer", StringComparison.OrdinalIgnoreCase) == -1 && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer)) + { + queryString += "&SegmentContainer=" + state.Request.SegmentContainer; + } + + // from universal audio service + if (!string.IsNullOrWhiteSpace(state.Request.TranscodeReasons) && queryString.IndexOf("TranscodeReasons=", StringComparison.OrdinalIgnoreCase) == -1) + { + queryString += "&TranscodeReasons=" + state.Request.TranscodeReasons; + } + + // Main stream + var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8"; + + playlistUrl += queryString; + + var subtitleStreams = state.MediaSource + .MediaStreams + .Where(i => i.IsTextSubtitleStream) + .ToList(); + + var subtitleGroup = subtitleStreams.Count > 0 && (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Hls || state.VideoRequest!.EnableSubtitlesInManifest) + ? "subs" + : null; + + // If we're burning in subtitles then don't add additional subs to the manifest + if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) + { + subtitleGroup = null; + } + + if (!string.IsNullOrWhiteSpace(subtitleGroup)) + { + AddSubtitles(state, subtitleStreams, builder, controller.Request.HttpContext.User); + } + + AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup); + + if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, controller.Request.HttpContext.Connection.RemoteIpAddress)) + { + var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0; + + // By default, vary by just 200k + var variation = GetBitrateVariation(totalBitrate); + + var newBitrate = totalBitrate - variation; + var variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); + AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup); + + variation *= 2; + newBitrate = totalBitrate - variation; + variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); + AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup); + } + + return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8")); + } + + private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string? subtitleGroup) + { + builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") + .Append(bitrate.ToString(CultureInfo.InvariantCulture)) + .Append(",AVERAGE-BANDWIDTH=") + .Append(bitrate.ToString(CultureInfo.InvariantCulture)); + + AppendPlaylistCodecsField(builder, state); + + AppendPlaylistResolutionField(builder, state); + + AppendPlaylistFramerateField(builder, state); + + if (!string.IsNullOrWhiteSpace(subtitleGroup)) + { + builder.Append(",SUBTITLES=\"") + .Append(subtitleGroup) + .Append('"'); + } + + builder.Append(Environment.NewLine); + builder.AppendLine(url); + } + + /// + /// Appends a CODECS field containing formatted strings of + /// the active streams output video and audio codecs. + /// + /// + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistCodecsField(StringBuilder builder, StreamState state) + { + // Video + string videoCodecs = string.Empty; + int? videoCodecLevel = GetOutputVideoCodecLevel(state); + if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec) && videoCodecLevel.HasValue) + { + videoCodecs = GetPlaylistVideoCodecs(state, state.ActualOutputVideoCodec, videoCodecLevel.Value); + } + + // Audio + string audioCodecs = string.Empty; + if (!string.IsNullOrEmpty(state.ActualOutputAudioCodec)) + { + audioCodecs = GetPlaylistAudioCodecs(state); + } + + StringBuilder codecs = new StringBuilder(); + + codecs.Append(videoCodecs); + + if (!string.IsNullOrEmpty(videoCodecs) && !string.IsNullOrEmpty(audioCodecs)) + { + codecs.Append(','); + } + + codecs.Append(audioCodecs); + + if (codecs.Length > 1) + { + builder.Append(",CODECS=\"") + .Append(codecs) + .Append('"'); + } + } + + /// + /// Appends a RESOLUTION field containing the resolution of the output stream. + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistResolutionField(StringBuilder builder, StreamState state) + { + if (state.OutputWidth.HasValue && state.OutputHeight.HasValue) + { + builder.Append(",RESOLUTION=") + .Append(state.OutputWidth.GetValueOrDefault()) + .Append('x') + .Append(state.OutputHeight.GetValueOrDefault()); + } + } + + /// + /// Appends a FRAME-RATE field containing the framerate of the output stream. + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistFramerateField(StringBuilder builder, StreamState state) + { + double? framerate = null; + if (state.TargetFramerate.HasValue) + { + framerate = Math.Round(state.TargetFramerate.GetValueOrDefault(), 3); + } + else if (state.VideoStream?.RealFrameRate != null) + { + framerate = Math.Round(state.VideoStream.RealFrameRate.GetValueOrDefault(), 3); + } + + if (framerate.HasValue) + { + builder.Append(",FRAME-RATE=") + .Append(framerate.Value); + } + } + + private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, IPAddress ipAddress) + { + // Within the local network this will likely do more harm than good. + var ip = RequestHelpers.NormalizeIp(ipAddress).ToString(); + if (_networkManager.IsInLocalNetwork(ip)) + { + return false; + } + + if (!enableAdaptiveBitrateStreaming) + { + return false; + } + + if (isLiveStream || string.IsNullOrWhiteSpace(state.MediaPath)) + { + // Opening live streams is so slow it's not even worth it + return false; + } + + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) + { + return false; + } + + if (EncodingHelper.IsCopyCodec(state.OutputAudioCodec)) + { + return false; + } + + if (!state.IsOutputVideo) + { + return false; + } + + // Having problems in android + return false; + // return state.VideoRequest.VideoBitRate.HasValue; + } + + private void AddSubtitles(StreamState state, IEnumerable subtitles, StringBuilder builder, ClaimsPrincipal user) + { + var selectedIndex = state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index; + const string Format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\""; + + foreach (var stream in subtitles) + { + var name = stream.DisplayTitle; + + var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index; + var isForced = stream.IsForced; + + var url = string.Format( + CultureInfo.InvariantCulture, + "{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}", + state.Request.MediaSourceId, + stream.Index.ToString(CultureInfo.InvariantCulture), + 30.ToString(CultureInfo.InvariantCulture), + ClaimHelpers.GetToken(user)); + + var line = string.Format( + CultureInfo.InvariantCulture, + Format, + name, + isDefault ? "YES" : "NO", + isForced ? "YES" : "NO", + url, + stream.Language ?? "Unknown"); + + builder.AppendLine(line); + } + } + + /// + /// Get the H.26X level of the output video stream. + /// + /// StreamState of the current stream. + /// H.26X level of the output video stream. + private int? GetOutputVideoCodecLevel(StreamState state) + { + string? levelString; + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) + && state.VideoStream.Level.HasValue) + { + levelString = state.VideoStream?.Level.ToString(); + } + else + { + levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec); + } + + if (int.TryParse(levelString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedLevel)) + { + return parsedLevel; + } + + return null; + } + + /// + /// Gets a formatted string of the output audio codec, for use in the CODECS field. + /// + /// + /// + /// StreamState of the current stream. + /// Formatted audio codec string. + private string GetPlaylistAudioCodecs(StreamState state) + { + if (string.Equals(state.ActualOutputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase)) + { + string? profile = state.GetRequestedProfiles("aac").FirstOrDefault(); + return HlsCodecStringHelpers.GetAACString(profile); + } + + if (string.Equals(state.ActualOutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringHelpers.GetMP3String(); + } + + if (string.Equals(state.ActualOutputAudioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringHelpers.GetAC3String(); + } + + if (string.Equals(state.ActualOutputAudioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringHelpers.GetEAC3String(); + } + + return string.Empty; + } + + /// + /// Gets a formatted string of the output video codec, for use in the CODECS field. + /// + /// + /// + /// StreamState of the current stream. + /// Video codec. + /// Video level. + /// Formatted video codec string. + private string GetPlaylistVideoCodecs(StreamState state, string codec, int level) + { + if (level == 0) + { + // This is 0 when there's no requested H.26X level in the device profile + // and the source is not encoded in H.26X + _logger.LogError("Got invalid H.26X level when building CODECS field for HLS master playlist"); + return string.Empty; + } + + if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase)) + { + string profile = state.GetRequestedProfiles("h264").FirstOrDefault(); + return HlsCodecStringHelpers.GetH264String(profile, level); + } + + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + string profile = state.GetRequestedProfiles("h265").FirstOrDefault(); + + return HlsCodecStringHelpers.GetH265String(profile, level); + } + + return string.Empty; + } + + private int GetBitrateVariation(int bitrate) + { + // By default, vary by just 50k + var variation = 50000; + + if (bitrate >= 10000000) + { + variation = 2000000; + } + else if (bitrate >= 5000000) + { + variation = 1500000; + } + else if (bitrate >= 3000000) + { + variation = 1000000; + } + else if (bitrate >= 2000000) + { + variation = 500000; + } + else if (bitrate >= 1000000) + { + variation = 300000; + } + else if (bitrate >= 600000) + { + variation = 200000; + } + else if (bitrate >= 400000) + { + variation = 100000; + } + + return variation; + } + + private string ReplaceBitrate(string url, int oldValue, int newValue) + { + return url.Replace( + "videobitrate=" + oldValue.ToString(CultureInfo.InvariantCulture), + "videobitrate=" + newValue.ToString(CultureInfo.InvariantCulture), + StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs new file mode 100644 index 0000000000..e94109c917 --- /dev/null +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -0,0 +1,573 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Session; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Api.Helpers +{ + /// + /// Media info helper. + /// + public class MediaInfoHelper + { + private readonly IUserManager _userManager; + private readonly ILibraryManager _libraryManager; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly IMediaEncoder _mediaEncoder; + private readonly IServerConfigurationManager _serverConfigurationManager; + private readonly ILogger _logger; + private readonly INetworkManager _networkManager; + private readonly IDeviceManager _deviceManager; + private readonly IAuthorizationContext _authContext; + + /// + /// 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. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public MediaInfoHelper( + IUserManager userManager, + ILibraryManager libraryManager, + IMediaSourceManager mediaSourceManager, + IMediaEncoder mediaEncoder, + IServerConfigurationManager serverConfigurationManager, + ILogger logger, + INetworkManager networkManager, + IDeviceManager deviceManager, + IAuthorizationContext authContext) + { + _userManager = userManager; + _libraryManager = libraryManager; + _mediaSourceManager = mediaSourceManager; + _mediaEncoder = mediaEncoder; + _serverConfigurationManager = serverConfigurationManager; + _logger = logger; + _networkManager = networkManager; + _deviceManager = deviceManager; + _authContext = authContext; + } + + /// + /// Get playback info. + /// + /// Item id. + /// User Id. + /// Media source id. + /// Live stream id. + /// A containing the . + public async Task GetPlaybackInfo( + Guid id, + Guid? userId, + string? mediaSourceId = null, + string? liveStreamId = null) + { + var user = userId.HasValue && !userId.Equals(Guid.Empty) + ? _userManager.GetUserById(userId.Value) + : null; + var item = _libraryManager.GetItemById(id); + var result = new PlaybackInfoResponse(); + + MediaSourceInfo[] mediaSources; + if (string.IsNullOrWhiteSpace(liveStreamId)) + { + // TODO (moved from MediaBrowser.Api) handle supportedLiveMediaTypes? + var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, true, CancellationToken.None).ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(mediaSourceId)) + { + mediaSources = mediaSourcesList.ToArray(); + } + else + { + mediaSources = mediaSourcesList + .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) + .ToArray(); + } + } + else + { + var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); + + mediaSources = new[] { mediaSource }; + } + + if (mediaSources.Length == 0) + { + result.MediaSources = Array.Empty(); + + result.ErrorCode ??= PlaybackErrorCode.NoCompatibleStream; + } + else + { + // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it + // Should we move this directly into MediaSourceManager? + result.MediaSources = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(mediaSources)); + + result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + } + + return result; + } + + /// + /// SetDeviceSpecificData. + /// + /// Item to set data for. + /// Media source info. + /// Device profile. + /// Authorization info. + /// Max bitrate. + /// Start time ticks. + /// Media source id. + /// Audio stream index. + /// Subtitle stream index. + /// Max audio channels. + /// Play session id. + /// User id. + /// Enable direct play. + /// Enable direct stream. + /// Enable transcoding. + /// Allow video stream copy. + /// Allow audio stream copy. + /// Requesting IP address. + public void SetDeviceSpecificData( + BaseItem item, + MediaSourceInfo mediaSource, + DeviceProfile profile, + AuthorizationInfo auth, + long? maxBitrate, + long startTimeTicks, + string mediaSourceId, + int? audioStreamIndex, + int? subtitleStreamIndex, + int? maxAudioChannels, + string playSessionId, + Guid userId, + bool enableDirectPlay, + bool enableDirectStream, + bool enableTranscoding, + bool allowVideoStreamCopy, + bool allowAudioStreamCopy, + string ipAddress) + { + var streamBuilder = new StreamBuilder(_mediaEncoder, _logger); + + var options = new VideoOptions + { + MediaSources = new[] { mediaSource }, + Context = EncodingContext.Streaming, + DeviceId = auth.DeviceId, + ItemId = item.Id, + Profile = profile, + MaxAudioChannels = maxAudioChannels + }; + + if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase)) + { + options.MediaSourceId = mediaSourceId; + options.AudioStreamIndex = audioStreamIndex; + options.SubtitleStreamIndex = subtitleStreamIndex; + } + + var user = _userManager.GetUserById(userId); + + if (!enableDirectPlay) + { + mediaSource.SupportsDirectPlay = false; + } + + if (!enableDirectStream) + { + mediaSource.SupportsDirectStream = false; + } + + if (!enableTranscoding) + { + mediaSource.SupportsTranscoding = false; + } + + if (item is Audio) + { + _logger.LogInformation( + "User policy for {0}. EnableAudioPlaybackTranscoding: {1}", + user.Username, + user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)); + } + else + { + _logger.LogInformation( + "User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}", + user.Username, + user.HasPermission(PermissionKind.EnablePlaybackRemuxing), + user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding), + user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)); + } + + // Beginning of Playback Determination: Attempt DirectPlay first + if (mediaSource.SupportsDirectPlay) + { + if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) + { + mediaSource.SupportsDirectPlay = false; + } + else + { + var supportsDirectStream = mediaSource.SupportsDirectStream; + + // Dummy this up to fool StreamBuilder + mediaSource.SupportsDirectStream = true; + options.MaxBitrate = maxBitrate; + + if (item is Audio) + { + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)) + { + options.ForceDirectPlay = true; + } + } + else if (item is Video) + { + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) + { + options.ForceDirectPlay = true; + } + } + + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); + + if (streamInfo == null || !streamInfo.IsDirectStream) + { + mediaSource.SupportsDirectPlay = false; + } + + // Set this back to what it was + mediaSource.SupportsDirectStream = supportsDirectStream; + + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + } + + if (mediaSource.SupportsDirectStream) + { + if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) + { + mediaSource.SupportsDirectStream = false; + } + else + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user, ipAddress); + + if (item is Audio) + { + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)) + { + options.ForceDirectStream = true; + } + } + else if (item is Video) + { + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) + { + options.ForceDirectStream = true; + } + } + + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); + + if (streamInfo == null || !streamInfo.IsDirectStream) + { + mediaSource.SupportsDirectStream = false; + } + + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + } + + if (mediaSource.SupportsTranscoding) + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user, ipAddress); + + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); + + if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) + { + if (streamInfo != null) + { + streamInfo.PlaySessionId = playSessionId; + streamInfo.StartPositionTicks = startTimeTicks; + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + mediaSource.TranscodingContainer = streamInfo.Container; + mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + + // Do this after the above so that StartPositionTicks is set + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + else + { + if (streamInfo != null) + { + streamInfo.PlaySessionId = playSessionId; + + if (streamInfo.PlayMethod == PlayMethod.Transcode) + { + streamInfo.StartPositionTicks = startTimeTicks; + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + + if (!allowVideoStreamCopy) + { + mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; + } + + if (!allowAudioStreamCopy) + { + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + } + + mediaSource.TranscodingContainer = streamInfo.Container; + mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + } + + if (!allowAudioStreamCopy) + { + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + } + + mediaSource.TranscodingContainer = streamInfo.Container; + mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + + // Do this after the above so that StartPositionTicks is set + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + } + + foreach (var attachment in mediaSource.MediaAttachments) + { + attachment.DeliveryUrl = string.Format( + CultureInfo.InvariantCulture, + "/Videos/{0}/{1}/Attachments/{2}", + item.Id, + mediaSource.Id, + attachment.Index); + } + } + + /// + /// Sort media source. + /// + /// Playback info response. + /// Max bitrate. + public void SortMediaSources(PlaybackInfoResponse result, long? maxBitrate) + { + var originalList = result.MediaSources.ToList(); + + result.MediaSources = result.MediaSources.OrderBy(i => + { + // Nothing beats direct playing a file + if (i.SupportsDirectPlay && i.Protocol == MediaProtocol.File) + { + return 0; + } + + return 1; + }) + .ThenBy(i => + { + // Let's assume direct streaming a file is just as desirable as direct playing a remote url + if (i.SupportsDirectPlay || i.SupportsDirectStream) + { + return 0; + } + + return 1; + }) + .ThenBy(i => + { + return i.Protocol switch + { + MediaProtocol.File => 0, + _ => 1, + }; + }) + .ThenBy(i => + { + if (maxBitrate.HasValue && i.Bitrate.HasValue) + { + return i.Bitrate.Value <= maxBitrate.Value ? 0 : 2; + } + + return 1; + }) + .ThenBy(originalList.IndexOf) + .ToArray(); + } + + /// + /// Open media source. + /// + /// Http Request. + /// Live stream request. + /// A containing the . + public async Task OpenMediaSource(HttpRequest httpRequest, LiveStreamRequest request) + { + var authInfo = _authContext.GetAuthorizationInfo(httpRequest); + + var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false); + + var profile = request.DeviceProfile; + if (profile == null) + { + var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (caps != null) + { + profile = caps.DeviceProfile; + } + } + + if (profile != null) + { + var item = _libraryManager.GetItemById(request.ItemId); + + SetDeviceSpecificData( + item, + result.MediaSource, + profile, + authInfo, + request.MaxStreamingBitrate, + request.StartTimeTicks ?? 0, + result.MediaSource.Id, + request.AudioStreamIndex, + request.SubtitleStreamIndex, + request.MaxAudioChannels, + request.PlaySessionId, + request.UserId, + request.EnableDirectPlay, + request.EnableDirectStream, + true, + true, + true, + httpRequest.HttpContext.Connection.RemoteIpAddress.ToString()); + } + else + { + if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl)) + { + result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId; + } + } + + // here was a check if (result.MediaSource != null) but Rider said it will never be null + NormalizeMediaSourceContainer(result.MediaSource, profile!, DlnaProfileType.Video); + + return result; + } + + /// + /// Normalize media source container. + /// + /// Media source. + /// Device profile. + /// Dlna profile type. + public void NormalizeMediaSourceContainer(MediaSourceInfo mediaSource, DeviceProfile profile, DlnaProfileType type) + { + mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, mediaSource.Path, profile, type); + } + + private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken) + { + var profiles = info.GetSubtitleProfiles(_mediaEncoder, false, "-", accessToken); + mediaSource.DefaultSubtitleStreamIndex = info.SubtitleStreamIndex; + + mediaSource.TranscodeReasons = info.TranscodeReasons; + + foreach (var profile in profiles) + { + foreach (var stream in mediaSource.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Index == profile.Index) + { + stream.DeliveryMethod = profile.DeliveryMethod; + + if (profile.DeliveryMethod == SubtitleDeliveryMethod.External) + { + stream.DeliveryUrl = profile.Url.TrimStart('-'); + stream.IsExternalUrl = profile.IsExternalUrl; + } + } + } + } + } + + private long? GetMaxBitrate(long? clientMaxBitrate, User user, string ipAddress) + { + var maxBitrate = clientMaxBitrate; + var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0; + + if (remoteClientMaxBitrate <= 0) + { + remoteClientMaxBitrate = _serverConfigurationManager.Configuration.RemoteClientBitrateLimit; + } + + if (remoteClientMaxBitrate > 0) + { + var isInLocalNetwork = _networkManager.IsInLocalNetwork(ipAddress); + + _logger.LogInformation("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, ipAddress, isInLocalNetwork); + if (!isInLocalNetwork) + { + maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate); + } + } + + return maxBitrate; + } + } +} From c5e9cf15f6476d96eadc1d32e38687c3a5a98a17 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 10 Aug 2020 07:53:32 -0600 Subject: [PATCH 018/102] Use proper IHttpContextAccessor --- Jellyfin.Api/Controllers/AudioController.cs | 2 +- .../Controllers/DynamicHlsController.cs | 6 ++-- .../Controllers/HlsSegmentController.cs | 4 +-- .../Controllers/UniversalAudioController.cs | 5 ++- Jellyfin.Api/Controllers/VideosController.cs | 7 ++-- Jellyfin.Api/Helpers/AudioHelper.cs | 36 ++++++++++--------- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 23 ++++++------ .../Helpers/FileStreamResponseHelpers.cs | 36 +++++++++---------- 8 files changed, 59 insertions(+), 60 deletions(-) diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 107db95715..d300b4891e 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -192,7 +192,7 @@ namespace Jellyfin.Api.Controllers StreamOptions = streamOptions }; - return await _audioHelper.GetAudioStream(this, _transcodingJobType, streamingRequest).ConfigureAwait(false); + return await _audioHelper.GetAudioStream(_transcodingJobType, streamingRequest).ConfigureAwait(false); } } } diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 1a85a07b88..b4fe3bc8f8 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -272,7 +272,7 @@ namespace Jellyfin.Api.Controllers EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming }; - return await _dynamicHlsHelper.GetMasterHlsPlaylist(this, _transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); + return await _dynamicHlsHelper.GetMasterHlsPlaylist(_transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); } /// @@ -439,7 +439,7 @@ namespace Jellyfin.Api.Controllers EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming }; - return await _dynamicHlsHelper.GetMasterHlsPlaylist(this, _transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); + return await _dynamicHlsHelper.GetMasterHlsPlaylist(_transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); } /// @@ -1657,7 +1657,7 @@ namespace Jellyfin.Api.Controllers return Task.CompletedTask; }); - return FileStreamResponseHelpers.GetStaticFileResult(segmentPath, MimeTypes.GetMimeType(segmentPath)!, false, this); + return FileStreamResponseHelpers.GetStaticFileResult(segmentPath, MimeTypes.GetMimeType(segmentPath)!, false, HttpContext); } private long GetEndPositionTicks(StreamState state, int requestedIndex) diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index e4a6842bce..816252f80d 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -61,7 +61,7 @@ namespace Jellyfin.Api.Controllers var file = segmentId + Path.GetExtension(Request.Path); file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); - return FileStreamResponseHelpers.GetStaticFileResult(file, MimeTypes.GetMimeType(file)!, false, this); + return FileStreamResponseHelpers.GetStaticFileResult(file, MimeTypes.GetMimeType(file)!, false, HttpContext); } /// @@ -148,7 +148,7 @@ namespace Jellyfin.Api.Controllers return Task.CompletedTask; }); - return FileStreamResponseHelpers.GetStaticFileResult(path, MimeTypes.GetMimeType(path)!, false, this); + return FileStreamResponseHelpers.GetStaticFileResult(path, MimeTypes.GetMimeType(path)!, false, HttpContext); } } } diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 2ea4624881..2a788e03ad 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -110,7 +110,6 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool breakOnNonKeyFrames, [FromQuery] bool enableRedirection = true) { - bool isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head; var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); _authorizationContext.GetAuthorizationInfo(Request).DeviceId = deviceId; @@ -222,7 +221,7 @@ namespace Jellyfin.Api.Controllers EnableAdaptiveBitrateStreaming = true }; - return await _dynamicHlsHelper.GetMasterHlsPlaylist(this, TranscodingJobType.Hls, dynamicHlsRequestDto, true) + return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType.Hls, dynamicHlsRequestDto, true) .ConfigureAwait(false); } @@ -251,7 +250,7 @@ namespace Jellyfin.Api.Controllers Context = EncodingContext.Static }; - return await _audioHelper.GetAudioStream(this, TranscodingJobType.Progressive, audioStreamingDto).ConfigureAwait(false); + return await _audioHelper.GetAudioStream(TranscodingJobType.Progressive, audioStreamingDto).ConfigureAwait(false); } private DeviceProfile GetDeviceProfile( diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index fafa722c57..8eee51c2c1 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -471,7 +471,7 @@ namespace Jellyfin.Api.Controllers StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager); using var httpClient = _httpClientFactory.CreateClient(); - return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, httpClient).ConfigureAwait(false); + return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, HttpContext).ConfigureAwait(false); } if (@static.HasValue && @static.Value && state.InputProtocol != MediaProtocol.File) @@ -507,7 +507,7 @@ namespace Jellyfin.Api.Controllers state.MediaPath, contentType, isHeadRequest, - this); + HttpContext); } // Need to start ffmpeg (because media can't be returned directly) @@ -517,10 +517,9 @@ namespace Jellyfin.Api.Controllers return await FileStreamResponseHelpers.GetTranscodedFile( state, isHeadRequest, - this, + HttpContext, _transcodingJobHelper, ffmpegCommandLineArguments, - Request, _transcodingJobType, cancellationTokenSource).ConfigureAwait(false); } diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs index 0010284322..694e1f0ddd 100644 --- a/Jellyfin.Api/Helpers/AudioHelper.cs +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -12,6 +12,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -35,6 +36,7 @@ namespace Jellyfin.Api.Helpers private readonly IDeviceManager _deviceManager; private readonly TranscodingJobHelper _transcodingJobHelper; private readonly IHttpClientFactory _httpClientFactory; + private readonly IHttpContextAccessor _httpContextAccessor; /// /// Initializes a new instance of the class. @@ -52,6 +54,7 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of . /// Instance of the interface. + /// Instance of the interface. public AudioHelper( IDlnaManager dlnaManager, IAuthorizationContext authContext, @@ -65,7 +68,8 @@ namespace Jellyfin.Api.Helpers IConfiguration configuration, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, - IHttpClientFactory httpClientFactory) + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor) { _dlnaManager = dlnaManager; _authContext = authContext; @@ -80,26 +84,25 @@ namespace Jellyfin.Api.Helpers _deviceManager = deviceManager; _transcodingJobHelper = transcodingJobHelper; _httpClientFactory = httpClientFactory; + _httpContextAccessor = httpContextAccessor; } /// /// Get audio stream. /// - /// Requesting controller. /// Transcoding job type. /// Streaming controller.Request dto. /// A containing the resulting . public async Task GetAudioStream( - BaseJellyfinApiController controller, TranscodingJobType transcodingJobType, StreamingRequestDto streamingRequest) { - bool isHeadRequest = controller.Request.Method == System.Net.WebRequestMethods.Http.Head; + bool isHeadRequest = _httpContextAccessor.HttpContext.Request.Method == System.Net.WebRequestMethods.Http.Head; var cancellationTokenSource = new CancellationTokenSource(); using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - controller.Request, + _httpContextAccessor.HttpContext.Request, _authContext, _mediaSourceManager, _userManager, @@ -118,30 +121,30 @@ namespace Jellyfin.Api.Helpers if (streamingRequest.Static && state.DirectStreamProvider != null) { - StreamingHelpers.AddDlnaHeaders(state, controller.Response.Headers, true, streamingRequest.StartTimeTicks, controller.Request, _dlnaManager); + StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, true, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager); await new ProgressiveFileCopier(state.DirectStreamProvider, null, _transcodingJobHelper, CancellationToken.None) { AllowEndOfFile = false - }.WriteToAsync(controller.Response.Body, CancellationToken.None) + }.WriteToAsync(_httpContextAccessor.HttpContext.Response.Body, CancellationToken.None) .ConfigureAwait(false); // TODO (moved from MediaBrowser.Api): Don't hardcode contentType - return controller.File(controller.Response.Body, MimeTypes.GetMimeType("file.ts")!); + return new FileStreamResult(_httpContextAccessor.HttpContext.Response.Body, MimeTypes.GetMimeType("file.ts")!); } // Static remote stream if (streamingRequest.Static && state.InputProtocol == MediaProtocol.Http) { - StreamingHelpers.AddDlnaHeaders(state, controller.Response.Headers, true, streamingRequest.StartTimeTicks, controller.Request, _dlnaManager); + StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, true, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager); using var httpClient = _httpClientFactory.CreateClient(); - return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, controller, httpClient).ConfigureAwait(false); + return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, _httpContextAccessor.HttpContext).ConfigureAwait(false); } if (streamingRequest.Static && state.InputProtocol != MediaProtocol.File) { - return controller.BadRequest($"Input protocol {state.InputProtocol} cannot be streamed statically"); + return new BadRequestObjectResult($"Input protocol {state.InputProtocol} cannot be streamed statically"); } var outputPath = state.OutputFilePath; @@ -150,7 +153,7 @@ namespace Jellyfin.Api.Helpers var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive); var isTranscodeCached = outputPathExists && transcodingJob != null; - StreamingHelpers.AddDlnaHeaders(state, controller.Response.Headers, streamingRequest.Static || isTranscodeCached, streamingRequest.StartTimeTicks, controller.Request, _dlnaManager); + StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, streamingRequest.Static || isTranscodeCached, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager); // Static stream if (streamingRequest.Static) @@ -162,17 +165,17 @@ namespace Jellyfin.Api.Helpers await new ProgressiveFileCopier(state.MediaPath, null, _transcodingJobHelper, CancellationToken.None) { AllowEndOfFile = false - }.WriteToAsync(controller.Response.Body, CancellationToken.None) + }.WriteToAsync(_httpContextAccessor.HttpContext.Response.Body, CancellationToken.None) .ConfigureAwait(false); - return controller.File(controller.Response.Body, contentType); + return new FileStreamResult(_httpContextAccessor.HttpContext.Response.Body, contentType); } return FileStreamResponseHelpers.GetStaticFileResult( state.MediaPath, contentType, isHeadRequest, - controller); + _httpContextAccessor.HttpContext); } // Need to start ffmpeg (because media can't be returned directly) @@ -182,10 +185,9 @@ namespace Jellyfin.Api.Helpers return await FileStreamResponseHelpers.GetTranscodedFile( state, isHeadRequest, - controller, + _httpContextAccessor.HttpContext, _transcodingJobHelper, ffmpegCommandLineArguments, - controller.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); } diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 127d914306..6a8829d462 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -19,6 +19,7 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -45,6 +46,7 @@ namespace Jellyfin.Api.Helpers private readonly TranscodingJobHelper _transcodingJobHelper; private readonly INetworkManager _networkManager; private readonly ILogger _logger; + private readonly IHttpContextAccessor _httpContextAccessor; /// /// Initializes a new instance of the class. @@ -63,6 +65,7 @@ namespace Jellyfin.Api.Helpers /// Instance of . /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public DynamicHlsHelper( ILibraryManager libraryManager, IUserManager userManager, @@ -77,7 +80,8 @@ namespace Jellyfin.Api.Helpers IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, INetworkManager networkManager, - ILogger logger) + ILogger logger, + IHttpContextAccessor httpContextAccessor) { _libraryManager = libraryManager; _userManager = userManager; @@ -93,26 +97,24 @@ namespace Jellyfin.Api.Helpers _transcodingJobHelper = transcodingJobHelper; _networkManager = networkManager; _logger = logger; + _httpContextAccessor = httpContextAccessor; } /// /// Get master hls playlist. /// - /// Requesting controller. /// Transcoding job type. /// Streaming request dto. /// Enable adaptive bitrate streaming. /// A containing the resulting . public async Task GetMasterHlsPlaylist( - BaseJellyfinApiController controller, TranscodingJobType transcodingJobType, StreamingRequestDto streamingRequest, bool enableAdaptiveBitrateStreaming) { - var isHeadRequest = controller.Request.Method == WebRequestMethods.Http.Head; + var isHeadRequest = _httpContextAccessor.HttpContext.Request.Method == WebRequestMethods.Http.Head; var cancellationTokenSource = new CancellationTokenSource(); return await GetMasterPlaylistInternal( - controller, streamingRequest, isHeadRequest, enableAdaptiveBitrateStreaming, @@ -121,7 +123,6 @@ namespace Jellyfin.Api.Helpers } private async Task GetMasterPlaylistInternal( - BaseJellyfinApiController controller, StreamingRequestDto streamingRequest, bool isHeadRequest, bool enableAdaptiveBitrateStreaming, @@ -130,7 +131,7 @@ namespace Jellyfin.Api.Helpers { using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - controller.Request, + _httpContextAccessor.HttpContext.Request, _authContext, _mediaSourceManager, _userManager, @@ -147,7 +148,7 @@ namespace Jellyfin.Api.Helpers cancellationTokenSource.Token) .ConfigureAwait(false); - controller.Response.Headers.Add(HeaderNames.Expires, "0"); + _httpContextAccessor.HttpContext.Response.Headers.Add(HeaderNames.Expires, "0"); if (isHeadRequest) { return new FileContentResult(Array.Empty(), MimeTypes.GetMimeType("playlist.m3u8")); @@ -161,7 +162,7 @@ namespace Jellyfin.Api.Helpers var isLiveStream = state.IsSegmentedLiveStream; - var queryString = controller.Request.QueryString.ToString(); + var queryString = _httpContextAccessor.HttpContext.Request.QueryString.ToString(); // from universal audio service if (queryString.IndexOf("SegmentContainer", StringComparison.OrdinalIgnoreCase) == -1 && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer)) @@ -197,12 +198,12 @@ namespace Jellyfin.Api.Helpers if (!string.IsNullOrWhiteSpace(subtitleGroup)) { - AddSubtitles(state, subtitleStreams, builder, controller.Request.HttpContext.User); + AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.Request.HttpContext.User); } AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup); - if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, controller.Request.HttpContext.Connection.RemoteIpAddress)) + if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, _httpContextAccessor.HttpContext.Request.HttpContext.Connection.RemoteIpAddress)) { var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0; diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index a463783e00..e2f82b2de1 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -22,14 +22,14 @@ namespace Jellyfin.Api.Helpers /// /// The current . /// Whether the current request is a HTTP HEAD request so only the headers get returned. - /// The managing the response. /// The making the remote request. + /// The current http context. /// A containing the API response. public static async Task GetStaticRemoteStreamResult( StreamState state, bool isHeadRequest, - ControllerBase controller, - HttpClient httpClient) + HttpClient httpClient, + HttpContext httpContext) { if (state.RemoteHttpHeaders.TryGetValue(HeaderNames.UserAgent, out var useragent)) { @@ -39,14 +39,14 @@ namespace Jellyfin.Api.Helpers using var response = await httpClient.GetAsync(state.MediaPath).ConfigureAwait(false); var contentType = response.Content.Headers.ContentType.ToString(); - controller.Response.Headers[HeaderNames.AcceptRanges] = "none"; + httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none"; if (isHeadRequest) { - return controller.File(Array.Empty(), contentType); + return new FileContentResult(Array.Empty(), contentType); } - return controller.File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), contentType); + return new FileStreamResult(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), contentType); } /// @@ -55,23 +55,23 @@ namespace Jellyfin.Api.Helpers /// The path to the file. /// The content type of the file. /// Whether the current request is a HTTP HEAD request so only the headers get returned. - /// The managing the response. + /// The current http context. /// An the file. public static ActionResult GetStaticFileResult( string path, string contentType, bool isHeadRequest, - ControllerBase controller) + HttpContext httpContext) { - controller.Response.ContentType = contentType; + httpContext.Response.ContentType = contentType; // if the request is a head request, return a NoContent result with the same headers as it would with a GET request if (isHeadRequest) { - return controller.NoContent(); + return new NoContentResult(); } - return controller.PhysicalFile(path, contentType); + return new PhysicalFileResult(path, contentType); } /// @@ -79,34 +79,32 @@ namespace Jellyfin.Api.Helpers /// /// The current . /// Whether the current request is a HTTP HEAD request so only the headers get returned. - /// The managing the response. + /// The current http context. /// The singleton. /// The command line arguments to start ffmpeg. - /// The starting the transcoding. /// The . /// The . /// A containing the transcoded file. public static async Task GetTranscodedFile( StreamState state, bool isHeadRequest, - ControllerBase controller, + HttpContext httpContext, TranscodingJobHelper transcodingJobHelper, string ffmpegCommandLineArguments, - HttpRequest request, TranscodingJobType transcodingJobType, CancellationTokenSource cancellationTokenSource) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; - controller.Response.Headers[HeaderNames.AcceptRanges] = "none"; + httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none"; var contentType = state.GetMimeType(outputPath); // Headers only if (isHeadRequest) { - return controller.File(Array.Empty(), contentType); + return new FileContentResult(Array.Empty(), contentType); } var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath); @@ -116,7 +114,7 @@ namespace Jellyfin.Api.Helpers TranscodingJobDto? job; if (!File.Exists(outputPath)) { - job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); + job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); } else { @@ -127,7 +125,7 @@ namespace Jellyfin.Api.Helpers var memoryStream = new MemoryStream(); await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false); memoryStream.Position = 0; - return controller.File(memoryStream, contentType); + return new FileStreamResult(memoryStream, contentType); } finally { From 2e3ef18a11f73c8365573ffb86c62cb4bd82a74d Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 10 Aug 2020 10:09:27 -0600 Subject: [PATCH 019/102] fix route typo --- Jellyfin.Api/Controllers/SessionController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 9bdaa17833..4795fb0cf7 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -480,7 +480,7 @@ namespace Jellyfin.Api.Controllers /// /// Password reset providers retrieved. /// An with the password reset providers. - [HttpGet("Auto/PasswordResetProviders")] + [HttpGet("Auth/PasswordResetProviders")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.RequiresElevation)] public ActionResult> GetPasswordResetProviders() From 2f853b902a801da16c819cd745087bbed23701b6 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 10 Aug 2020 11:33:42 -0600 Subject: [PATCH 020/102] fix datetime parsing --- Jellyfin.Api/Controllers/PlaystateController.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 0422bfe727..70394800b5 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; @@ -73,15 +74,21 @@ namespace Jellyfin.Api.Controllers public ActionResult MarkPlayedItem( [FromRoute] Guid userId, [FromRoute] Guid itemId, - [FromQuery] DateTime? datePlayed) + [FromQuery] string? datePlayed) { + DateTime? parsedDatePlayed = null; + if (!string.IsNullOrEmpty(datePlayed)) + { + parsedDatePlayed = DateTime.ParseExact(datePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + } + var user = _userManager.GetUserById(userId); var session = RequestHelpers.GetSession(_sessionManager, _authContext, Request); - var dto = UpdatePlayedStatus(user, itemId, true, datePlayed); + var dto = UpdatePlayedStatus(user, itemId, true, parsedDatePlayed); foreach (var additionalUserInfo in session.AdditionalUsers) { var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId); - UpdatePlayedStatus(additionalUser, itemId, true, datePlayed); + UpdatePlayedStatus(additionalUser, itemId, true, parsedDatePlayed); } return dto; From 4b87bbf53d61e5855c69e8d566d466e7c849c9e8 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 10 Aug 2020 11:38:02 -0600 Subject: [PATCH 021/102] fix route --- Jellyfin.Api/Controllers/PlaystateController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 70394800b5..40e5e462a4 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -101,7 +101,7 @@ namespace Jellyfin.Api.Controllers /// Item id. /// Item marked as unplayed. /// A containing the . - [HttpDelete("Users/{userId}/PlayedItem/{itemId}")] + [HttpDelete("Users/{userId}/PlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult MarkUnplayedItem([FromRoute] Guid userId, [FromRoute] Guid itemId) { From b0532c549a6ba8c743a807aed7f6b42b63aee708 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 10 Aug 2020 11:58:06 -0600 Subject: [PATCH 022/102] add proper converter for legacy datetime --- .../TypeConverters/DateTimeTypeConverter.cs | 37 +++++++++++++++++++ Jellyfin.Server/Startup.cs | 7 +++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs diff --git a/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs b/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs new file mode 100644 index 0000000000..c54a8c0d81 --- /dev/null +++ b/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Jellyfin.Api.TypeConverters +{ + /// + /// Custom datetime parser. + /// + public class DateTimeTypeConverter : TypeConverter + { + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + + return base.CanConvertFrom(context, sourceType); + } + + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string dateString) + { + if (DateTime.TryParseExact(dateString, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dateTime)) + { + return dateTime; + } + } + + return base.ConvertFrom(context, culture, value); + } + } +} diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 108d8f881e..d0dd183c68 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -1,4 +1,6 @@ -using System.Net.Http; +using System; +using System.ComponentModel; +using Jellyfin.Api.TypeConverters; using Jellyfin.Server.Extensions; using Jellyfin.Server.Middleware; using Jellyfin.Server.Models; @@ -94,6 +96,9 @@ namespace Jellyfin.Server }); app.Use(serverApplicationHost.ExecuteHttpHandlerAsync); + + // Add type descriptor for legacy datetime parsing. + TypeDescriptor.AddAttributes(typeof(DateTime?), new TypeConverterAttribute(typeof(DateTimeTypeConverter))); } } } From 3ba1f4c9019155daf7db6865016eaa8914f54ee6 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 10 Aug 2020 12:02:14 -0600 Subject: [PATCH 023/102] add proper converter for legacy datetime --- Jellyfin.Api/Controllers/PlaystateController.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 40e5e462a4..351e817039 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -1,6 +1,6 @@ using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; @@ -74,21 +74,15 @@ namespace Jellyfin.Api.Controllers public ActionResult MarkPlayedItem( [FromRoute] Guid userId, [FromRoute] Guid itemId, - [FromQuery] string? datePlayed) + [FromQuery] DateTime? datePlayed) { - DateTime? parsedDatePlayed = null; - if (!string.IsNullOrEmpty(datePlayed)) - { - parsedDatePlayed = DateTime.ParseExact(datePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); - } - var user = _userManager.GetUserById(userId); var session = RequestHelpers.GetSession(_sessionManager, _authContext, Request); - var dto = UpdatePlayedStatus(user, itemId, true, parsedDatePlayed); + var dto = UpdatePlayedStatus(user, itemId, true, datePlayed); foreach (var additionalUserInfo in session.AdditionalUsers) { var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId); - UpdatePlayedStatus(additionalUser, itemId, true, parsedDatePlayed); + UpdatePlayedStatus(additionalUser, itemId, true, datePlayed); } return dto; From bb08e25f86c4542fac49e7b6184edd5065fb970b Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 12 Aug 2020 12:43:43 -0600 Subject: [PATCH 024/102] userid isn't actually required --- Jellyfin.Api/Controllers/DevicesController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs index 23d10e2156..1aed20ade6 100644 --- a/Jellyfin.Api/Controllers/DevicesController.cs +++ b/Jellyfin.Api/Controllers/DevicesController.cs @@ -48,7 +48,7 @@ namespace Jellyfin.Api.Controllers [HttpGet] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetDevices([FromQuery] bool? supportsSync, [FromQuery, Required] Guid? userId) + public ActionResult> GetDevices([FromQuery] bool? supportsSync, [FromQuery] Guid? userId) { var deviceQuery = new DeviceQuery { SupportsSync = supportsSync, UserId = userId ?? Guid.Empty }; return _deviceManager.GetDevices(deviceQuery); From eb7ab31b5ab8045aff41f014bb61dfd67b4ec557 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 12 Aug 2020 12:43:57 -0600 Subject: [PATCH 025/102] add support for GetActivityLogs --- Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs b/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs index c54a8c0d81..b6e5225b1c 100644 --- a/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs +++ b/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs @@ -25,9 +25,16 @@ namespace Jellyfin.Api.TypeConverters { if (value is string dateString) { - if (DateTime.TryParseExact(dateString, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dateTime)) + // Mark Played Item. + if (DateTime.TryParseExact(dateString, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dateTime1)) { - return dateTime; + return dateTime1; + } + + // Get Activity Logs. + if (DateTime.TryParse(dateString, null, DateTimeStyles.RoundtripKind, out var dateTime2)) + { + return dateTime2; } } From afeba4dae75b6436a12b77965551da149452808a Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 12 Aug 2020 13:07:11 -0600 Subject: [PATCH 026/102] simplify datetime parsing --- Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs b/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs index b6e5225b1c..315b473293 100644 --- a/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs +++ b/Jellyfin.Api/TypeConverters/DateTimeTypeConverter.cs @@ -26,15 +26,15 @@ namespace Jellyfin.Api.TypeConverters if (value is string dateString) { // Mark Played Item. - if (DateTime.TryParseExact(dateString, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dateTime1)) + if (DateTime.TryParseExact(dateString, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dateTime)) { - return dateTime1; + return dateTime; } // Get Activity Logs. - if (DateTime.TryParse(dateString, null, DateTimeStyles.RoundtripKind, out var dateTime2)) + if (DateTime.TryParse(dateString, null, DateTimeStyles.RoundtripKind, out dateTime)) { - return dateTime2; + return dateTime; } } From ae1593b59029882c07f29685bee5a2caace8f32e Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 12 Aug 2020 13:14:42 -0600 Subject: [PATCH 027/102] fix route method --- Jellyfin.Api/Controllers/LiveTvController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 89112eea7d..c6a98fb0ae 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1004,7 +1004,7 @@ namespace Jellyfin.Api.Controllers /// Validate login. /// Created listings provider returned. /// A containing the created listings provider. - [HttpGet("ListingProviders")] + [HttpPost("ListingProviders")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA5350:RemoveSha1", MessageId = "AddListingProvider", Justification = "Imported from ServiceStack")] From 687b008d59b42cdadd5d7aa53585fdd2edf6a9b2 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 12 Aug 2020 14:41:10 -0600 Subject: [PATCH 028/102] fix livetv id check --- Jellyfin.Api/Controllers/LiveTvController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index c6a98fb0ae..c4a7160cd1 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -592,11 +592,11 @@ namespace Jellyfin.Api.Controllers GenreIds = RequestHelpers.GetGuids(genreIds) }; - if (!librarySeriesId.Equals(Guid.Empty)) + if (librarySeriesId != null && !librarySeriesId.Equals(Guid.Empty)) { query.IsSeries = true; - if (_libraryManager.GetItemById(librarySeriesId ?? Guid.Empty) is Series series) + if (_libraryManager.GetItemById(librarySeriesId.Value) is Series series) { query.Name = series.Name; } From 7cbf75839a31b477feed46a10784a219c194604a Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Thu, 13 Aug 2020 15:29:32 +0000 Subject: [PATCH 029/102] Translated using Weblate (Tamil) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/ --- Emby.Server.Implementations/Localization/Core/ta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index f722dd8c07..8090806a80 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -93,7 +93,7 @@ "Channels": "சேனல்கள்", "Books": "புத்தகங்கள்", "AuthenticationSucceededWithUserName": "{0} வெற்றிகரமாக அங்கீகரிக்கப்பட்டது", - "Artists": "கலைஞர்கள்", + "Artists": "கலைஞர்", "Application": "செயலி", "Albums": "ஆல்பங்கள்" } From 32d465742989520b8cd9ecdad759ed9cfc128a54 Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Thu, 13 Aug 2020 16:37:52 +0000 Subject: [PATCH 030/102] Translated using Weblate (Tamil) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/ --- Emby.Server.Implementations/Localization/Core/ta.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index 8090806a80..16136a90c9 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -95,5 +95,7 @@ "AuthenticationSucceededWithUserName": "{0} வெற்றிகரமாக அங்கீகரிக்கப்பட்டது", "Artists": "கலைஞர்", "Application": "செயலி", - "Albums": "ஆல்பங்கள்" + "Albums": "ஆல்பங்கள்", + "NewVersionIsAvailable": "ஜெல்லிஃபின் சேவையகத்தின் புதிய பதிப்பு பதிவிறக்கத்திற்கு கிடைக்கிறது.", + "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0 புதுப்பிக்கப்பட்டது" } From 2be1865b429540a831806832c1313d7d48ad2f45 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 13 Aug 2020 12:50:04 -0400 Subject: [PATCH 031/102] Remove unused methods in IDtoService --- Emby.Server.Implementations/Dto/DtoService.cs | 48 ++----------------- MediaBrowser.Controller/Dto/IDtoService.cs | 24 ---------- 2 files changed, 5 insertions(+), 67 deletions(-) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index c967e9230e..f2c7118fe0 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -73,25 +73,6 @@ namespace Emby.Server.Implementations.Dto _livetvManagerFactory = livetvManagerFactory; } - /// - /// Converts a BaseItem to a DTOBaseItem. - /// - /// The item. - /// The fields. - /// The user. - /// The owner. - /// Task{DtoBaseItem}. - /// item - public BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null) - { - var options = new DtoOptions - { - Fields = fields - }; - - return GetBaseItemDto(item, options, user, owner); - } - /// public IReadOnlyList GetBaseItemDtos(IReadOnlyList items, DtoOptions options, User user = null, BaseItem owner = null) { @@ -443,17 +424,6 @@ namespace Emby.Server.Implementations.Dto return folder.GetChildCount(user); } - /// - /// Gets client-side Id of a server-side BaseItem. - /// - /// The item. - /// System.String. - /// item - public string GetDtoId(BaseItem item) - { - return item.Id.ToString("N", CultureInfo.InvariantCulture); - } - private static void SetBookProperties(BaseItemDto dto, Book item) { dto.SeriesName = item.SeriesName; @@ -484,6 +454,11 @@ namespace Emby.Server.Implementations.Dto } } + private string GetDtoId(BaseItem item) + { + return item.Id.ToString("N", CultureInfo.InvariantCulture); + } + private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item) { if (!string.IsNullOrEmpty(item.Album)) @@ -513,19 +488,6 @@ namespace Emby.Server.Implementations.Dto .ToArray(); } - private string GetImageCacheTag(BaseItem item, ImageType type) - { - try - { - return _imageProcessor.GetImageCacheTag(item, type); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error getting {type} image info", type); - return null; - } - } - private string GetImageCacheTag(BaseItem item, ItemImageInfo image) { try diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 0dadc283ee..988557f42c 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Dto { @@ -11,20 +10,6 @@ namespace MediaBrowser.Controller.Dto /// public interface IDtoService { - /// - /// Gets the dto id. - /// - /// The item. - /// System.String. - string GetDtoId(BaseItem item); - - /// - /// Attaches the primary image aspect ratio. - /// - /// The dto. - /// The item. - void AttachPrimaryImageAspectRatio(IItemDto dto, BaseItem item); - /// /// Gets the primary image aspect ratio. /// @@ -32,15 +17,6 @@ namespace MediaBrowser.Controller.Dto /// System.Nullable<System.Double>. double? GetPrimaryImageAspectRatio(BaseItem item); - /// - /// Gets the base item dto. - /// - /// The item. - /// The fields. - /// The user. - /// The owner. - BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null); - /// /// Gets the base item dto. /// From 70e56314d7185367246449c85e239be136ccb789 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 13 Aug 2020 13:11:30 -0400 Subject: [PATCH 032/102] Minor fixes to LiveTvMediaSourceProvider --- .../LiveTv/LiveTvMediaSourceProvider.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index f3fc413527..8a0c0043a9 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -19,8 +19,7 @@ namespace Emby.Server.Implementations.LiveTv public class LiveTvMediaSourceProvider : IMediaSourceProvider { // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. - private const char StreamIdDelimeter = '_'; - private const string StreamIdDelimeterString = "_"; + private const char StreamIdDelimiter = '_'; private readonly ILiveTvManager _liveTvManager; private readonly ILogger _logger; @@ -47,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv } } - return Task.FromResult>(Array.Empty()); + return Task.FromResult(Enumerable.Empty()); } private async Task> GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) @@ -98,7 +97,7 @@ namespace Emby.Server.Implementations.LiveTv source.Id ?? string.Empty }; - source.OpenToken = string.Join(StreamIdDelimeterString, openKeys); + source.OpenToken = string.Join(StreamIdDelimiter, openKeys); } // Dummy this up so that direct play checks can still run @@ -116,7 +115,7 @@ namespace Emby.Server.Implementations.LiveTv /// public async Task OpenMediaSource(string openToken, List currentLiveStreams, CancellationToken cancellationToken) { - var keys = openToken.Split(new[] { StreamIdDelimeter }, 3); + var keys = openToken.Split(StreamIdDelimiter, 3); var mediaSourceId = keys.Length >= 3 ? keys[2] : null; var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false); From 1ce4b70104b70739f68c4bfa7c1ed6aac9ea1509 Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Thu, 13 Aug 2020 16:38:45 +0000 Subject: [PATCH 033/102] Translated using Weblate (Tamil) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/ --- Emby.Server.Implementations/Localization/Core/ta.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index 16136a90c9..df5a637ed5 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -45,7 +45,7 @@ "TvShows": "தொலைக்காட்சித் தொடர்கள்", "Sync": "ஒத்திசைவு", "StartupEmbyServerIsLoading": "ஜெல்லிஃபின் சேவையகம் துவங்குகிறது. சிறிது நேரம் கழித்து முயற்சிக்கவும்.", - "Songs": "பாட்டுகள்", + "Songs": "பாடல்கள்", "Shows": "தொடர்கள்", "ServerNameNeedsToBeRestarted": "{0} மறுதொடக்கம் செய்யப்பட வேண்டும்", "ScheduledTaskStartedWithName": "{0} துவங்கியது", @@ -97,5 +97,8 @@ "Application": "செயலி", "Albums": "ஆல்பங்கள்", "NewVersionIsAvailable": "ஜெல்லிஃபின் சேவையகத்தின் புதிய பதிப்பு பதிவிறக்கத்திற்கு கிடைக்கிறது.", - "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0 புதுப்பிக்கப்பட்டது" + "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0 புதுப்பிக்கப்பட்டது", + "TaskCleanCacheDescription": "கணினிக்கு இனி தேவைப்படாத தற்காலிக கோப்புகளை நீக்கு", + "UserOfflineFromDevice": "{0} இலிருந்து {1} துண்டிக்கப்பட்டுள்ளது", + "SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0 } இலிருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன" } From 27139709e5a1831af52f7473edf017d76e8af066 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 13 Aug 2020 17:05:55 -0400 Subject: [PATCH 034/102] Remove ListHelper.cs --- MediaBrowser.Model/Extensions/ListHelper.cs | 29 --------------------- 1 file changed, 29 deletions(-) delete mode 100644 MediaBrowser.Model/Extensions/ListHelper.cs diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs deleted file mode 100644 index b893a3509a..0000000000 --- a/MediaBrowser.Model/Extensions/ListHelper.cs +++ /dev/null @@ -1,29 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Extensions -{ - // TODO: @bond remove - public static class ListHelper - { - public static bool ContainsIgnoreCase(string[] list, string value) - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - foreach (var item in list) - { - if (string.Equals(item, value, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - } -} From 97fd11c9a43c136badf2d35945bbe50ffaaa6a85 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 13 Aug 2020 15:50:19 -0600 Subject: [PATCH 035/102] Remove httpclient from Jellyfin.Api --- Jellyfin.Api/Controllers/LiveTvController.cs | 21 +++++++------- .../Controllers/RemoteImageController.cs | 28 +++++++------------ 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 89112eea7d..bce74b780f 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Net.Http; using System.Net.Mime; using System.Security.Cryptography; using System.Text; @@ -15,7 +16,6 @@ using Jellyfin.Api.Models.LiveTvDtos; using Jellyfin.Data.Enums; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -39,7 +39,7 @@ namespace Jellyfin.Api.Controllers { private readonly ILiveTvManager _liveTvManager; private readonly IUserManager _userManager; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly IDtoService _dtoService; private readonly ISessionContext _sessionContext; @@ -52,7 +52,7 @@ namespace Jellyfin.Api.Controllers /// /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. + /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -62,7 +62,7 @@ namespace Jellyfin.Api.Controllers public LiveTvController( ILiveTvManager liveTvManager, IUserManager userManager, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IDtoService dtoService, ISessionContext sessionContext, @@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers { _liveTvManager = liveTvManager; _userManager = userManager; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; _dtoService = dtoService; _sessionContext = sessionContext; @@ -1069,13 +1069,12 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetSchedulesDirectCountries() { + using var client = _httpClientFactory.CreateClient(); // https://json.schedulesdirect.org/20141201/available/countries - var response = await _httpClient.Get(new HttpRequestOptions - { - Url = "https://json.schedulesdirect.org/20141201/available/countries", - BufferContent = false - }).ConfigureAwait(false); - return File(response, MediaTypeNames.Application.Json); + var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries") + .ConfigureAwait(false); + + return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json); } /// diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index baa3d80ac8..bcb20d833a 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; +using System.Net.Http; using System.Net.Mime; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -30,7 +30,7 @@ namespace Jellyfin.Api.Controllers { private readonly IProviderManager _providerManager; private readonly IServerApplicationPaths _applicationPaths; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; /// @@ -38,17 +38,17 @@ namespace Jellyfin.Api.Controllers /// /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. + /// Instance of the interface. /// Instance of the interface. public RemoteImageController( IProviderManager providerManager, IServerApplicationPaths applicationPaths, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager) { _providerManager = providerManager; _applicationPaths = applicationPaths; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; } @@ -244,22 +244,14 @@ namespace Jellyfin.Api.Controllers /// Task. private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath) { - using var result = await _httpClient.GetResponse(new HttpRequestOptions - { - Url = url, - BufferContent = false - }).ConfigureAwait(false); - var ext = result.ContentType.Split('/').Last(); - + using var httpClient = _httpClientFactory.CreateClient(); + var response = await httpClient.GetAsync(url).ConfigureAwait(false); + var ext = response.Content.Headers.ContentType.MediaType.Split('/').Last(); var fullCachePath = GetFullCachePath(urlHash + "." + ext); Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); - await using (var stream = result.Content) - { - await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); - await stream.CopyToAsync(fileStream).ConfigureAwait(false); - } - + await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await response.Content.CopyToAsync(fileStream).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath, CancellationToken.None) .ConfigureAwait(false); From 42a9feb2f3b6bb38144779a5b90e0e2334fb40e2 Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Fri, 14 Aug 2020 10:07:08 +0000 Subject: [PATCH 036/102] Translated using Weblate (Tamil) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/ --- .../Localization/Core/ta.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index df5a637ed5..8369710ecc 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -100,5 +100,16 @@ "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0 புதுப்பிக்கப்பட்டது", "TaskCleanCacheDescription": "கணினிக்கு இனி தேவைப்படாத தற்காலிக கோப்புகளை நீக்கு", "UserOfflineFromDevice": "{0} இலிருந்து {1} துண்டிக்கப்பட்டுள்ளது", - "SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0 } இலிருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன" + "SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0 } இலிருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன", + "TaskDownloadMissingSubtitlesDescription": "மெட்டாடேட்டா உள்ளமைவின் அடிப்படையில் வசன வரிகள் காணாமல் போனதற்கு இணையத்தைத் தேடுகிறது.", + "TaskCleanTranscodeDescription": "டிரான்ஸ்கோட் கோப்புகளை ஒரு நாளுக்கு மேல் பழையதாக நீக்குகிறது.", + "TaskUpdatePluginsDescription": "தானாகவே புதுப்பிக்க கட்டமைக்கப்பட்ட செருகுநிரல்களுக்கான புதுப்பிப்புகளை பதிவிறக்குகிறது மற்றும் நிறுவுகிறது.", + "TaskRefreshPeopleDescription": "உங்கள் மீடியா நூலகத்தில் உள்ள நடிகர்கள் மற்றும் இயக்குனர்களுக்கான மெட்டாடேட்டாவை புதுப்பிக்கும்.", + "TaskCleanLogsDescription": "{0} நாட்களுக்கு மேல் இருக்கும் பதிவு கோப்புகளை நீக்கும்.", + "TaskCleanLogs": "பதிவு அடைவு சுத்தம் செய்யுங்கள்", + "TaskRefreshLibraryDescription": "புதிய கோப்புகளுக்காக உங்கள் மீடியா நூலகத்தை ஸ்கேன் செய்து மீத்தரவை புதுப்பிக்கும்.", + "TaskRefreshChapterImagesDescription": "அத்தியாயங்களைக் கொண்ட வீடியோக்களுக்கான சிறு உருவங்களை உருவாக்குகிறது.", + "ValueHasBeenAddedToLibrary": "உங்கள் மீடியா நூலகத்தில் {0} சேர்க்கப்பட்டது", + "UserOnlineFromDevice": "{1} இருந்து {0} ஆன்லைன்", + "HomeVideos": "முகப்பு வீடியோக்கள்" } From 64658a0bd0a7721f076fd0fe3abd471c823bd8d1 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 14 Aug 2020 08:23:23 -0600 Subject: [PATCH 037/102] Return int64 in json as number --- MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs index d18fd95d5f..7a720a4c90 100644 --- a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Common.Json.Converters /// Options. public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options) { - writer.WriteStringValue(value.ToString(NumberFormatInfo.InvariantInfo)); + writer.WriteNumberValue(value); } } } From 64ace58ef5352fdf9659f9246410be0b02d7c349 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 14 Aug 2020 08:34:32 -0600 Subject: [PATCH 038/102] fix docs --- MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs index 7a720a4c90..427f1fa7e0 100644 --- a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs @@ -8,7 +8,7 @@ using System.Text.Json.Serialization; namespace MediaBrowser.Common.Json.Converters { /// - /// Long to String JSON converter. + /// Parse JSON string as long. /// Javascript does not support 64-bit integers. /// public class JsonInt64Converter : JsonConverter @@ -43,7 +43,7 @@ namespace MediaBrowser.Common.Json.Converters } /// - /// Write long to JSON string. + /// Write long to JSON long. /// /// . /// Value to write. From 6d883bbbe966bf9aa966d2717a6687d61fa79ab3 Mon Sep 17 00:00:00 2001 From: nothing things Date: Fri, 14 Aug 2020 16:49:20 +0000 Subject: [PATCH 039/102] Translated using Weblate (Bengali) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bn/ --- .../Localization/Core/bn.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index ca14d44715..9a6a944307 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -1,12 +1,12 @@ { "DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে", "DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে", - "Collections": "সংকলন", + "Collections": "কলেক্শন", "ChapterNameValue": "অধ্যায় {0}", "Channels": "চ্যানেল", - "CameraImageUploadedFrom": "একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে {0} থেকে", + "CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে", "Books": "বই", - "AuthenticationSucceededWithUserName": "{0} যাচাই সফল", + "AuthenticationSucceededWithUserName": "{0} অনুমোদন সফল", "Artists": "শিল্পীরা", "Application": "অ্যাপ্লিকেশন", "Albums": "অ্যালবামগুলো", @@ -14,13 +14,13 @@ "HeaderFavoriteArtists": "প্রিয় শিল্পীরা", "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো", "HeaderContinueWatching": "দেখতে থাকুন", - "HeaderCameraUploads": "ক্যামেরার আপলোডগুলো", - "HeaderAlbumArtists": "এলবামের শিল্পী", - "Genres": "ঘরানা", + "HeaderCameraUploads": "ক্যামেরার আপলোড সমূহ", + "HeaderAlbumArtists": "এলবাম শিল্পী", + "Genres": "জেনার", "Folders": "ফোল্ডারগুলো", - "Favorites": "ফেভারিটগুলো", + "Favorites": "পছন্দসমূহ", "FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে", - "AppDeviceValues": "এপ: {0}, ডিভাইস: {0}", + "AppDeviceValues": "অ্যাপ: {0}, ডিভাইস: {0}", "VersionNumber": "সংস্করণ {0}", "ValueSpecialEpisodeName": "বিশেষ - {0}", "ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে", From a43383b9ac4b5bf42e2883a66b4b73cb88fde585 Mon Sep 17 00:00:00 2001 From: nothing things Date: Fri, 14 Aug 2020 16:59:42 +0000 Subject: [PATCH 040/102] Translated using Weblate (Bengali) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bn/ --- .../Localization/Core/bn.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index 9a6a944307..1bd1909827 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -74,20 +74,20 @@ "NameInstallFailed": "{0} ইন্সটল ব্যর্থ", "MusicVideos": "গানের ভিডিও", "Music": "গান", - "Movies": "সিনেমা", + "Movies": "চলচ্চিত্র", "MixedContent": "মিশ্র কন্টেন্ট", - "MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন হালনাগাদ করা হয়েছে", - "HeaderRecordingGroups": "রেকর্ডিং গ্রুপ", - "MessageNamedServerConfigurationUpdatedWithValue": "সার্ভারের {0} কনফিগারেসন অংশ আপডেট করা হয়েছে", - "MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে হালনাগাদ করা হয়েছে", - "MessageApplicationUpdated": "জেলিফিন সার্ভার হালনাগাদ করা হয়েছে", - "Latest": "একদম নতুন", + "MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন আপডেট করা হয়েছে", + "HeaderRecordingGroups": "রেকর্ডিং দল", + "MessageNamedServerConfigurationUpdatedWithValue": "সার্ভারের {0} কনফিগারেসনের অংশ আপডেট করা হয়েছে", + "MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে আপডেট করা হয়েছে", + "MessageApplicationUpdated": "জেলিফিন সার্ভার আপডেট করা হয়েছে", + "Latest": "সর্বশেষ", "LabelRunningTimeValue": "চলার সময়: {0}", - "LabelIpAddressValue": "আইপি ঠিকানা: {0}", + "LabelIpAddressValue": "আইপি এড্রেস: {0}", "ItemRemovedWithName": "{0} লাইব্রেরি থেকে বাদ দেয়া হয়েছে", "ItemAddedWithName": "{0} লাইব্রেরিতে যোগ করা হয়েছে", "Inherit": "থেকে পাওয়া", - "HomeVideos": "বাসার ভিডিও", + "HomeVideos": "হোম ভিডিও", "HeaderNextUp": "এরপরে আসছে", "HeaderLiveTV": "লাইভ টিভি", "HeaderFavoriteSongs": "প্রিয় গানগুলো", From f505f48e8580833cb2d3a80413ab7b168aa61fe7 Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Sat, 15 Aug 2020 11:16:37 +0000 Subject: [PATCH 041/102] Translated using Weblate (Tamil) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/ --- Emby.Server.Implementations/Localization/Core/ta.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index 8369710ecc..f66d68b68e 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -111,5 +111,7 @@ "TaskRefreshChapterImagesDescription": "அத்தியாயங்களைக் கொண்ட வீடியோக்களுக்கான சிறு உருவங்களை உருவாக்குகிறது.", "ValueHasBeenAddedToLibrary": "உங்கள் மீடியா நூலகத்தில் {0} சேர்க்கப்பட்டது", "UserOnlineFromDevice": "{1} இருந்து {0} ஆன்லைன்", - "HomeVideos": "முகப்பு வீடியோக்கள்" + "HomeVideos": "முகப்பு வீடியோக்கள்", + "UserStoppedPlayingItemWithValues": "{2} இல் {1} முடித்துவிட்டது", + "UserStartedPlayingItemWithValues": "{0} {2}இல் {1} ஐ இயக்குகிறது" } From 2d4e91c5a202aeb7b5d6d2206767d05a8f850212 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 15 Aug 2020 10:39:24 -0600 Subject: [PATCH 042/102] Add xml output formatter --- .../ApiServiceCollectionExtensions.cs | 1 + .../Formatters/XmlOutputFormatter.cs | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 Jellyfin.Server/Formatters/XmlOutputFormatter.cs diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 83d8fac5b5..2e2bfea684 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -154,6 +154,7 @@ namespace Jellyfin.Server.Extensions opts.OutputFormatters.Insert(0, new PascalCaseJsonProfileFormatter()); opts.OutputFormatters.Add(new CssOutputFormatter()); + opts.OutputFormatters.Add(new XmlOutputFormatter()); }) // Clear app parts to avoid other assemblies being picked up diff --git a/Jellyfin.Server/Formatters/XmlOutputFormatter.cs b/Jellyfin.Server/Formatters/XmlOutputFormatter.cs new file mode 100644 index 0000000000..5f2337b2a2 --- /dev/null +++ b/Jellyfin.Server/Formatters/XmlOutputFormatter.cs @@ -0,0 +1,30 @@ +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Formatters; + +namespace Jellyfin.Server.Formatters +{ + /// + /// Xml output formatter. + /// + public class XmlOutputFormatter : TextOutputFormatter + { + /// + /// Initializes a new instance of the class. + /// + public XmlOutputFormatter() + { + SupportedMediaTypes.Add("text/xml"); + SupportedMediaTypes.Add("text/xml;charset=UTF-8"); + SupportedEncodings.Add(Encoding.UTF8); + SupportedEncodings.Add(Encoding.Unicode); + } + + /// + public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) + { + return context.HttpContext.Response.WriteAsync(context.Object?.ToString()); + } + } +} From 0f85e66962220de17bd632ad4a0d1465324e166c Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Sat, 15 Aug 2020 17:12:59 +0000 Subject: [PATCH 043/102] Translated using Weblate (Tamil) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/ --- Emby.Server.Implementations/Localization/Core/ta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index f66d68b68e..d6be86da3e 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -98,7 +98,7 @@ "Albums": "ஆல்பங்கள்", "NewVersionIsAvailable": "ஜெல்லிஃபின் சேவையகத்தின் புதிய பதிப்பு பதிவிறக்கத்திற்கு கிடைக்கிறது.", "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0 புதுப்பிக்கப்பட்டது", - "TaskCleanCacheDescription": "கணினிக்கு இனி தேவைப்படாத தற்காலிக கோப்புகளை நீக்கு", + "TaskCleanCacheDescription": "கணினிக்கு இனி தேவைப்படாத தற்காலிக கோப்புகளை நீக்கு.", "UserOfflineFromDevice": "{0} இலிருந்து {1} துண்டிக்கப்பட்டுள்ளது", "SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0 } இலிருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன", "TaskDownloadMissingSubtitlesDescription": "மெட்டாடேட்டா உள்ளமைவின் அடிப்படையில் வசன வரிகள் காணாமல் போனதற்கு இணையத்தைத் தேடுகிறது.", From 7d2ad3e0fc4e1002d5d5739e0766fb0c253f32f4 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 08:32:03 -0600 Subject: [PATCH 044/102] Fix DlnaControlResponse string return --- Emby.Dlna/ControlResponse.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs index 140ef9b463..ee72329b77 100644 --- a/Emby.Dlna/ControlResponse.cs +++ b/Emby.Dlna/ControlResponse.cs @@ -16,5 +16,10 @@ namespace Emby.Dlna public string Xml { get; set; } public bool IsSuccessful { get; set; } + + public override string ToString() + { + return Xml; + } } } From 7fe372749e248721c57f60527733ee002215ad09 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 08:32:13 -0600 Subject: [PATCH 045/102] fix dlna server routes --- Jellyfin.Api/Controllers/DlnaServerController.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index ef507f2ed6..0100d642be 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -60,8 +60,8 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Dlna content directory returned. /// An containing the dlna content directory xml. - [HttpGet("{serverId}/ContentDirectory/ContentDirectory")] - [HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_2")] + [HttpGet("{serverId}/ContentDirectory")] + [HttpGet("{serverId}/ContentDirectory.xml", Name = "GetContentDirectory_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -75,8 +75,8 @@ namespace Jellyfin.Api.Controllers /// /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar")] - [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] + [HttpGet("{serverId}/MediaReceiverRegistrar")] + [HttpGet("{serverId}/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -90,8 +90,8 @@ namespace Jellyfin.Api.Controllers /// /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{serverId}/ConnectionManager/ConnectionManager")] - [HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_2")] + [HttpGet("{serverId}/ConnectionManager")] + [HttpGet("{serverId}/ConnectionManager.xml", Name = "GetConnectionManager_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] From 09c859746116a949008ebba832fa17b55c90e6e4 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 08:40:43 -0600 Subject: [PATCH 046/102] inheritdoc --- Emby.Dlna/ControlResponse.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs index ee72329b77..243b097867 100644 --- a/Emby.Dlna/ControlResponse.cs +++ b/Emby.Dlna/ControlResponse.cs @@ -17,6 +17,7 @@ namespace Emby.Dlna public bool IsSuccessful { get; set; } + /// public override string ToString() { return Xml; From 8ddcd12348da48c8a07a7f897c014974995af6d9 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 09:47:08 -0600 Subject: [PATCH 047/102] Remove all using for HttpClient --- Jellyfin.Api/Controllers/AudioController.cs | 2 +- Jellyfin.Api/Controllers/LiveTvController.cs | 2 +- Jellyfin.Api/Controllers/RemoteImageController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 4de87616c5..fb3f09ba65 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -295,7 +295,7 @@ namespace Jellyfin.Api.Controllers { StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager); - using var httpClient = _httpClientFactory.CreateClient(); + var httpClient = _httpClientFactory.CreateClient(); return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, httpClient).ConfigureAwait(false); } diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index bce74b780f..2838361d70 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1069,7 +1069,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetSchedulesDirectCountries() { - using var client = _httpClientFactory.CreateClient(); + var client = _httpClientFactory.CreateClient(); // https://json.schedulesdirect.org/20141201/available/countries var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries") .ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index bcb20d833a..e5b038186f 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -244,7 +244,7 @@ namespace Jellyfin.Api.Controllers /// Task. private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath) { - using var httpClient = _httpClientFactory.CreateClient(); + var httpClient = _httpClientFactory.CreateClient(); var response = await httpClient.GetAsync(url).ConfigureAwait(false); var ext = response.Content.Headers.ContentType.MediaType.Split('/').Last(); var fullCachePath = GetFullCachePath(urlHash + "." + ext); diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index fafa722c57..ae32dbf035 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -470,7 +470,7 @@ namespace Jellyfin.Api.Controllers { StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager); - using var httpClient = _httpClientFactory.CreateClient(); + var httpClient = _httpClientFactory.CreateClient(); return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, httpClient).ConfigureAwait(false); } From 8da646e12b6122b3a73830eb9cbf3e39033d8ba3 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 10:57:01 -0600 Subject: [PATCH 048/102] Dispose httpclient response --- Jellyfin.Api/Controllers/LiveTvController.cs | 2 +- Jellyfin.Api/Controllers/RemoteImageController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 2838361d70..9d8ec9f75c 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1071,7 +1071,7 @@ namespace Jellyfin.Api.Controllers { var client = _httpClientFactory.CreateClient(); // https://json.schedulesdirect.org/20141201/available/countries - var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries") + using var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries") .ConfigureAwait(false); return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json); diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index e5b038186f..a203c50b96 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -245,7 +245,7 @@ namespace Jellyfin.Api.Controllers private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath) { var httpClient = _httpClientFactory.CreateClient(); - var response = await httpClient.GetAsync(url).ConfigureAwait(false); + using var response = await httpClient.GetAsync(url).ConfigureAwait(false); var ext = response.Content.Headers.ContentType.MediaType.Split('/').Last(); var fullCachePath = GetFullCachePath(urlHash + "." + ext); From 0c50015400c63d68513184c58b25fb19d5b600fe Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 11:34:41 -0600 Subject: [PATCH 049/102] Fix audio stream routes --- Jellyfin.Api/Controllers/AudioController.cs | 4 ++-- Jellyfin.Api/Controllers/UniversalAudioController.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index d300b4891e..802cd026e0 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -83,9 +83,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The streaming options. /// Audio stream returned. /// A containing the audio file. - [HttpGet("{itemId}/{stream=stream}.{container?}", Name = "GetAudioStreamByContainer")] + [HttpGet("{itemId}/stream.{container}", Name = "GetAudioStreamByContainer")] [HttpGet("{itemId}/stream", Name = "GetAudioStream")] - [HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadAudioStreamByContainer")] + [HttpHead("{itemId}/stream.{container}", Name = "HeadAudioStreamByContainer")] [HttpHead("{itemId}/stream", Name = "HeadAudioStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetAudioStream( diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 2a788e03ad..8f576d412e 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -85,9 +85,9 @@ namespace Jellyfin.Api.Controllers /// Redirected to remote audio stream. /// A containing the audio file. [HttpGet("Audio/{itemId}/universal")] - [HttpGet("Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")] + [HttpGet("Audio/{itemId}/universal.{container}", Name = "GetUniversalAudioStream_2")] [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")] - [HttpHead("Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")] + [HttpHead("Audio/{itemId}/universal.{container}", Name = "HeadUniversalAudioStream_2")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] From 7861c72754172a1d2ebaffdde861fd614fa65488 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 11:47:26 -0600 Subject: [PATCH 050/102] Revert "Fix audio stream routes" This reverts commit 0c50015400c63d68513184c58b25fb19d5b600fe. --- Jellyfin.Api/Controllers/AudioController.cs | 4 ++-- Jellyfin.Api/Controllers/UniversalAudioController.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 802cd026e0..d300b4891e 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -83,9 +83,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The streaming options. /// Audio stream returned. /// A containing the audio file. - [HttpGet("{itemId}/stream.{container}", Name = "GetAudioStreamByContainer")] + [HttpGet("{itemId}/{stream=stream}.{container?}", Name = "GetAudioStreamByContainer")] [HttpGet("{itemId}/stream", Name = "GetAudioStream")] - [HttpHead("{itemId}/stream.{container}", Name = "HeadAudioStreamByContainer")] + [HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadAudioStreamByContainer")] [HttpHead("{itemId}/stream", Name = "HeadAudioStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetAudioStream( diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 8f576d412e..2a788e03ad 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -85,9 +85,9 @@ namespace Jellyfin.Api.Controllers /// Redirected to remote audio stream. /// A containing the audio file. [HttpGet("Audio/{itemId}/universal")] - [HttpGet("Audio/{itemId}/universal.{container}", Name = "GetUniversalAudioStream_2")] + [HttpGet("Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")] [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")] - [HttpHead("Audio/{itemId}/universal.{container}", Name = "HeadUniversalAudioStream_2")] + [HttpHead("Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] From 21cc308ec9fa9397ec93d41c49310c1fd056c3ce Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 11:48:54 -0600 Subject: [PATCH 051/102] Use proper mediatypename --- Jellyfin.Server/Formatters/XmlOutputFormatter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server/Formatters/XmlOutputFormatter.cs b/Jellyfin.Server/Formatters/XmlOutputFormatter.cs index 5f2337b2a2..58319657d6 100644 --- a/Jellyfin.Server/Formatters/XmlOutputFormatter.cs +++ b/Jellyfin.Server/Formatters/XmlOutputFormatter.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Net.Mime; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; @@ -15,7 +16,7 @@ namespace Jellyfin.Server.Formatters /// public XmlOutputFormatter() { - SupportedMediaTypes.Add("text/xml"); + SupportedMediaTypes.Add(MediaTypeNames.Text.Xml); SupportedMediaTypes.Add("text/xml;charset=UTF-8"); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); From aee72aefb0c89fb9d173da27d2e97c97bb689822 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 11:46:40 -0600 Subject: [PATCH 052/102] Remove IHttpClient from StudiosImageProvider --- .../MediaBrowser.Providers.csproj | 3 +- .../Studios/StudiosImageProvider.cs | 30 +++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 7c0b542509..85966b3bf5 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -18,6 +18,7 @@ + @@ -27,7 +28,7 @@ netstandard2.1 false true - true + true diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index 25f8beb402..1fcd63d3f0 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -20,13 +20,13 @@ namespace MediaBrowser.Providers.Studios public class StudiosImageProvider : IRemoteImageProvider { private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; - public StudiosImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) + public StudiosImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IFileSystem fileSystem) { _config = config; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; } @@ -108,14 +108,14 @@ namespace MediaBrowser.Providers.Studios { const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studiothumbs.txt"; - return EnsureList(url, file, _httpClient, _fileSystem, cancellationToken); + return EnsureList(url, file, _fileSystem, cancellationToken); } private Task EnsurePosterList(string file, CancellationToken cancellationToken) { const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studioposters.txt"; - return EnsureList(url, file, _httpClient, _fileSystem, cancellationToken); + return EnsureList(url, file, _fileSystem, cancellationToken); } public int Order => 0; @@ -139,26 +139,18 @@ namespace MediaBrowser.Providers.Studios /// The file system. /// The cancellation token. /// Task. - public async Task EnsureList(string url, string file, IHttpClient httpClient, IFileSystem fileSystem, CancellationToken cancellationToken) + public async Task EnsureList(string url, string file, IFileSystem fileSystem, CancellationToken cancellationToken) { var fileInfo = fileSystem.GetFileInfo(file); if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1) { - Directory.CreateDirectory(Path.GetDirectoryName(file)); + var httpClient = _httpClientFactory.CreateClient(); - using (var res = await httpClient.SendAsync( - new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }, - HttpMethod.Get).ConfigureAwait(false)) - using (var content = res.Content) - using (var fileStream = new FileStream(file, FileMode.Create)) - { - await content.CopyToAsync(fileStream).ConfigureAwait(false); - } + Directory.CreateDirectory(Path.GetDirectoryName(file)); + await using var response = await httpClient.GetStreamAsync(url).ConfigureAwait(false); + await using var fileStream = new FileStream(file, FileMode.Create); + await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } return file; From f7dc124b5eaa7cf545d25afd991b1469c157f95b Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 11:50:07 -0600 Subject: [PATCH 053/102] Remove IHttpClient from ProviderManager --- .../Manager/ProviderManager.cs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index bbd7166e68..8e46c73eee 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Net.Mime; using System.Threading; using System.Threading.Tasks; @@ -44,7 +45,7 @@ namespace MediaBrowser.Providers.Manager { private readonly object _refreshQueueLock = new object(); private readonly ILogger _logger; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryMonitor _libraryMonitor; private readonly IFileSystem _fileSystem; private readonly IServerApplicationPaths _appPaths; @@ -66,7 +67,7 @@ namespace MediaBrowser.Providers.Manager /// /// Initializes a new instance of the class. /// - /// The Http client. + /// The Http client factory. /// The subtitle manager. /// The configuration manager. /// The library monitor. @@ -75,7 +76,7 @@ namespace MediaBrowser.Providers.Manager /// The server application paths. /// The library manager. public ProviderManager( - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ISubtitleManager subtitleManager, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, @@ -85,7 +86,7 @@ namespace MediaBrowser.Providers.Manager ILibraryManager libraryManager) { _logger = logger; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _configurationManager = configurationManager; _libraryMonitor = libraryMonitor; _fileSystem = fileSystem; @@ -155,25 +156,23 @@ namespace MediaBrowser.Providers.Manager /// public async Task SaveImage(BaseItem item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken) { - using var response = await _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false - }).ConfigureAwait(false); + var httpClient = _httpClientFactory.CreateClient(); + using var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); + + var contentType = response.Content.Headers.ContentType.MediaType; // Workaround for tvheadend channel icons // TODO: Isolate this hack into the tvh plugin - if (string.IsNullOrEmpty(response.ContentType)) + if (string.IsNullOrEmpty(contentType)) { if (url.IndexOf("/imagecache/", StringComparison.OrdinalIgnoreCase) != -1) { - response.ContentType = "image/png"; + contentType = "image/png"; } } // thetvdb will sometimes serve a rubbish 404 html page with a 200 OK code, because reasons... - if (response.ContentType.Equals(MediaTypeNames.Text.Html, StringComparison.OrdinalIgnoreCase)) + if (contentType.Equals(MediaTypeNames.Text.Html, StringComparison.OrdinalIgnoreCase)) { throw new HttpException("Invalid image received.") { @@ -181,7 +180,13 @@ namespace MediaBrowser.Providers.Manager }; } - await SaveImage(item, response.Content, response.ContentType, type, imageIndex, cancellationToken).ConfigureAwait(false); + await SaveImage( + item, + await response.Content.ReadAsStreamAsync().ConfigureAwait(false), + contentType, + type, + imageIndex, + cancellationToken).ConfigureAwait(false); } /// From 2e0c2336ccb2746ed1851285429013862f835809 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 11:51:53 -0600 Subject: [PATCH 054/102] Remove IHttpClient from IRemoteImageProvider --- MediaBrowser.Controller/Providers/IRemoteImageProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs index 68a968f901..ee8f5b860a 100644 --- a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -34,6 +35,6 @@ namespace MediaBrowser.Controller.Providers /// The URL. /// The cancellation token. /// Task{HttpResponseInfo}. - Task GetImageResponse(string url, CancellationToken cancellationToken); + Task GetImageResponse(string url, CancellationToken cancellationToken); } } From 7a0fa341625215720d4c74149d9d579a50b5ad48 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 11:52:09 -0600 Subject: [PATCH 055/102] Remove IHttpClient from StudiosImageProvider --- .../Studios/StudiosImageProvider.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index 1fcd63d3f0..1c3de1f1a8 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -120,14 +120,10 @@ namespace MediaBrowser.Providers.Studios public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false - }); + var httpClient = _httpClientFactory.CreateClient(); + return httpClient.GetAsync(url, cancellationToken); } /// @@ -135,7 +131,6 @@ namespace MediaBrowser.Providers.Studios /// /// The URL. /// The file. - /// The HTTP client. /// The file system. /// The cancellation token. /// Task. From 10ac32569e94acb48cce3a549700abc94bfa068f Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 11:54:55 -0600 Subject: [PATCH 056/102] Remove IHttpClient from ItemImageProvider --- .../Manager/ItemImageProvider.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 6cc3ca3691..47b8554dd8 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -465,9 +465,15 @@ namespace MediaBrowser.Providers.Manager try { - var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); + using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); - await _providerManager.SaveImage(item, response.Content, response.ContentType, type, null, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage( + item, + await response.Content.ReadAsStreamAsync().ConfigureAwait(false), + response.Content.Headers.ContentType.MediaType, + type, + null, + cancellationToken).ConfigureAwait(false); result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; return true; @@ -565,14 +571,14 @@ namespace MediaBrowser.Providers.Manager try { - var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); + using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); // If there's already an image of the same size, skip it - if (response.ContentLength.HasValue) + if (response.Content.Headers.ContentLength.HasValue) { try { - if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.ContentLength.Value)) + if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.Content.Headers.ContentLength.Value)) { response.Content.Dispose(); continue; @@ -584,7 +590,13 @@ namespace MediaBrowser.Providers.Manager } } - await _providerManager.SaveImage(item, response.Content, response.ContentType, imageType, null, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage( + item, + await response.Content.ReadAsStreamAsync().ConfigureAwait(false), + response.Content.Headers.ContentType.MediaType, + imageType, + null, + cancellationToken).ConfigureAwait(false); result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; } catch (HttpException ex) From 14f57dddcb6e99f317596e8ee6f04efd7ce292be Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 11:55:58 -0600 Subject: [PATCH 057/102] Remove IHttpClient from AlbumImageProvider --- .../Plugins/AudioDb/AlbumImageProvider.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index b211ed8b77..c9dac9ecd8 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -1,9 +1,9 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -17,13 +17,13 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class AudioDbAlbumImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; - public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IJsonSerializer json) + public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IJsonSerializer json) { _config = config; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _json = json; } @@ -94,13 +94,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + var httpClient = _httpClientFactory.CreateClient(); + return httpClient.GetAsync(url, cancellationToken); } /// From 188166d6ebfae60db17c7fdfddb8d74b9de66ce2 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 11:56:49 -0600 Subject: [PATCH 058/102] Remove IHttpClient from ArtistImageProvider --- .../Plugins/AudioDb/ArtistImageProvider.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index 243b62f7bb..6512668681 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -1,9 +1,9 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -17,14 +17,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class AudioDbArtistImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; - public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClient httpClient) + public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClientFactory httpClientFactory) { _config = config; _json = json; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } /// @@ -135,13 +135,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb return list; } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + var httpClient = _httpClientFactory.CreateClient(); + return httpClient.GetAsync(url, cancellationToken); } /// From 1325815182a9471b453da5abbfdaec68cac4feb2 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 13:10:02 -0600 Subject: [PATCH 059/102] Remove IHttpClient from Providers --- .../Controllers/ItemLookupController.cs | 6 +- .../Providers/IProviderManager.cs | 3 +- .../Providers/IRemoteSearchProvider.cs | 3 +- .../Manager/ProviderManager.cs | 2 +- .../MediaInfo/SubtitleResolver.cs | 1 - .../Plugins/AudioDb/AlbumProvider.cs | 25 +- .../Plugins/AudioDb/ArtistProvider.cs | 31 +-- .../Plugins/MusicBrainz/AlbumProvider.cs | 213 ++++++++---------- .../Plugins/MusicBrainz/ArtistProvider.cs | 24 +- .../Plugins/Omdb/OmdbEpisodeProvider.cs | 14 +- .../Plugins/Omdb/OmdbImageProvider.cs | 18 +- .../Plugins/Omdb/OmdbItemProvider.cs | 130 +++++------ .../Plugins/Omdb/OmdbProvider.cs | 45 ++-- .../TheTvdb/TvdbEpisodeImageProvider.cs | 16 +- .../Plugins/TheTvdb/TvdbEpisodeProvider.cs | 16 +- .../TheTvdb/TvdbPersonImageProvider.cs | 16 +- .../TheTvdb/TvdbSeasonImageProvider.cs | 16 +- .../TheTvdb/TvdbSeriesImageProvider.cs | 16 +- .../Plugins/TheTvdb/TvdbSeriesProvider.cs | 17 +- .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 16 +- .../Tmdb/BoxSets/TmdbBoxSetProvider.cs | 52 ++--- .../Plugins/Tmdb/Movies/TmdbImageProvider.cs | 16 +- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 103 +++------ .../Plugins/Tmdb/Movies/TmdbSearch.cs | 126 +++++------ .../Tmdb/Music/TmdbMusicVideoProvider.cs | 4 +- .../Tmdb/People/TmdbPersonImageProvider.cs | 16 +- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 61 ++--- .../Tmdb/TV/TmdbEpisodeImageProvider.cs | 8 +- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 8 +- .../Tmdb/TV/TmdbEpisodeProviderBase.cs | 34 +-- .../Tmdb/TV/TmdbSeasonImageProvider.cs | 16 +- .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 34 +-- .../Tmdb/TV/TmdbSeriesImageProvider.cs | 16 +- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 109 ++++----- .../Tmdb/Trailers/TmdbTrailerProvider.cs | 16 +- .../Studios/StudiosImageProvider.cs | 1 - 36 files changed, 483 insertions(+), 765 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index c9ad15babe..a752af5454 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -331,12 +331,12 @@ namespace Jellyfin.Api.Controllers /// Task. private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath) { - var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false); - var ext = result.ContentType.Split('/').Last(); + using var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false); + var ext = result.Content.Headers.ContentType.MediaType.Split('/').LastOrDefault(); var fullCachePath = GetFullCachePath(urlHash + "." + ext); Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); - await using (var stream = result.Content) + using (var stream = result.Content) { await using var fileStream = new FileStream( fullCachePath, diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 955db0278c..8ba01d7730 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; @@ -157,7 +158,7 @@ namespace MediaBrowser.Controller.Providers /// The URL. /// The cancellation token. /// Task{HttpResponseInfo}. - Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken); + Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken); Dictionary GetRefreshQueue(); diff --git a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs index fdb0c8eb56..17ad9e4a3a 100644 --- a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs @@ -1,3 +1,4 @@ +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -12,6 +13,6 @@ namespace MediaBrowser.Controller.Providers /// The URL. /// The cancellation token. /// Task{HttpResponseInfo}. - Task GetImageResponse(string url, CancellationToken cancellationToken); + Task GetImageResponse(string url, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 8e46c73eee..34d3a8a0f3 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -893,7 +893,7 @@ namespace MediaBrowser.Providers.Manager } /// - public Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken) + public Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken) { var provider = _metadataProviders.OfType().FirstOrDefault(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 64a5e7c8eb..43659b68cc 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -8,7 +8,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.MediaInfo { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 7e54fcbdda..321144edf9 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; @@ -26,16 +25,16 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; public static AudioDbAlbumProvider Current; - public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json) + public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json) { _config = config; _fileSystem = fileSystem; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _json = json; Current = this; @@ -174,18 +173,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var httpResponse = await _httpClient.SendAsync( - new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken - }, - HttpMethod.Get).ConfigureAwait(false)) - using (var response = httpResponse.Content) - using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) - { - await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); - } + using var response = await _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false); } private static string GetAlbumDataPath(IApplicationPaths appPaths, string musicBrainzReleaseGroupId) @@ -294,7 +285,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index 892f734228..708426500b 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; @@ -25,7 +24,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; public static AudioDbArtistProvider Current; @@ -33,11 +32,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private const string ApiKey = "195003"; public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey; - public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json) + public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json) { _config = config; _fileSystem = fileSystem; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _json = json; Current = this; } @@ -155,23 +154,13 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); - using (var httpResponse = await _httpClient.SendAsync( - new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = true - }, - HttpMethod.Get).ConfigureAwait(false)) - using (var response = httpResponse.Content) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); + using var response = await _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) - { - await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); - } - } + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false); } /// @@ -289,7 +278,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs index 9f36a03f92..23acb7fd6a 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs @@ -8,12 +8,12 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using MediaBrowser.Common; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.Music internal static MusicBrainzAlbumProvider Current; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IApplicationHost _appHost; private readonly ILogger _logger; @@ -51,11 +51,11 @@ namespace MediaBrowser.Providers.Music private Stopwatch _stopWatchMusicBrainz = new Stopwatch(); public MusicBrainzAlbumProvider( - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, IApplicationHost appHost, ILogger logger) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _appHost = appHost; _logger = logger; @@ -123,11 +123,9 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrWhiteSpace(url)) { - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - { - return GetResultsFromResponse(stream); - } + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return GetResultsFromResponse(stream); } return Enumerable.Empty(); @@ -282,23 +280,19 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), artistId); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - using (var reader = XmlReader.Create(oReader, settings)) - { - return ReleaseResult.Parse(reader).FirstOrDefault(); - } - } + using var reader = XmlReader.Create(oReader, settings); + return ReleaseResult.Parse(reader).FirstOrDefault(); } private async Task GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken) @@ -309,23 +303,19 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), WebUtility.UrlEncode(artistName)); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings() { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - using (var reader = XmlReader.Create(oReader, settings)) - { - return ReleaseResult.Parse(reader).FirstOrDefault(); - } - } + using var reader = XmlReader.Create(oReader, settings); + return ReleaseResult.Parse(reader).FirstOrDefault(); } private class ReleaseResult @@ -624,30 +614,21 @@ namespace MediaBrowser.Providers.Music { var url = "/ws/2/release?release-group=" + releaseGroupId.ToString(CultureInfo.InvariantCulture); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; - - using (var reader = XmlReader.Create(oReader, settings)) - { - var result = ReleaseResult.Parse(reader).FirstOrDefault(); + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - if (result != null) - { - return result.ReleaseId; - } - } - } + using var reader = XmlReader.Create(oReader, settings); + var result = ReleaseResult.Parse(reader).FirstOrDefault(); - return null; + return result?.ReleaseId; } /// @@ -660,59 +641,57 @@ namespace MediaBrowser.Providers.Music { var url = "/ws/2/release-group/?query=reid:" + releaseEntryId.ToString(CultureInfo.InvariantCulture); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - using (var reader = XmlReader.Create(oReader, settings)) - { - reader.MoveToContent(); - reader.Read(); + using (var reader = XmlReader.Create(oReader, settings)) + { + reader.MoveToContent(); + reader.Read(); - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) { - if (reader.NodeType == XmlNodeType.Element) + switch (reader.Name) { - switch (reader.Name) + case "release-group-list": { - case "release-group-list": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } - using (var subReader = reader.ReadSubtree()) - { - return GetFirstReleaseGroupId(subReader); - } - } + using (var subReader = reader.ReadSubtree()) + { + return GetFirstReleaseGroupId(subReader); + } + } - default: - { - reader.Skip(); - break; - } + default: + { + reader.Skip(); + break; } } - else - { - reader.Read(); - } } - - return null; + else + { + reader.Read(); + } } + + return null; } } @@ -755,23 +734,19 @@ namespace MediaBrowser.Providers.Music /// A number of retries shall be made in order to try and satisfy the request before /// giving up and returning null. /// - internal async Task GetMusicBrainzResponse(string url, CancellationToken cancellationToken) + internal async Task GetMusicBrainzResponse(string url, CancellationToken cancellationToken) { - var options = new HttpRequestOptions - { - Url = _musicBrainzBaseUrl.TrimEnd('/') + url, - CancellationToken = cancellationToken, - // MusicBrainz request a contact email address is supplied, as comment, in user agent field: - // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent - UserAgent = string.Format( - CultureInfo.InvariantCulture, - "{0} ( {1} )", - _appHost.ApplicationUserAgent, - _appHost.ApplicationUserAgentAddress), - BufferContent = false - }; + using var options = new HttpRequestMessage(HttpMethod.Get, _musicBrainzBaseUrl.TrimEnd('/') + url); + + // MusicBrainz request a contact email address is supplied, as comment, in user agent field: + // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent + options.Headers.UserAgent.Add(new ProductInfoHeaderValue(string.Format( + CultureInfo.InvariantCulture, + "{0} ( {1} )", + _appHost.ApplicationUserAgent, + _appHost.ApplicationUserAgentAddress))); - HttpResponseInfo response; + HttpResponseMessage response; var attempts = 0u; do @@ -790,7 +765,7 @@ namespace MediaBrowser.Providers.Music _logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds); _stopWatchMusicBrainz.Restart(); - response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false); + response = await _httpClientFactory.CreateClient().SendAsync(options).ConfigureAwait(false); // We retry a finite number of times, and only whilst MB is indicating 503 (throttling) } @@ -799,14 +774,14 @@ namespace MediaBrowser.Providers.Music // Log error if unable to query MB database due to throttling if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable) { - _logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.Url); + _logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.RequestUri); } return response; } /// - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs index 9557664039..b829ed3780 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs @@ -6,11 +6,11 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; @@ -37,11 +37,9 @@ namespace MediaBrowser.Providers.Music { var url = "/ws/2/artist/?query=arid:{0}" + musicBrainzId.ToString(CultureInfo.InvariantCulture); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - { - return GetResultsFromResponse(stream); - } + using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return GetResultsFromResponse(stream); } else { @@ -51,7 +49,7 @@ namespace MediaBrowser.Providers.Music var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch)); using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) + await using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { var results = GetResultsFromResponse(stream).ToList(); @@ -66,13 +64,9 @@ namespace MediaBrowser.Providers.Music // Try again using the search with accent characters url url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - { - using (var stream = response.Content) - { - return GetResultsFromResponse(stream); - } - } + using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return GetResultsFromResponse(stream); } } @@ -298,7 +292,7 @@ namespace MediaBrowser.Providers.Music public string Name => "MusicBrainz"; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs index 50d6b78ae9..bfc840ea59 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs @@ -1,10 +1,10 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb public class OmdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly OmdbItemProvider _itemProvider; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; @@ -28,17 +28,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb public OmdbEpisodeProvider( IJsonSerializer jsonSerializer, IApplicationHost appHost, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; - _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClient, libraryManager, fileSystem, configurationManager); + _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClientFactory, libraryManager, fileSystem, configurationManager); } // After TheTvDb @@ -69,7 +69,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue) { - result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager) + result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager) .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } } @@ -77,7 +77,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb return result; } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _itemProvider.GetImageResponse(url, cancellationToken); } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs index 2d09a66c35..41e664aace 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs @@ -1,10 +1,10 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -19,16 +19,16 @@ namespace MediaBrowser.Providers.Plugins.Omdb { public class OmdbImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; - public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager) + public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var list = new List(); - var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager); + var provider = new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager); if (!string.IsNullOrWhiteSpace(imdbId)) { @@ -79,13 +79,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb return list; } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } public string Name => "The Open Movie Database"; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 12aecba849..d2823a08c3 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -5,10 +5,10 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb IRemoteMetadataProvider, IRemoteMetadataProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; @@ -35,13 +35,13 @@ namespace MediaBrowser.Providers.Plugins.Omdb public OmdbItemProvider( IJsonSerializer jsonSerializer, IApplicationHost appHost, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; _fileSystem = fileSystem; _configurationManager = configurationManager; @@ -129,67 +129,63 @@ namespace MediaBrowser.Providers.Plugins.Omdb var url = OmdbProvider.GetOmdbUrl(urlQuery, _appHost, cancellationToken); - using (var response = await OmdbProvider.GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) + using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var resultList = new List(); + + if (isSearch) { - using (var stream = response.Content) + var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + if (searchResultList != null && searchResultList.Search != null) { - var resultList = new List(); - - if (isSearch) - { - var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - if (searchResultList != null && searchResultList.Search != null) - { - resultList.AddRange(searchResultList.Search); - } - } - else - { - var result = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase)) - { - resultList.Add(result); - } - } - - return resultList.Select(result => - { - var item = new RemoteSearchResult - { - IndexNumber = searchInfo.IndexNumber, - Name = result.Title, - ParentIndexNumber = searchInfo.ParentIndexNumber, - SearchProviderName = Name - }; - - if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue) - { - item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value; - } - - item.SetProviderId(MetadataProvider.Imdb, result.imdbID); - - if (result.Year.Length > 0 - && int.TryParse(result.Year.AsSpan().Slice(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear)) - { - item.ProductionYear = parsedYear; - } - - if (!string.IsNullOrEmpty(result.Released) - && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released)) - { - item.PremiereDate = released; - } - - if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase)) - { - item.ImageUrl = result.Poster; - } - - return item; - }); + resultList.AddRange(searchResultList.Search); } } + else + { + var result = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase)) + { + resultList.Add(result); + } + } + + return resultList.Select(result => + { + var item = new RemoteSearchResult + { + IndexNumber = searchInfo.IndexNumber, + Name = result.Title, + ParentIndexNumber = searchInfo.ParentIndexNumber, + SearchProviderName = Name + }; + + if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue) + { + item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value; + } + + item.SetProviderId(MetadataProvider.Imdb, result.imdbID); + + if (result.Year.Length > 0 + && int.TryParse(result.Year.AsSpan().Slice(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear)) + { + item.ProductionYear = parsedYear; + } + + if (!string.IsNullOrEmpty(result.Released) + && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released)) + { + item.PremiereDate = released; + } + + if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase)) + { + item.ImageUrl = result.Poster; + } + + return item; + }); } public Task> GetMetadata(TrailerInfo info, CancellationToken cancellationToken) @@ -224,7 +220,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; @@ -256,7 +252,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; @@ -276,13 +272,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb return first == null ? null : first.GetProviderId(MetadataProvider.Imdb); } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } class SearchResult diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 13098d1405..6ad5298deb 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -10,7 +10,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; @@ -25,14 +24,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IApplicationHost _appHost; - public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager) + public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; @@ -293,15 +292,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), _appHost, cancellationToken); - using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) - { - using (var stream = response.Content) - { - var rootObject = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(rootObject, path); - } - } + using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var rootObject = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + _jsonSerializer.SerializeToFile(rootObject, path); return path; } @@ -330,28 +325,18 @@ namespace MediaBrowser.Providers.Plugins.Omdb var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), _appHost, cancellationToken); - using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) - { - using (var stream = response.Content) - { - var rootObject = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(rootObject, path); - } - } + using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var rootObject = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + _jsonSerializer.SerializeToFile(rootObject, path); return path; } - public static Task GetOmdbResponse(IHttpClient httpClient, string url, CancellationToken cancellationToken) + public static Task GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) { - return httpClient.SendAsync(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = true, - EnableDefaultUserAgent = true - }, HttpMethod.Get); + return httpClient.GetAsync(url, cancellationToken); } internal string GetDataFilePath(string imdbId) diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs index 9b87e36176..4d38d38dca 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; @@ -18,13 +18,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbEpisodeImageProvider : IRemoteImageProvider { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger logger, TvdbClientManager tvdbClientManager) + public TvdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, ILogger logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -113,13 +113,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs index 52fc538728..90436c7c99 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; @@ -21,13 +21,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb /// public class TvdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbEpisodeProvider(IHttpClient httpClient, ILogger logger, TvdbClientManager tvdbClientManager) + public TvdbEpisodeProvider(IHttpClientFactory httpClientFactory, ILogger logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -242,13 +242,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb return result; } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } public int Order => 0; diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs index 9db21f0128..388a4e3e72 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -20,15 +20,15 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbPersonImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly TvdbClientManager _tvdbClientManager; - public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger logger, TvdbClientManager tvdbClientManager) + public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClientFactory httpClientFactory, ILogger logger, TvdbClientManager tvdbClientManager) { _libraryManager = libraryManager; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -104,13 +104,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb } /// - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs index e9ba204754..ff8c3455fa 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; @@ -20,13 +20,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger logger, TvdbClientManager tvdbClientManager) + public TvdbSeasonImageProvider(IHttpClientFactory httpClientFactory, ILogger logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -146,13 +146,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs index c33aefbc19..d287828eb5 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -20,13 +20,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger logger, TvdbClientManager tvdbClientManager) + public TvdbSeriesImageProvider(IHttpClientFactory httpClientFactory, ILogger logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -144,13 +144,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs index df48629d03..c6dd8a5f3d 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs @@ -3,11 +3,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -25,15 +25,15 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { internal static TvdbSeriesProvider Current { get; private set; } - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localizationManager; private readonly TvdbClientManager _tvdbClientManager; - public TvdbSeriesProvider(IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvdbClientManager tvdbClientManager) + public TvdbSeriesProvider(IHttpClientFactory httpClientFactory, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _libraryManager = libraryManager; _localizationManager = localizationManager; @@ -408,14 +408,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index c41bd925e9..f434440283 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; @@ -20,11 +20,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { public class TmdbBoxSetImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbBoxSetImageProvider(IHttpClient httpClient) + public TmdbBoxSetImageProvider(IHttpClientFactory httpClientFactory) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } public string Name => ProviderName; @@ -153,13 +153,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index 20b6cd505c..a5f823f7c6 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -5,10 +5,11 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; @@ -36,7 +37,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; public TmdbBoxSetProvider( @@ -45,7 +46,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets IServerConfigurationManager config, IFileSystem fileSystem, ILocalizationManager localization, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager) { _logger = logger; @@ -53,7 +54,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets _config = config; _fileSystem = fileSystem; _localization = localization; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; Current = this; } @@ -187,20 +188,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets cancellationToken.ThrowIfCancellationRequested(); - CollectionResult mainResult; - - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - mainResult = await _json.DeserializeFromStreamAsync(json).ConfigureAwait(false); - } - } + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var mainResult = await _json.DeserializeFromStreamAsync(stream).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); @@ -216,18 +208,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); } - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - mainResult = await _json.DeserializeFromStreamAsync(json).ConfigureAwait(false); - } - } + using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + await using var langStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + mainResult = await _json.DeserializeFromStreamAsync(langStream).ConfigureAwait(false); } } @@ -277,13 +261,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets return dataPath; } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs index 36a06fba79..60d7a599ea 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; @@ -23,13 +23,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies public class TmdbImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; - public TmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) + public TmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; } @@ -202,13 +202,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 27ab6756f1..f078153f69 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -6,11 +6,11 @@ using System.Globalization; using System.IO; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -18,7 +18,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Plugins.Tmdb.Models.Movies; @@ -34,7 +33,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies internal static TmdbMovieProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly ILogger _logger; @@ -45,7 +44,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies public TmdbMovieProvider( IJsonSerializer jsonSerializer, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, @@ -53,7 +52,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies IApplicationHost appHost) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _logger = logger; @@ -146,20 +145,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies return _tmdbSettings; } - using (HttpResponseInfo response = await GetMovieDbResponse(new HttpRequestOptions - { - Url = string.Format(CultureInfo.InvariantCulture, TmdbConfigUrl, TmdbUtils.ApiKey), - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (Stream json = response.Content) - { - _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); - - return _tmdbSettings; - } - } + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, string.Format(CultureInfo.InvariantCulture, TmdbConfigUrl, TmdbUtils.ApiKey)); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await GetMovieDbResponse(requestMessage).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + return _tmdbSettings; } private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}"; @@ -331,41 +322,18 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies url += "&include_image_language=" + GetImageLanguagesParam(language); } - MovieResult mainResult; - cancellationToken.ThrowIfCancellationRequested(); - // Cache if not using a tmdbId because we won't have the tmdb cache directory structure. So use the lower level cache. - var cacheMode = isTmdbId ? CacheMode.None : CacheMode.Unconditional; - var cacheLength = TimeSpan.FromDays(3); - - try + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var mainResponse = await GetMovieDbResponse(requestMessage); + if (mainResponse.StatusCode == HttpStatusCode.NotFound) { - using (var response = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader, - CacheMode = cacheMode, - CacheLength = cacheLength - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - mainResult = await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); - } - } + return null; } - catch (HttpException ex) - { - // Return null so that callers know there is no metadata for this id - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) - { - return null; - } - throw; - } + await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var mainResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); @@ -385,22 +353,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies url += "&include_image_language=" + GetImageLanguagesParam(language); } - using (var response = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader, - CacheMode = cacheMode, - CacheLength = cacheLength - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var englishResult = await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); + using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var langResponse = await GetMovieDbResponse(langRequestMessage); - mainResult.Overview = englishResult.Overview; - } - } + await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var langResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + mainResult.Overview = langResult.Overview; } return mainResult; @@ -409,25 +368,19 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// /// Gets the movie db response. /// - internal async Task GetMovieDbResponse(HttpRequestOptions options) + internal async Task GetMovieDbResponse(HttpRequestMessage message) { - options.BufferContent = true; - options.UserAgent = _appHost.ApplicationUserAgent; - - return await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false); + message.Headers.UserAgent.Add(new ProductInfoHeaderValue(_appHost.ApplicationUserAgent)); + return await _httpClientFactory.CreateClient().SendAsync(message); } /// public int Order => 1; /// - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs index 10935c6555..d5dfca98a4 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs @@ -5,10 +5,11 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -168,47 +169,34 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url3, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader + var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var searchResults = await _json.DeserializeFromStreamAsync>(json).ConfigureAwait(false); + var results = searchResults.Results ?? new List(); - var results = searchResults.Results ?? new List(); + return results + .Select(i => + { + var remoteResult = new RemoteSearchResult {SearchProviderName = TmdbMovieProvider.Current.Name, Name = i.Title ?? i.Name ?? i.Original_Title, ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path}; - return results - .Select(i => + if (!string.IsNullOrWhiteSpace(i.Release_Date)) + { + // These dates are always in this exact format + if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) { - var remoteResult = new RemoteSearchResult - { - SearchProviderName = TmdbMovieProvider.Current.Name, - Name = i.Title ?? i.Name ?? i.Original_Title, - ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path - }; - - if (!string.IsNullOrWhiteSpace(i.Release_Date)) - { - // These dates are always in this exact format - if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) - { - remoteResult.PremiereDate = r.ToUniversalTime(); - remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; - } - } - - remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); - - return remoteResult; - }) - .ToList(); - } - } + remoteResult.PremiereDate = r.ToUniversalTime(); + remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; + } + } + + remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); + + return remoteResult; + }) + .ToList(); } private async Task> GetSearchResultsTv(string name, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) @@ -220,46 +208,34 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv"); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url3, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var searchResults = await _json.DeserializeFromStreamAsync>(json).ConfigureAwait(false); + var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); - var results = searchResults.Results ?? new List(); + var results = searchResults.Results ?? new List(); - return results - .Select(i => + return results + .Select(i => + { + var remoteResult = new RemoteSearchResult {SearchProviderName = TmdbMovieProvider.Current.Name, Name = i.Name ?? i.Original_Name, ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path}; + + if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) + { + // These dates are always in this exact format + if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) { - var remoteResult = new RemoteSearchResult - { - SearchProviderName = TmdbMovieProvider.Current.Name, - Name = i.Name ?? i.Original_Name, - ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path - }; - - if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) - { - // These dates are always in this exact format - if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) - { - remoteResult.PremiereDate = r.ToUniversalTime(); - remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; - } - } - - remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); - - return remoteResult; - }) - .ToList(); - } - } + remoteResult.PremiereDate = r.ToUniversalTime(); + remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; + } + } + + remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); + + return remoteResult; + }) + .ToList(); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs index d4264dd4ea..73e49ba5bf 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; @@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Music public string Name => TmdbMovieProvider.Current.Name; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index 2faa9f835b..f31a7faea2 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; @@ -22,13 +22,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People { private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClient httpClient) + public TmdbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory) { _config = config; _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } public string Name => ProviderName; @@ -127,13 +127,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 58cbf9eef6..97482f34a9 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -6,11 +6,12 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; @@ -36,20 +37,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; public TmdbPersonProvider( IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILogger logger) { _fileSystem = fileSystem; _configurationManager = configurationManager; _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; Current = this; } @@ -96,22 +97,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), TmdbUtils.ApiKey); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var result = await _jsonSerializer.DeserializeFromStreamAsync>(json).ConfigureAwait(false) ?? - new TmdbSearchResult(); + var result2 = await _jsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false) + ?? new TmdbSearchResult(); - return result.Results.Select(i => GetSearchResult(i, tmdbImageUrl)); - } - } + return result2.Results.Select(i => GetSearchResult(i, tmdbImageUrl)); } private RemoteSearchResult GetSearchResult(PersonSearchResult i, string baseImageUrl) @@ -230,23 +224,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", TmdbUtils.ApiKey, id); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - - using (var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) - { - await json.CopyToAsync(fs).ConfigureAwait(false); - } - } - } + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + await using var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await response.Content.CopyToAsync(fs).ConfigureAwait(false); } private static string GetPersonDataPath(IApplicationPaths appPaths, string tmdbId) @@ -266,13 +249,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People return Path.Combine(appPaths.CachePath, "tmdb-people"); } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 77e4b2c568..eebecdac67 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; @@ -26,8 +26,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV IRemoteImageProvider, IHasOrder { - public TmdbEpisodeImageProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) - : base(httpClient, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) + public TmdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + : base(httpClientFactory, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) { } public IEnumerable GetSupportedImages(BaseItem item) @@ -115,7 +115,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return images.Stills ?? new List(); } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { return GetResponse(url, cancellationToken); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index a07ceb40ec..b4aef4542d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -5,9 +5,9 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -27,8 +27,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV IRemoteMetadataProvider, IHasOrder { - public TmdbEpisodeProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) - : base(httpClient, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) + public TmdbEpisodeProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + : base(httpClientFactory, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) { } public async Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) @@ -201,7 +201,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return result; } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { return GetResponse(url, cancellationToken); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs index 846e6095b5..5ccccc04f2 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs @@ -3,9 +3,10 @@ using System; using System.Globalization; using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; @@ -19,16 +20,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public abstract class TmdbEpisodeProviderBase { private const string EpisodeUrlPattern = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos"; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; private readonly ILogger _logger; - protected TmdbEpisodeProviderBase(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + protected TmdbEpisodeProviderBase(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _configurationManager = configurationManager; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; @@ -124,27 +125,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - return await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); - } - } + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); } - protected Task GetResponse(string url, CancellationToken cancellationToken) + protected Task GetResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index 56b6e44833..787514d05d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; @@ -22,12 +22,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public class TmdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient) + public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } public int Order => 1; @@ -36,13 +36,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public static string ProviderName => TmdbUtils.ProviderName; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 822dc4317f..247b794b16 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -5,9 +5,10 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -26,7 +27,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public class TmdbSeasonProvider : IRemoteMetadataProvider { private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos"; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; @@ -35,9 +36,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV internal static TmdbSeasonProvider Current { get; private set; } - public TmdbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILogger logger) + public TmdbSeasonProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILogger logger) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _configurationManager = configurationManager; _fileSystem = fileSystem; _localization = localization; @@ -121,13 +122,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return Task.FromResult>(new List()); } - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } private async Task GetSeasonInfo(string seriesTmdbId, int season, string preferredMetadataLanguage, @@ -215,18 +212,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - return await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); - } - } + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index 95c451493c..f11eeb15b3 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; @@ -23,13 +23,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public class TmdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; - public TmdbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) + public TmdbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; } @@ -180,13 +180,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // After tvdb and fanart public int Order => 2; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 3480a15f64..6679f0684e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -5,10 +5,11 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -35,7 +36,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV private readonly IServerConfigurationManager _configurationManager; private readonly ILogger _logger; private readonly ILocalizationManager _localization; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); @@ -48,7 +49,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager) { _jsonSerializer = jsonSerializer; @@ -56,7 +57,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV _configurationManager = configurationManager; _logger = logger; _localization = localization; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; Current = this; } @@ -413,24 +414,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); - SeriesResult mainResult; + using var mainRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage); + await using var mainStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var mainResult = await _jsonSerializer.DeserializeFromStreamAsync(mainStream).ConfigureAwait(false); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) + if (!string.IsNullOrEmpty(language)) { - using (var json = response.Content) - { - mainResult = await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); - - if (!string.IsNullOrEmpty(language)) - { - mainResult.ResultLanguage = language; - } - } + mainResult.ResultLanguage = language; } cancellationToken.ThrowIfCancellationRequested(); @@ -451,21 +443,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); } - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var englishResult = await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var englishResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - mainResult.Overview = englishResult.Overview; - mainResult.ResultLanguage = "en"; - } - } + mainResult.Overview = englishResult.Overview; + mainResult.ResultLanguage = "en"; } return mainResult; @@ -515,38 +500,34 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV TmdbUtils.ApiKey, externalSource); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - }).ConfigureAwait(false)) + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + + var result = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + + if (result != null && result.Tv_Results != null) { - using (var json = response.Content) + var tv = result.Tv_Results.FirstOrDefault(); + + if (tv != null) { - var result = await _jsonSerializer.DeserializeFromStreamAsync(json).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - if (result != null && result.Tv_Results != null) + var remoteResult = new RemoteSearchResult { - var tv = result.Tv_Results.FirstOrDefault(); - - if (tv != null) - { - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - var remoteResult = new RemoteSearchResult - { - Name = tv.Name, - SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(tv.Poster_Path) ? null : tmdbImageUrl + tv.Poster_Path - }; + Name = tv.Name, + SearchProviderName = Name, + ImageUrl = string.IsNullOrWhiteSpace(tv.Poster_Path) + ? null + : tmdbImageUrl + tv.Poster_Path + }; - remoteResult.SetProviderId(MetadataProvider.Tmdb, tv.Id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProvider.Tmdb, tv.Id.ToString(_usCulture)); - return remoteResult; - } - } + return remoteResult; } } @@ -556,13 +537,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // After TheTVDB public int Order => 1; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs index 7e2b062574..10374bde9e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs @@ -1,9 +1,9 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; @@ -13,11 +13,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Trailers { public class TmdbTrailerProvider : IHasOrder, IRemoteMetadataProvider { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbTrailerProvider(IHttpClient httpClient) + public TmdbTrailerProvider(IHttpClientFactory httpClientFactory) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } public Task> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken) @@ -34,13 +34,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Trailers public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index 1c3de1f1a8..dad155c815 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; From 767c73e5c12fab004bb7de30bd1173295ee2d111 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 13:22:42 -0600 Subject: [PATCH 060/102] fix usings --- Jellyfin.Api/Controllers/LiveTvController.cs | 4 ++-- Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 2 +- MediaBrowser.Providers/Manager/ItemImageProvider.cs | 6 ++++-- MediaBrowser.Providers/Manager/ProviderManager.cs | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 9d8ec9f75c..b2a9393a00 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1065,13 +1065,13 @@ namespace Jellyfin.Api.Controllers /// Available countries returned. /// A containing the available countries. [HttpGet("ListingProviders/SchedulesDirect/Countries")] - [Authorize(Policy = Policies.DefaultAuthorization)] + // [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetSchedulesDirectCountries() { var client = _httpClientFactory.CreateClient(); // https://json.schedulesdirect.org/20141201/available/countries - using var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries") + var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries") .ConfigureAwait(false); return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json); diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index a463783e00..ca62d4267a 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -36,7 +36,7 @@ namespace Jellyfin.Api.Helpers httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, useragent); } - using var response = await httpClient.GetAsync(state.MediaPath).ConfigureAwait(false); + var response = await httpClient.GetAsync(state.MediaPath).ConfigureAwait(false); var contentType = response.Content.Headers.ContentType.ToString(); controller.Response.Headers[HeaderNames.AcceptRanges] = "none"; diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 47b8554dd8..a5eb095c46 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -466,10 +466,11 @@ namespace MediaBrowser.Providers.Manager try { using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); await _providerManager.SaveImage( item, - await response.Content.ReadAsStreamAsync().ConfigureAwait(false), + stream, response.Content.Headers.ContentType.MediaType, type, null, @@ -590,9 +591,10 @@ namespace MediaBrowser.Providers.Manager } } + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); await _providerManager.SaveImage( item, - await response.Content.ReadAsStreamAsync().ConfigureAwait(false), + stream, response.Content.Headers.ContentType.MediaType, imageType, null, diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 34d3a8a0f3..e67d1b8c37 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -180,9 +180,10 @@ namespace MediaBrowser.Providers.Manager }; } + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); await SaveImage( item, - await response.Content.ReadAsStreamAsync().ConfigureAwait(false), + stream, contentType, type, imageIndex, From 898ccab222a5224ae54c3ac6af1a5195c6181fc4 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 13:41:16 -0600 Subject: [PATCH 061/102] oop --- Jellyfin.Api/Controllers/LiveTvController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index b2a9393a00..2838361d70 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1065,7 +1065,7 @@ namespace Jellyfin.Api.Controllers /// Available countries returned. /// A containing the available countries. [HttpGet("ListingProviders/SchedulesDirect/Countries")] - // [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetSchedulesDirectCountries() { From 01ed5d54071d160d7db780b2e212112cc7644925 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 13:59:29 -0600 Subject: [PATCH 062/102] Fix adding accept headers --- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 12 ++++++++++-- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 18 +++++++++++++++--- .../Plugins/Tmdb/Movies/TmdbSearch.cs | 12 ++++++++++-- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 12 ++++++++++-- .../Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs | 6 +++++- .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 6 +++++- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 18 +++++++++++++++--- .../Plugins/Tmdb/TmdbUtils.cs | 3 ++- 8 files changed, 72 insertions(+), 15 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index a5f823f7c6..4f86a0293a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -189,7 +189,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets cancellationToken.ThrowIfCancellationRequested(); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); var mainResult = await _json.DeserializeFromStreamAsync(stream).ConfigureAwait(false); @@ -209,7 +213,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets } using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + await using var langStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); mainResult = await _json.DeserializeFromStreamAsync(langStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index f078153f69..685561e2dc 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -146,7 +146,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies } using var requestMessage = new HttpRequestMessage(HttpMethod.Get, string.Format(CultureInfo.InvariantCulture, TmdbConfigUrl, TmdbUtils.ApiKey)); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await GetMovieDbResponse(requestMessage).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); @@ -325,7 +329,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies cancellationToken.ThrowIfCancellationRequested(); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var mainResponse = await GetMovieDbResponse(requestMessage); if (mainResponse.StatusCode == HttpStatusCode.NotFound) { @@ -354,7 +362,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies } using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var langResponse = await GetMovieDbResponse(langRequestMessage); await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs index d5dfca98a4..48f2a68a63 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs @@ -170,7 +170,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type); var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); @@ -209,7 +213,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv"); var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 97482f34a9..e9fb5c7034 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -98,7 +98,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), TmdbUtils.ApiKey); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); @@ -225,7 +229,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", TmdbUtils.ApiKey, id); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); await using var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs index 5ccccc04f2..154664321f 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs @@ -126,7 +126,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 247b794b16..2b9077f557 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -213,7 +213,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 6679f0684e..ac577b125a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -415,7 +415,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); using var mainRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage); await using var mainStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); var mainResult = await _jsonSerializer.DeserializeFromStreamAsync(mainStream).ConfigureAwait(false); @@ -444,7 +448,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var englishResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); @@ -501,7 +509,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV externalSource); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(TmdbUtils.AcceptHeader)); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index 2f1e8b791a..1415d69761 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Mime; using MediaBrowser.Model.Entities; using MediaBrowser.Providers.Plugins.Tmdb.Models.General; @@ -32,7 +33,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// /// Value of the Accept header for requests to the provider. /// - public const string AcceptHeader = "application/json,image/*"; + public static readonly string[] AcceptHeaders = { MediaTypeNames.Application.Json, "image/*" }; /// /// Maps the TMDB provided roles for crew members to Jellyfin roles. From 9c6b4f033ab92dcc042d1a6a7016d8b9a2005bf5 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 14:55:14 -0600 Subject: [PATCH 063/102] revert lastordefault --- Jellyfin.Api/Controllers/ItemLookupController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index a752af5454..2857d6c8d4 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -332,7 +332,7 @@ namespace Jellyfin.Api.Controllers private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath) { using var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false); - var ext = result.Content.Headers.ContentType.MediaType.Split('/').LastOrDefault(); + var ext = result.Content.Headers.ContentType.MediaType.Split('/').Last(); var fullCachePath = GetFullCachePath(urlHash + "." + ext); Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); From 3a91047413d6185c6c18f08a15f94b84be992d32 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 14:59:00 -0600 Subject: [PATCH 064/102] P E R F O R M A N C E --- Jellyfin.Api/Controllers/ItemLookupController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index 2857d6c8d4..afde4a4336 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -332,7 +332,7 @@ namespace Jellyfin.Api.Controllers private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath) { using var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false); - var ext = result.Content.Headers.ContentType.MediaType.Split('/').Last(); + var ext = result.Content.Headers.ContentType.MediaType.Split('/')[^1]; var fullCachePath = GetFullCachePath(urlHash + "." + ext); Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); From be67528958fc225d9af17cc34da87a613fb07cc9 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 15:03:45 -0600 Subject: [PATCH 065/102] add disposal docs --- Jellyfin.Api/Controllers/LiveTvController.cs | 1 + Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 2838361d70..97a88bc149 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1071,6 +1071,7 @@ namespace Jellyfin.Api.Controllers { var client = _httpClientFactory.CreateClient(); // https://json.schedulesdirect.org/20141201/available/countries + // Can't dispose the response as it's required up the call chain. var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries") .ConfigureAwait(false); diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index ca62d4267a..5a59313c94 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -36,6 +36,7 @@ namespace Jellyfin.Api.Helpers httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, useragent); } + // Can't dispose the response as it's required up the call chain. var response = await httpClient.GetAsync(state.MediaPath).ConfigureAwait(false); var contentType = response.Content.Headers.ContentType.ToString(); From 4ec1341c8fb7f19588cbd9a56dba71808d7a4615 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 17 Aug 2020 15:54:51 -0600 Subject: [PATCH 066/102] Fix conflicts --- Jellyfin.Api/Controllers/VideosController.cs | 2 +- Jellyfin.Api/Helpers/AudioHelper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index de9813c586..14d3f24607 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -471,7 +471,7 @@ namespace Jellyfin.Api.Controllers StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager); var httpClient = _httpClientFactory.CreateClient(); - return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, httpClient).ConfigureAwait(false); + return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, HttpContext).ConfigureAwait(false); } if (@static.HasValue && @static.Value && state.InputProtocol != MediaProtocol.File) diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs index 694e1f0ddd..eae2be05ba 100644 --- a/Jellyfin.Api/Helpers/AudioHelper.cs +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -138,7 +138,7 @@ namespace Jellyfin.Api.Helpers { StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, true, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager); - using var httpClient = _httpClientFactory.CreateClient(); + var httpClient = _httpClientFactory.CreateClient(); return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, _httpContextAccessor.HttpContext).ConfigureAwait(false); } From 7c2e01a70b1cf59d314c8036ea1f8f224120af3d Mon Sep 17 00:00:00 2001 From: Hilman Maulana Date: Tue, 18 Aug 2020 10:01:18 +0000 Subject: [PATCH 067/102] Translated using Weblate (Indonesian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/id/ --- Emby.Server.Implementations/Localization/Core/id.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index eabdb9138a..f5ad3183c0 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -91,5 +91,12 @@ "NotificationOptionAudioPlaybackStopped": "Pemutaran audio berhenti", "NotificationOptionAudioPlayback": "Pemutaran audio dimulai", "MixedContent": "Konten campur", - "PluginUninstalledWithName": "{0} telah dihapus" + "PluginUninstalledWithName": "{0} telah dihapus", + "TaskRefreshChapterImagesDescription": "Membuat gambar mini untuk video yang memiliki bab.", + "TaskRefreshChapterImages": "Ekstrak Gambar Bab", + "TaskCleanCacheDescription": "Menghapus file cache yang tidak lagi dibutuhkan oleh sistem.", + "TaskCleanCache": "Bersihkan Cache Direktori", + "TasksLibraryCategory": "Perpustakaan", + "TasksMaintenanceCategory": "Perbaikan", + "TasksApplicationCategory": "Aplikasi" } From 34be17a4e80c7f8debd161944fcb98986b587a5a Mon Sep 17 00:00:00 2001 From: millallo Date: Tue, 18 Aug 2020 11:48:30 +0000 Subject: [PATCH 068/102] 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 0e27806dd9..c61702c16d 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -106,7 +106,7 @@ "TaskCleanLogsDescription": "Rimuovi i file di log più vecchi di {0} giorni.", "TaskCleanLogs": "Pulisci la cartella dei log", "TaskRefreshLibraryDescription": "Analizza la tua libreria multimediale per nuovi file e rinnova i metadati.", - "TaskRefreshLibrary": "Analizza la libreria dei contenuti multimediali", + "TaskRefreshLibrary": "Scan Librerie", "TaskRefreshChapterImagesDescription": "Crea le thumbnail per i video che hanno capitoli.", "TaskRefreshChapterImages": "Estrai immagini capitolo", "TaskCleanCacheDescription": "Cancella i file di cache non più necessari al sistema.", From 9e8f0cf26d9fc55fe990e8fb46d6011f03439c19 Mon Sep 17 00:00:00 2001 From: Hilman Maulana Date: Tue, 18 Aug 2020 10:22:14 +0000 Subject: [PATCH 069/102] Translated using Weblate (Indonesian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/id/ --- .../Localization/Core/id.json | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index f5ad3183c0..ccb72ff937 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -7,8 +7,8 @@ "MessageApplicationUpdated": "Jellyfin Server sudah diperbarui", "Latest": "Terbaru", "LabelIpAddressValue": "Alamat IP: {0}", - "ItemRemovedWithName": "{0} sudah dikeluarkan dari perpustakaan", - "ItemAddedWithName": "{0} sudah dimasukkan ke dalam perpustakaan", + "ItemRemovedWithName": "{0} sudah dikeluarkan dari pustaka", + "ItemAddedWithName": "{0} telah dimasukkan ke dalam pustaka", "Inherit": "Warisan", "HomeVideos": "Video Rumah", "HeaderRecordingGroups": "Grup Rekaman", @@ -19,8 +19,8 @@ "HeaderFavoriteEpisodes": "Episode Favorit", "HeaderFavoriteArtists": "Artis Favorit", "HeaderFavoriteAlbums": "Album Favorit", - "HeaderContinueWatching": "Masih Melihat", - "HeaderCameraUploads": "Uplod Kamera", + "HeaderContinueWatching": "Lanjutkan Menonton", + "HeaderCameraUploads": "Unggahan Kamera", "HeaderAlbumArtists": "Album Artis", "Genres": "Genre", "Folders": "Folder", @@ -32,11 +32,11 @@ "ChapterNameValue": "Bagian {0}", "Channels": "Saluran", "TvShows": "Seri TV", - "SubtitleDownloadFailureFromForItem": "Talop gagal diunduh dari {0} untuk {1}", - "StartupEmbyServerIsLoading": "Peladen Jellyfin sedang dimuat. Silakan coba kembali beberapa saat lagi.", + "SubtitleDownloadFailureFromForItem": "Subtitel gagal diunduh dari {0} untuk {1}", + "StartupEmbyServerIsLoading": "Server Jellyfin sedang dimuat. Silakan coba lagi nanti.", "Songs": "Lagu", "Playlists": "Daftar putar", - "NotificationOptionPluginUninstalled": "Plugin dilepas", + "NotificationOptionPluginUninstalled": "Plugin dihapus", "MusicVideos": "Video musik", "VersionNumber": "Versi {0}", "ValueSpecialEpisodeName": "Spesial - {0}", @@ -65,7 +65,7 @@ "Photos": "Foto", "NotificationOptionUserLockedOut": "Pengguna terkunci", "NotificationOptionTaskFailed": "Kegagalan tugas terjadwal", - "NotificationOptionServerRestartRequired": "Restart peladen dibutuhkan", + "NotificationOptionServerRestartRequired": "Muat ulang server dibutuhkan", "NotificationOptionPluginUpdateInstalled": "Pembaruan plugin terpasang", "NotificationOptionPluginInstalled": "Plugin terpasang", "NotificationOptionPluginError": "Kegagalan plugin", @@ -74,14 +74,14 @@ "NotificationOptionCameraImageUploaded": "Gambar kamera terunggah", "NotificationOptionApplicationUpdateInstalled": "Pembaruan aplikasi terpasang", "NotificationOptionApplicationUpdateAvailable": "Pembaruan aplikasi tersedia", - "NewVersionIsAvailable": "Sebuah versi baru dari Peladen Jellyfin tersedia untuk diunduh.", + "NewVersionIsAvailable": "Versi baru dari Jellyfin Server tersedia untuk diunduh.", "NameSeasonUnknown": "Musim tak diketahui", "NameSeasonNumber": "Musim {0}", - "NameInstallFailed": "{0} instalasi gagal", + "NameInstallFailed": "{0} penginstalan gagal", "Music": "Musik", "Movies": "Film", - "MessageServerConfigurationUpdated": "Konfigurasi peladen telah diperbarui", - "MessageNamedServerConfigurationUpdatedWithValue": "Konfigurasi peladen bagian {0} telah diperbarui", + "MessageServerConfigurationUpdated": "Konfigurasi server telah diperbarui", + "MessageNamedServerConfigurationUpdatedWithValue": "Bagian konfigurasi server {0} telah diperbarui", "FailedLoginAttemptWithUserName": "Percobaan login gagal dari {0}", "CameraImageUploadedFrom": "Sebuah gambar baru telah diunggah dari {0}", "DeviceOfflineWithName": "{0} telah terputus", @@ -90,13 +90,28 @@ "NotificationOptionVideoPlayback": "Pemutaran video dimulai", "NotificationOptionAudioPlaybackStopped": "Pemutaran audio berhenti", "NotificationOptionAudioPlayback": "Pemutaran audio dimulai", - "MixedContent": "Konten campur", + "MixedContent": "Konten campuran", "PluginUninstalledWithName": "{0} telah dihapus", - "TaskRefreshChapterImagesDescription": "Membuat gambar mini untuk video yang memiliki bab.", - "TaskRefreshChapterImages": "Ekstrak Gambar Bab", + "TaskRefreshChapterImagesDescription": "Membuat gambar mini untuk video yang memiliki bagian.", + "TaskRefreshChapterImages": "Ekstrak Gambar Bagian", "TaskCleanCacheDescription": "Menghapus file cache yang tidak lagi dibutuhkan oleh sistem.", "TaskCleanCache": "Bersihkan Cache Direktori", - "TasksLibraryCategory": "Perpustakaan", + "TasksLibraryCategory": "Pustaka", "TasksMaintenanceCategory": "Perbaikan", - "TasksApplicationCategory": "Aplikasi" + "TasksApplicationCategory": "Aplikasi", + "TaskRefreshPeopleDescription": "Memperbarui metadata untuk aktor dan sutradara di pustaka media Anda.", + "TaskRefreshLibraryDescription": "Memindai Pustaka media Anda untuk mencari file baru dan memperbarui metadata.", + "TasksChannelsCategory": "Saluran Online", + "TaskDownloadMissingSubtitlesDescription": "Mencari di internet untuk subtitle yang hilang berdasarkan konfigurasi metadata.", + "TaskDownloadMissingSubtitles": "Unduh subtitle yang hilang", + "TaskRefreshChannelsDescription": "Segarkan informasi saluran internet.", + "TaskRefreshChannels": "Segarkan Saluran", + "TaskCleanTranscodeDescription": "Menghapus file transcode yang berumur lebih dari satu hari.", + "TaskCleanTranscode": "Bersihkan Direktori Transcode", + "TaskUpdatePluginsDescription": "Unduh dan instal pembaruan untuk plugin yang dikonfigurasi untuk memperbarui secara otomatis.", + "TaskUpdatePlugins": "Perbarui Plugin", + "TaskRefreshPeople": "Muat ulang Orang", + "TaskCleanLogsDescription": "Menghapus file log yang lebih dari {0} hari.", + "TaskCleanLogs": "Bersihkan Log Direktori", + "TaskRefreshLibrary": "Pindai Pustaka Media" } From 287baa49654e618345dc6f5f8bf00d5e80146530 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 18 Aug 2020 07:29:12 -0600 Subject: [PATCH 070/102] remove unused directives --- Jellyfin.Api/Controllers/NotificationsController.cs | 1 - Jellyfin.Api/Controllers/PlaystateController.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/NotificationsController.cs b/Jellyfin.Api/Controllers/NotificationsController.cs index cf3e780b44..0ceda6815c 100644 --- a/Jellyfin.Api/Controllers/NotificationsController.cs +++ b/Jellyfin.Api/Controllers/NotificationsController.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 351e817039..22f2ca5c32 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Jellyfin.Api.Constants; From b90e240af649b1cf2ad72318e985755514b893c3 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 18 Aug 2020 07:29:39 -0600 Subject: [PATCH 071/102] rename clientCapabilities --- Jellyfin.Api/Controllers/UniversalAudioController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 2a788e03ad..3994fb9958 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -119,10 +119,10 @@ namespace Jellyfin.Api.Controllers if (deviceProfile == null) { - var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); - if (caps != null) + var clientCapabilities = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (clientCapabilities != null) { - deviceProfile = caps.DeviceProfile; + deviceProfile = clientCapabilities.DeviceProfile; } } From 684d9856b11e4795798cd08f84ac4e84e50fa17c Mon Sep 17 00:00:00 2001 From: millallo Date: Tue, 18 Aug 2020 14:20:42 +0000 Subject: [PATCH 072/102] 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index c61702c16d..bf1a0ef136 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -102,7 +102,7 @@ "TaskUpdatePluginsDescription": "Scarica e installa gli aggiornamenti per i plugin che sono stati configurati per essere aggiornati contemporaneamente.", "TaskUpdatePlugins": "Aggiorna i Plugin", "TaskRefreshPeopleDescription": "Aggiorna i metadati per gli attori e registi nella tua libreria multimediale.", - "TaskRefreshPeople": "Aggiorna persone", + "TaskRefreshPeople": "Aggiornamento Persone", "TaskCleanLogsDescription": "Rimuovi i file di log più vecchi di {0} giorni.", "TaskCleanLogs": "Pulisci la cartella dei log", "TaskRefreshLibraryDescription": "Analizza la tua libreria multimediale per nuovi file e rinnova i metadati.", From 3a722740ceb83d796a7111ae40b2b67d6a313465 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Wed, 19 Aug 2020 04:53:25 -0600 Subject: [PATCH 073/102] Update Jellyfin.Api/Helpers/MediaInfoHelper.cs Co-authored-by: Claus Vium --- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index e94109c917..d2d1855a4c 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -465,10 +465,10 @@ namespace Jellyfin.Api.Helpers var profile = request.DeviceProfile; if (profile == null) { - var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); - if (caps != null) + var clientCapabilities = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (clientCapabilities != null) { - profile = caps.DeviceProfile; + profile = clientCapabilities.DeviceProfile; } } From 2b400c99ef946ef1e52e3f01cb18bc008a369c59 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 Aug 2020 19:26:28 +0200 Subject: [PATCH 074/102] Fix warnings --- Emby.Dlna/DlnaManager.cs | 12 +- Emby.Dlna/PlayTo/Device.cs | 36 +++--- Emby.Dlna/PlayTo/TransportCommands.cs | 17 +-- .../Data/SqliteItemRepository.cs | 4 +- .../LiveTv/EmbyTV/EncodedRecorder.cs | 2 +- .../LiveTv/Listings/XmlTvListingsProvider.cs | 5 +- .../LiveTv/LiveTvManager.cs | 106 +++++++++--------- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 16 +-- Jellyfin.Api/Controllers/ImageController.cs | 2 +- .../Entities/Movies/Movie.cs | 4 +- MediaBrowser.Controller/Entities/TV/Series.cs | 2 +- MediaBrowser.Controller/Entities/Trailer.cs | 3 +- MediaBrowser.Controller/Library/Profiler.cs | 17 ++- .../LiveTv/LiveTvChannel.cs | 2 +- .../LiveTv/LiveTvProgram.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 8 +- .../Providers/IProviderManager.cs | 2 +- .../Encoder/EncodingUtils.cs | 9 +- .../Encoder/MediaEncoder.cs | 14 +-- .../Subtitles/SubtitleEncoder.cs | 2 +- .../Dlna/ContentFeatureBuilder.cs | 2 +- MediaBrowser.Model/Dlna/DlnaMaps.cs | 8 +- .../Dlna/MediaFormatProfileResolver.cs | 21 ++-- MediaBrowser.Model/Dlna/StreamInfo.cs | 12 +- MediaBrowser.Providers/Manager/ImageSaver.cs | 4 +- .../Manager/ItemImageProvider.cs | 57 +++++----- .../Manager/MetadataService.cs | 73 ++++++------ .../Manager/ProviderManager.cs | 37 ++++-- .../MediaInfo/FFProbeAudioInfo.cs | 15 +-- .../MediaInfo/FFProbeProvider.cs | 21 +--- MediaBrowser.Providers/Music/Extensions.cs | 4 +- .../Plugins/MusicBrainz/AlbumProvider.cs | 2 +- .../Plugins/MusicBrainz/ArtistProvider.cs | 4 +- .../Plugins/Omdb/OmdbImageProvider.cs | 3 +- .../Plugins/Omdb/OmdbItemProvider.cs | 2 +- .../Plugins/Omdb/OmdbProvider.cs | 25 +++-- .../Plugins/TheTvdb/TvdbClientManager.cs | 31 ++--- .../TheTvdb/TvdbEpisodeImageProvider.cs | 7 +- .../Tmdb/BoxSets/TmdbBoxSetProvider.cs | 4 +- .../Tmdb/Movies/GenericTmdbMovieInfo.cs | 2 +- .../Plugins/Tmdb/Movies/TmdbSearch.cs | 54 ++++++++- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 2 +- .../Tmdb/TV/TmdbEpisodeProviderBase.cs | 4 +- .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 4 +- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 2 +- .../Studios/StudiosImageProvider.cs | 3 +- 46 files changed, 366 insertions(+), 302 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 269f7ee43a..ce4be7b514 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -54,11 +54,15 @@ namespace Emby.Dlna _appHost = appHost; } + private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user"); + + private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system"); + public async Task InitProfilesAsync() { try { - await ExtractSystemProfilesAsync(); + await ExtractSystemProfilesAsync().ConfigureAwait(false); LoadProfiles(); } catch (Exception ex) @@ -240,7 +244,7 @@ namespace Emby.Dlna } else { - var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray()); + 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); } @@ -280,10 +284,6 @@ namespace Emby.Dlna return false; } - private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user"); - - private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system"); - private IEnumerable GetProfiles(string path, DeviceProfileType type) { try diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 72834c69d1..86dd52a1b6 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -19,6 +19,8 @@ namespace Emby.Dlna.PlayTo { public class Device : IDisposable { + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private Timer _timer; public DeviceInfo Properties { get; set; } @@ -55,16 +57,13 @@ namespace Emby.Dlna.PlayTo private readonly ILogger _logger; - private readonly IServerConfigurationManager _config; - public Action OnDeviceUnavailable { get; set; } - public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config) + public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger) { Properties = deviceProperties; _httpClient = httpClient; _logger = logger; - _config = config; } public void Start() @@ -275,7 +274,7 @@ namespace Emby.Dlna.PlayTo throw new InvalidOperationException("Unable to find service"); } - await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME")) + await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME")) .ConfigureAwait(false); RestartTimer(true); @@ -285,7 +284,7 @@ namespace Emby.Dlna.PlayTo { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); - url = url.Replace("&", "&"); + url = url.Replace("&", "&", StringComparison.Ordinal); _logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header); @@ -297,8 +296,8 @@ namespace Emby.Dlna.PlayTo var dictionary = new Dictionary { - {"CurrentURI", url}, - {"CurrentURIMetaData", CreateDidlMeta(metaData)} + { "CurrentURI", url }, + { "CurrentURIMetaData", CreateDidlMeta(metaData) } }; var service = GetAvTransportService(); @@ -732,10 +731,10 @@ namespace Emby.Dlna.PlayTo } var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null); - var trackUri = trackUriElem == null ? null : trackUriElem.Value; + var trackUri = trackUriElem?.Value; var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null); - var duration = durationElem == null ? null : durationElem.Value; + var duration = durationElem?.Value; if (!string.IsNullOrWhiteSpace(duration) && !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase)) @@ -748,7 +747,7 @@ namespace Emby.Dlna.PlayTo } var positionElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null); - var position = positionElem == null ? null : positionElem.Value; + var position = positionElem?.Value; if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase)) { @@ -819,7 +818,7 @@ namespace Emby.Dlna.PlayTo // some devices send back invalid xml try { - return XElement.Parse(xml.Replace("&", "&")); + return XElement.Parse(xml.Replace("&", "&", StringComparison.Ordinal)); } catch (XmlException) { @@ -848,7 +847,7 @@ namespace Emby.Dlna.PlayTo ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId), Title = container.GetValue(uPnpNamespaces.title), IconUrl = container.GetValue(uPnpNamespaces.Artwork), - SecondText = "", + SecondText = string.Empty, Url = url, ProtocolInfo = GetProtocolInfo(container), MetaData = container.ToString() @@ -941,12 +940,12 @@ namespace Emby.Dlna.PlayTo return url; } - if (!url.Contains("/")) + if (!url.Contains('/', StringComparison.Ordinal)) { url = "/dmr/" + url; } - if (!url.StartsWith("/")) + if (!url.StartsWith("/", StringComparison.Ordinal)) { url = "/" + url; } @@ -981,7 +980,7 @@ namespace Emby.Dlna.PlayTo var deviceProperties = new DeviceInfo() { Name = string.Join(" ", friendlyNames), - BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port) + BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port) }; var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault(); @@ -1068,10 +1067,9 @@ namespace Emby.Dlna.PlayTo } } - return new Device(deviceProperties, httpClient, logger, config); + return new Device(deviceProperties, httpClient, logger); } - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); private static DeviceIcon CreateIcon(XElement element) { if (element == null) @@ -1222,7 +1220,7 @@ namespace Emby.Dlna.PlayTo public override string ToString() { - return string.Format("{0} - {1}", Properties.Name, Properties.BaseUrl); + return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl); } } } diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs index c0ce3ab6e9..dc797a6918 100644 --- a/Emby.Dlna/PlayTo/TransportCommands.cs +++ b/Emby.Dlna/PlayTo/TransportCommands.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Xml.Linq; using Emby.Dlna.Common; @@ -11,14 +12,16 @@ namespace Emby.Dlna.PlayTo { public class TransportCommands { + private const string CommandBase = "\r\n" + "" + "" + "" + "{2}" + "" + ""; private List _stateVariables = new List(); + private List _serviceActions = new List(); + public List StateVariables { get => _stateVariables; set => _stateVariables = value; } - private List _serviceActions = new List(); public List ServiceActions { get => _serviceActions; @@ -123,7 +126,7 @@ namespace Emby.Dlna.PlayTo } } - return string.Format(CommandBase, action.Name, xmlNamespace, stateString); + return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString); } public string BuildPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "") @@ -147,7 +150,7 @@ namespace Emby.Dlna.PlayTo } } - return string.Format(CommandBase, action.Name, xmlNamesapce, stateString); + return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString); } public string BuildPost(ServiceAction action, string xmlNamesapce, object value, Dictionary dictionary) @@ -170,7 +173,7 @@ namespace Emby.Dlna.PlayTo } } - return string.Format(CommandBase, action.Name, xmlNamesapce, stateString); + return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString); } private string BuildArgumentXml(Argument argument, string value, string commandParameter = "") @@ -183,12 +186,10 @@ namespace Emby.Dlna.PlayTo state.AllowedValues.FirstOrDefault() ?? value; - return string.Format("<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}", argument.Name, state.DataType ?? "string", sendValue); + return string.Format(CultureInfo.InvariantCulture, "<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}", argument.Name, state.DataType ?? "string", sendValue); } - return string.Format("<{0}>{1}", argument.Name, value); + return string.Format(CultureInfo.InvariantCulture, "<{0}>{1}", argument.Name, value); } - - private const string CommandBase = "\r\n" + "" + "" + "" + "{2}" + "" + ""; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index d11e5e62e3..331ffc1349 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4560,13 +4560,13 @@ namespace Emby.Server.Implementations.Data if (query.AncestorIds.Length > 1) { var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); - whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause)); + whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause)); } if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)) { var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey"; - whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause)); + whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause)); if (statement != null) { statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index d8ec107ecd..612dc5238b 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -230,7 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (filters.Count > 0) { - output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray())); + output += string.Format(CultureInfo.InvariantCulture, " -vf \"{0}\"", string.Join(",", filters.ToArray())); } return output; diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 0a93c46748..f33d071747 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings && !programInfo.IsRepeat && (programInfo.EpisodeNumber ?? 0) == 0) { - programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture); + programInfo.ShowId += programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture); } } else @@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings } // Construct an id from the channel and start date - programInfo.Id = string.Format("{0}_{1:O}", program.ChannelId, program.StartDate); + programInfo.Id = string.Format(CultureInfo.InvariantCulture, "{0}_{1:O}", program.ChannelId, program.StartDate); if (programInfo.IsMovie) { @@ -296,7 +296,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings Name = c.DisplayName, ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null, Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number - }).ToList(); } } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 1b075d86a8..90cbd85a55 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -41,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv /// public class LiveTvManager : ILiveTvManager, IDisposable { + private const int MaxGuideDays = 14; private const string ExternalServiceTag = "ExternalServiceId"; private const string EtagKey = "ProgramEtag"; @@ -560,7 +561,7 @@ namespace Emby.Server.Implementations.LiveTv item.Audio = info.Audio; item.ChannelId = channel.Id; - item.CommunityRating = item.CommunityRating ?? info.CommunityRating; + item.CommunityRating ??= info.CommunityRating; if ((item.CommunityRating ?? 0).Equals(0)) { item.CommunityRating = null; @@ -645,8 +646,8 @@ namespace Emby.Server.Implementations.LiveTv item.IsSeries = isSeries; item.Name = info.Name; - item.OfficialRating = item.OfficialRating ?? info.OfficialRating; - item.Overview = item.Overview ?? info.Overview; + item.OfficialRating ??= info.OfficialRating; + item.Overview ??= info.Overview; item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks; item.ProviderIds = info.ProviderIds; @@ -683,19 +684,23 @@ namespace Emby.Server.Implementations.LiveTv { if (!string.IsNullOrWhiteSpace(info.ImagePath)) { - item.SetImage(new ItemImageInfo - { - Path = info.ImagePath, - Type = ImageType.Primary - }, 0); + item.SetImage( + new ItemImageInfo + { + Path = info.ImagePath, + Type = ImageType.Primary + }, + 0); } else if (!string.IsNullOrWhiteSpace(info.ImageUrl)) { - item.SetImage(new ItemImageInfo - { - Path = info.ImageUrl, - Type = ImageType.Primary - }, 0); + item.SetImage( + new ItemImageInfo + { + Path = info.ImageUrl, + Type = ImageType.Primary + }, + 0); } } @@ -703,11 +708,13 @@ namespace Emby.Server.Implementations.LiveTv { if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl)) { - item.SetImage(new ItemImageInfo - { - Path = info.ThumbImageUrl, - Type = ImageType.Thumb - }, 0); + item.SetImage( + new ItemImageInfo + { + Path = info.ThumbImageUrl, + Type = ImageType.Thumb + }, + 0); } } @@ -715,11 +722,13 @@ namespace Emby.Server.Implementations.LiveTv { if (!string.IsNullOrWhiteSpace(info.LogoImageUrl)) { - item.SetImage(new ItemImageInfo - { - Path = info.LogoImageUrl, - Type = ImageType.Logo - }, 0); + item.SetImage( + new ItemImageInfo + { + Path = info.LogoImageUrl, + Type = ImageType.Logo + }, + 0); } } @@ -727,11 +736,13 @@ namespace Emby.Server.Implementations.LiveTv { if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl)) { - item.SetImage(new ItemImageInfo - { - Path = info.BackdropImageUrl, - Type = ImageType.Backdrop - }, 0); + item.SetImage( + new ItemImageInfo + { + Path = info.BackdropImageUrl, + Type = ImageType.Backdrop + }, + 0); } } @@ -786,7 +797,6 @@ namespace Emby.Server.Implementations.LiveTv if (query.OrderBy.Count == 0) { - // Unless something else was specified, order by start date to take advantage of a specialized index query.OrderBy = new[] { @@ -824,7 +834,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrWhiteSpace(query.SeriesTimerId)) { - var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false); + var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false); var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase)); if (seriesTimer != null) { @@ -847,13 +857,11 @@ namespace Emby.Server.Implementations.LiveTv var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user); - var result = new QueryResult + return new QueryResult { Items = returnArray, TotalRecordCount = queryResult.TotalRecordCount }; - - return result; } public QueryResult GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken) @@ -1173,7 +1181,6 @@ namespace Emby.Server.Implementations.LiveTv var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery { - IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name }, ChannelIds = new Guid[] { currentChannel.Id }, DtoOptions = new DtoOptions(true) @@ -1298,8 +1305,6 @@ namespace Emby.Server.Implementations.LiveTv } } - private const int MaxGuideDays = 14; - private double GetGuideDays() { var config = GetConfiguration(); @@ -1712,7 +1717,7 @@ namespace Emby.Server.Implementations.LiveTv if (timer == null) { - throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id)); + throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "Timer with Id {0} not found", id)); } var service = GetService(timer.ServiceName); @@ -1731,7 +1736,7 @@ namespace Emby.Server.Implementations.LiveTv if (timer == null) { - throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id)); + throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "SeriesTimer with Id {0} not found", id)); } var service = GetService(timer.ServiceName); @@ -1743,10 +1748,12 @@ namespace Emby.Server.Implementations.LiveTv public async Task GetTimer(string id, CancellationToken cancellationToken) { - var results = await GetTimers(new TimerQuery - { - Id = id - }, cancellationToken).ConfigureAwait(false); + var results = await GetTimers( + new TimerQuery + { + Id = id + }, + cancellationToken).ConfigureAwait(false); return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); } @@ -1794,10 +1801,7 @@ namespace Emby.Server.Implementations.LiveTv } var returnArray = timers - .Select(i => - { - return i.Item1; - }) + .Select(i => i.Item1) .ToArray(); return new QueryResult @@ -1968,7 +1972,7 @@ namespace Emby.Server.Implementations.LiveTv if (service == null) { - service = _services.First(); + service = _services[0]; } var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false); @@ -1994,9 +1998,7 @@ namespace Emby.Server.Implementations.LiveTv { var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false); - var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null); - - return obj; + return _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null); } public async Task GetNewTimerDefaults(string programId, CancellationToken cancellationToken) @@ -2125,6 +2127,7 @@ namespace Emby.Server.Implementations.LiveTv public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } private bool _disposed = false; @@ -2447,8 +2450,7 @@ namespace Emby.Server.Implementations.LiveTv .SelectMany(i => i.Locations) .Distinct(StringComparer.OrdinalIgnoreCase) .Select(i => _libraryManager.FindByPath(i, true)) - .Where(i => i != null) - .Where(i => i.IsVisibleStandalone(user)) + .Where(i => i != null && i.IsVisibleStandalone(user)) .SelectMany(i => _libraryManager.GetCollectionFolders(i)) .GroupBy(x => x.Id) .Select(x => x.First()) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index c61189c0ac..f1e120a649 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -182,12 +182,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); - using (var response = await _httpClient.SendAsync(new HttpRequestOptions() - { - Url = string.Format("{0}/tuners.html", GetApiUrl(info)), - CancellationToken = cancellationToken, - BufferContent = false - }, HttpMethod.Get).ConfigureAwait(false)) + using (var response = await _httpClient.SendAsync( + new HttpRequestOptions() + { + Url = string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)), + CancellationToken = cancellationToken, + BufferContent = false + }, + HttpMethod.Get).ConfigureAwait(false)) using (var stream = response.Content) using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8)) { @@ -730,7 +732,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // Need a way to set the Receive timeout on the socket otherwise this might never timeout? try { - await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken); + await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken).ConfigureAwait(false); var receiveBuffer = new byte[8192]; while (!cancellationToken.IsCancellationRequested) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 8f5c6beb30..75734f0af0 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType))); await _providerManager - .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path) + .SaveImage(memoryStream, mimeType, user.ProfileImage.Path) .ConfigureAwait(false); await _userManager.UpdateUserAsync(user).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 53badac4db..5ae396e68b 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -1,5 +1,7 @@ + using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text.Json.Serialization; using System.Threading; @@ -179,7 +181,7 @@ namespace MediaBrowser.Controller.Entities.Movies list.Add(new ExternalUrl { Name = "Trakt", - Url = string.Format("https://trakt.tv/movies/{0}", imdbId) + Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId) }); } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 45daa8a539..23d9600920 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -496,7 +496,7 @@ namespace MediaBrowser.Controller.Entities.TV list.Add(new ExternalUrl { Name = "Trakt", - Url = string.Format("https://trakt.tv/shows/{0}", imdbId) + Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/shows/{0}", imdbId) }); } diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 6b544afc68..83e9ce1e75 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text.Json.Serialization; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Providers; @@ -86,7 +87,7 @@ namespace MediaBrowser.Controller.Entities list.Add(new ExternalUrl { Name = "Trakt", - Url = string.Format("https://trakt.tv/movies/{0}", imdbId) + Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId) }); } diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs index 399378a099..5efdc6a481 100644 --- a/MediaBrowser.Controller/Library/Profiler.cs +++ b/MediaBrowser.Controller/Library/Profiler.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Globalization; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Library @@ -13,6 +14,7 @@ namespace MediaBrowser.Controller.Library /// The name. /// readonly string _name; + /// /// The stopwatch. /// @@ -44,6 +46,7 @@ namespace MediaBrowser.Controller.Library public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } /// @@ -58,13 +61,19 @@ namespace MediaBrowser.Controller.Library string message; if (_stopwatch.ElapsedMilliseconds > 300000) { - message = string.Format("{0} took {1} minutes.", - _name, ((float)_stopwatch.ElapsedMilliseconds / 60000).ToString("F")); + message = string.Format( + CultureInfo.InvariantCulture, + "{0} took {1} minutes.", + _name, + ((float)_stopwatch.ElapsedMilliseconds / 60000).ToString("F", CultureInfo.InvariantCulture)); } else { - message = string.Format("{0} took {1} seconds.", - _name, ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000")); + message = string.Format( + CultureInfo.InvariantCulture, + "{0} took {1} seconds.", + _name, + ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000", CultureInfo.InvariantCulture)); } _logger.LogInformation(message); diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 10af981213..aa7c12dd14 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.LiveTv if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number)) { - return string.Format("{0:00000.0}", number) + "-" + (Name ?? string.Empty); + return string.Format(CultureInfo.InvariantCulture, "{0:00000.0}", number) + "-" + (Name ?? string.Empty); } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 472b061e6a..e1de01ff02 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -261,7 +261,7 @@ namespace MediaBrowser.Controller.LiveTv list.Add(new ExternalUrl { Name = "Trakt", - Url = string.Format("https://trakt.tv/movies/{0}", imdbId) + Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId) }); } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2dd21be3c3..7b09f489e9 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -675,7 +675,7 @@ namespace MediaBrowser.Controller.MediaEncoding // } // } - // fallbackFontParam = string.Format(":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath))); + // fallbackFontParam = string.Format(CultureInfo.InvariantCulture, ":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath))); if (state.SubtitleStream.IsExternal) { @@ -880,7 +880,7 @@ namespace MediaBrowser.Controller.MediaEncoding profileScore = Math.Min(profileScore, 2); // http://www.webmproject.org/docs/encoder-parameters/ - param += string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", + param += string.Format(CultureInfo.InvariantCulture, "-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", profileScore.ToString(_usCulture), crf, qmin, @@ -904,7 +904,7 @@ namespace MediaBrowser.Controller.MediaEncoding var framerate = GetFramerateParam(state); if (framerate.HasValue) { - param += string.Format(" -r {0}", framerate.Value.ToString(_usCulture)); + param += string.Format(CultureInfo.InvariantCulture, " -r {0}", framerate.Value.ToString(_usCulture)); } var targetVideoCodec = state.ActualOutputVideoCodec; @@ -1484,7 +1484,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (time > 0) { - return string.Format("-ss {0}", _mediaEncoder.GetTimeParameter(time)); + return string.Format(CultureInfo.InvariantCulture, "-ss {0}", _mediaEncoder.GetTimeParameter(time)); } return string.Empty; diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 8ba01d7730..c77349d010 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Controller.Providers /// Task. Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken); - Task SaveImage(User user, Stream source, string mimeType, string path); + Task SaveImage(Stream source, string mimeType, string path); /// /// Adds the metadata providers. diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index 7c2d9f1fd5..082ae2888d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Model.MediaInfo; @@ -14,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { var url = inputFiles[0]; - return string.Format("\"{0}\"", url); + return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", url); } return GetConcatInputArgument(inputFiles); @@ -33,7 +34,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { var files = string.Join("|", inputFiles.Select(NormalizePath)); - return string.Format("concat:\"{0}\"", files); + return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files); } // Determine the input path for video files @@ -49,13 +50,13 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (path.IndexOf("://") != -1) { - return string.Format("\"{0}\"", path); + return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", path); } // Quotes are valid path characters in linux and they need to be escaped here with a leading \ path = NormalizePath(path); - return string.Format("file:\"{0}\"", path); + return string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", path); } /// diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 778c0b18c2..b9a6432ad2 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -552,8 +552,8 @@ namespace MediaBrowser.MediaEncoding.Encoder // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty; - var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) : - string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg); + var args = useIFrame ? string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) : + string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg); var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); @@ -570,7 +570,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (offset.HasValue) { - args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args; + args = string.Format(CultureInfo.InvariantCulture, "-ss {0} ", GetTimeParameter(offset.Value)) + args; } if (videoStream != null) @@ -641,7 +641,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (exitCode == -1 || !file.Exists || file.Length == 0) { - var msg = string.Format("ffmpeg image extraction failed for {0}", inputPath); + var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath); _logger.LogError(msg); @@ -684,13 +684,13 @@ namespace MediaBrowser.MediaEncoding.Encoder { var maxWidthParam = maxWidth.Value.ToString(_usCulture); - vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam); + vf += string.Format(CultureInfo.InvariantCulture, ",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam); } Directory.CreateDirectory(targetDirectory); var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg"); - var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf); + var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf); var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); @@ -790,7 +790,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (exitCode == -1) { - var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument); + var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputArgument); _logger.LogError(msg); diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 374e35b969..fbe8bd69f7 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -435,7 +435,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles CreateNoWindow = true, UseShellExecute = false, FileName = _mediaEncoder.EncoderPath, - Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath), + Arguments = string.Format(CultureInfo.InvariantCulture, "{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false }, diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index a579f8464d..93e60753ae 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -157,7 +157,7 @@ namespace MediaBrowser.Model.Dlna // flagValue = flagValue | DlnaFlags.TimeBasedSeek; //} - string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", + string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container, diff --git a/MediaBrowser.Model/Dlna/DlnaMaps.cs b/MediaBrowser.Model/Dlna/DlnaMaps.cs index 052b4b78b0..95cd0ac276 100644 --- a/MediaBrowser.Model/Dlna/DlnaMaps.cs +++ b/MediaBrowser.Model/Dlna/DlnaMaps.cs @@ -1,18 +1,20 @@ #pragma warning disable CS1591 +using System.Globalization; + namespace MediaBrowser.Model.Dlna { public static class DlnaMaps { private static readonly string DefaultStreaming = - FlagsToString(DlnaFlags.StreamingTransferMode | + FlagsToString(DlnaFlags.StreamingTransferMode | DlnaFlags.BackgroundTransferMode | DlnaFlags.ConnectionStall | DlnaFlags.ByteBasedSeek | DlnaFlags.DlnaV15); private static readonly string DefaultInteractive = - FlagsToString(DlnaFlags.InteractiveTransferMode | + FlagsToString(DlnaFlags.InteractiveTransferMode | DlnaFlags.BackgroundTransferMode | DlnaFlags.ConnectionStall | DlnaFlags.ByteBasedSeek | @@ -20,7 +22,7 @@ namespace MediaBrowser.Model.Dlna public static string FlagsToString(DlnaFlags flags) { - return string.Format("{0:X8}{1:D24}", (ulong)flags, 0); + return string.Format(CultureInfo.InvariantCulture, "{0:X8}{1:D24}", (ulong)flags, 0); } public static string GetOrgOpValue(bool hasKnownRuntime, bool isDirectStream, TranscodeSeekInfo profileTranscodeSeekInfo) diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs index bdc5f8bb78..3c955989a1 100644 --- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs +++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Model.MediaInfo; @@ -142,26 +143,26 @@ namespace MediaBrowser.Model.Dlna { if (timestampType == TransportStreamTimestamp.None) { - return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) }; } - return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) }; } if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) { - return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) }; } if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) { - return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) }; } if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) { - return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) }; } } else if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase)) @@ -180,29 +181,29 @@ namespace MediaBrowser.Model.Dlna { suffix = string.Equals(suffix, "_ISO", StringComparison.OrdinalIgnoreCase) ? suffix : "_T"; - return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "VC1_TS_HD_DTS{0}", suffix)) }; } } else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) { - return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_AAC{0}", suffix)) }; } if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) { - return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) }; } if (string.Equals(audioCodec, "mp2", StringComparison.OrdinalIgnoreCase)) { - return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) }; } if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) { - return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) }; + return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_AC3{0}", suffix)) }; } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 204340c466..94d53ab70e 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -191,7 +191,7 @@ namespace MediaBrowser.Model.Dlna var encodedValue = pair.Value.Replace(" ", "%20"); - list.Add(string.Format("{0}={1}", pair.Name, encodedValue)); + list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue)); } string queryString = string.Join("&", list.ToArray()); @@ -214,18 +214,18 @@ namespace MediaBrowser.Model.Dlna { if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) { - return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); } - return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); + return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) { - return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); } - return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); + return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } private static List BuildParams(StreamInfo item, string accessToken) @@ -457,7 +457,7 @@ namespace MediaBrowser.Model.Dlna { if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal) { - info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", + info.Url = string.Format(CultureInfo.InvariantCulture, "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", baseUrl, ItemId, MediaSourceId, diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 26b50784b1..413d297cb5 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -187,7 +187,7 @@ namespace MediaBrowser.Providers.Manager } } - public async Task SaveImage(User user, Stream source, string path) + public async Task SaveImage(Stream source, string path) { await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false); } @@ -355,7 +355,7 @@ namespace MediaBrowser.Providers.Manager if (string.IsNullOrWhiteSpace(extension)) { - throw new ArgumentException(string.Format("Unable to determine image file extension from mime type {0}", mimeType)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to determine image file extension from mime type {0}", mimeType)); } if (type == ImageType.Thumb && saveLocally) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index a5eb095c46..9227b6d937 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -54,7 +54,12 @@ namespace MediaBrowser.Providers.Manager return hasChanges; } - public async Task RefreshImages(BaseItem item, LibraryOptions libraryOptions, List providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken) + public async Task RefreshImages( + BaseItem item, + LibraryOptions libraryOptions, + List providers, + ImageRefreshOptions refreshOptions, + CancellationToken cancellationToken) { if (refreshOptions.IsReplacingImage(ImageType.Backdrop)) { @@ -78,19 +83,15 @@ namespace MediaBrowser.Providers.Manager foreach (var provider in providers) { - var remoteProvider = provider as IRemoteImageProvider; - - if (remoteProvider != null) + if (provider is IRemoteImageProvider remoteProvider) { await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false); continue; } - var dynamicImageProvider = provider as IDynamicImageProvider; - - if (dynamicImageProvider != null) + if (provider is IDynamicImageProvider dynamicImageProvider) { - await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, libraryOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false); + await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false); } } @@ -100,11 +101,11 @@ namespace MediaBrowser.Providers.Manager /// /// Refreshes from provider. /// - private async Task RefreshFromProvider(BaseItem item, + private async Task RefreshFromProvider( + BaseItem item, IDynamicImageProvider provider, ImageRefreshOptions refreshOptions, TypeOptions savedOptions, - LibraryOptions libraryOptions, ICollection downloadedImages, RefreshResult result, CancellationToken cancellationToken) @@ -115,7 +116,7 @@ namespace MediaBrowser.Providers.Manager foreach (var imageType in images) { - if (!IsEnabled(savedOptions, imageType, item)) + if (!IsEnabled(savedOptions, imageType)) { continue; } @@ -133,12 +134,13 @@ namespace MediaBrowser.Providers.Manager if (response.Protocol == MediaProtocol.Http) { _logger.LogDebug("Setting image url into item {0}", item.Id); - item.SetImage(new ItemImageInfo - { - Path = response.Path, - Type = imageType - - }, 0); + item.SetImage( + new ItemImageInfo + { + Path = response.Path, + Type = imageType + }, + 0); } else { @@ -157,7 +159,7 @@ namespace MediaBrowser.Providers.Manager } downloadedImages.Add(imageType); - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + result.UpdateType |= ItemUpdateType.ImageUpdate; } } } @@ -279,7 +281,7 @@ namespace MediaBrowser.Providers.Manager foreach (var imageType in _singularImages) { - if (!IsEnabled(savedOptions, imageType, item)) + if (!IsEnabled(savedOptions, imageType)) { continue; } @@ -299,8 +301,7 @@ namespace MediaBrowser.Providers.Manager minWidth = savedOptions.GetMinWidth(ImageType.Backdrop); await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false); - var hasScreenshots = item as IHasScreenshots; - if (hasScreenshots != null) + if (item is IHasScreenshots hasScreenshots) { minWidth = savedOptions.GetMinWidth(ImageType.Screenshot); await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false); @@ -317,7 +318,7 @@ namespace MediaBrowser.Providers.Manager } } - private bool IsEnabled(TypeOptions options, ImageType type, BaseItem item) + private bool IsEnabled(TypeOptions options, ImageType type) { return options.IsEnabled(type); } @@ -452,10 +453,10 @@ namespace MediaBrowser.Providers.Manager .Where(i => i.Type == type && !(i.Width.HasValue && i.Width.Value < minWidth)) .ToList(); - if (EnableImageStub(item, type, libraryOptions) && eligibleImages.Count > 0) + if (EnableImageStub(item, libraryOptions) && eligibleImages.Count > 0) { SaveImageStub(item, type, eligibleImages.Select(i => i.Url)); - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + result.UpdateType |= ItemUpdateType.ImageUpdate; return true; } @@ -476,7 +477,7 @@ namespace MediaBrowser.Providers.Manager null, cancellationToken).ConfigureAwait(false); - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + result.UpdateType |= ItemUpdateType.ImageUpdate; return true; } catch (HttpException ex) @@ -495,7 +496,7 @@ namespace MediaBrowser.Providers.Manager return false; } - private bool EnableImageStub(BaseItem item, ImageType type, LibraryOptions libraryOptions) + private bool EnableImageStub(BaseItem item, LibraryOptions libraryOptions) { if (item is LiveTvProgram) { @@ -563,10 +564,10 @@ namespace MediaBrowser.Providers.Manager var url = image.Url; - if (EnableImageStub(item, imageType, libraryOptions)) + if (EnableImageStub(item, libraryOptions)) { SaveImageStub(item, imageType, new[] { url }); - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + result.UpdateType |= ItemUpdateType.ImageUpdate; continue; } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 3b0c7b56ca..dcae300fc1 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -52,7 +52,6 @@ namespace MediaBrowser.Providers.Manager public async Task RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { var itemOfType = (TItemType)item; - var config = ProviderManager.GetMetadataOptions(item); var updateType = ItemUpdateType.None; var requiresRefresh = false; @@ -86,7 +85,7 @@ namespace MediaBrowser.Providers.Manager // Always validate images and check for new locally stored ones. if (itemImageProvider.ValidateImages(item, allImageProviders.OfType(), refreshOptions.DirectoryService)) { - updateType = updateType | ItemUpdateType.ImageUpdate; + updateType |= ItemUpdateType.ImageUpdate; } } catch (Exception ex) @@ -102,7 +101,7 @@ namespace MediaBrowser.Providers.Manager bool hasRefreshedMetadata = true; bool hasRefreshedImages = true; - var isFirstRefresh = item.DateLastRefreshed == default(DateTime); + var isFirstRefresh = item.DateLastRefreshed == default; // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) @@ -114,7 +113,7 @@ namespace MediaBrowser.Providers.Manager { if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata)) { - updateType = updateType | ItemUpdateType.MetadataImport; + updateType |= ItemUpdateType.MetadataImport; } } @@ -132,7 +131,7 @@ namespace MediaBrowser.Providers.Manager var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false); - updateType = updateType | result.UpdateType; + updateType |= result.UpdateType; if (result.Failures > 0) { hasRefreshedMetadata = false; @@ -147,9 +146,9 @@ namespace MediaBrowser.Providers.Manager if (providers.Count > 0) { - var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false); + var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, cancellationToken).ConfigureAwait(false); - updateType = updateType | result.UpdateType; + updateType |= result.UpdateType; if (result.Failures > 0) { hasRefreshedImages = false; @@ -158,7 +157,7 @@ namespace MediaBrowser.Providers.Manager } var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType); - updateType = updateType | beforeSaveResult; + updateType |= beforeSaveResult; // Save if changes were made, or it's never been saved before if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh) @@ -175,7 +174,7 @@ namespace MediaBrowser.Providers.Manager // If any of these properties are set then make sure the updateType is not None, just to force everything to save if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata) { - updateType = updateType | ItemUpdateType.MetadataDownload; + updateType |= ItemUpdateType.MetadataDownload; } if (hasRefreshedMetadata && hasRefreshedImages) @@ -184,11 +183,11 @@ namespace MediaBrowser.Providers.Manager } else { - item.DateLastRefreshed = default(DateTime); + item.DateLastRefreshed = default; } // Save to database - SaveItem(metadataResult, libraryOptions, updateType, cancellationToken); + await SaveItemAsync(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false); } await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false); @@ -203,26 +202,26 @@ namespace MediaBrowser.Providers.Manager lookupInfo.Year = result.ProductionYear; } - protected void SaveItem(MetadataResult result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken) + protected async Task SaveItemAsync(MetadataResult result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken) { if (result.Item.SupportsPeople && result.People != null) { var baseItem = result.Item; LibraryManager.UpdatePeople(baseItem, result.People); - SavePeopleMetadata(result.People, libraryOptions, cancellationToken); + await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false); } result.Item.UpdateToRepository(reason, cancellationToken); } - private void SavePeopleMetadata(List people, LibraryOptions libraryOptions, CancellationToken cancellationToken) + private async Task SavePeopleMetadataAsync(List people, LibraryOptions libraryOptions, CancellationToken cancellationToken) { foreach (var person in people) { cancellationToken.ThrowIfCancellationRequested(); - if (person.ProviderIds.Any() || !string.IsNullOrWhiteSpace(person.ImageUrl)) + if (person.ProviderIds.Count > 0 || !string.IsNullOrWhiteSpace(person.ImageUrl)) { var updateType = ItemUpdateType.MetadataDownload; @@ -239,10 +238,10 @@ namespace MediaBrowser.Providers.Manager if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) { - AddPersonImage(personEntity, libraryOptions, person.ImageUrl, cancellationToken); + await AddPersonImageAsync(personEntity, libraryOptions, person.ImageUrl, cancellationToken).ConfigureAwait(false); saveEntity = true; - updateType = updateType | ItemUpdateType.ImageUpdate; + updateType |= ItemUpdateType.ImageUpdate; } if (saveEntity) @@ -253,26 +252,28 @@ namespace MediaBrowser.Providers.Manager } } - private void AddPersonImage(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken) + private async Task AddPersonImageAsync(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken) { - // if (libraryOptions.DownloadImagesInAdvance) - //{ - // try - // { - // await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - // return; - // } - // catch (Exception ex) - // { - // Logger.LogError(ex, "Error in AddPersonImage"); - // } - //} - - personEntity.SetImage(new ItemImageInfo - { - Path = imageUrl, - Type = ImageType.Primary - }, 0); + if (libraryOptions.DownloadImagesInAdvance) + { + try + { + await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); + return; + } + catch (Exception ex) + { + Logger.LogError(ex, "Error in AddPersonImage"); + } + } + + personEntity.SetImage( + new ItemImageInfo + { + Path = imageUrl, + Type = ImageType.Primary + }, + 0); } protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index e67d1b8c37..d9a84be5cc 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -210,10 +210,10 @@ namespace MediaBrowser.Providers.Manager } /// - public Task SaveImage(User user, Stream source, string mimeType, string path) + public Task SaveImage(Stream source, string mimeType, string path) { return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger) - .SaveImage(user, source, path); + .SaveImage(source, path); } /// @@ -563,7 +563,7 @@ namespace MediaBrowser.Providers.Manager var pluginList = summary.Plugins.ToList(); AddMetadataPlugins(pluginList, dummy, libraryOptions, options); - AddImagePlugins(pluginList, dummy, imageProviders); + AddImagePlugins(pluginList, imageProviders); var subtitleProviders = _subtitleManager.GetSupportedProviders(dummy); @@ -594,14 +594,14 @@ namespace MediaBrowser.Providers.Manager var providers = GetMetadataProvidersInternal(item, libraryOptions, options, true, true).ToList(); // Locals - list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin + list.AddRange(providers.Where(i => i is ILocalMetadataProvider).Select(i => new MetadataPlugin { Name = i.Name, Type = MetadataPluginType.LocalMetadataProvider })); // Fetchers - list.AddRange(providers.Where(i => (i is IRemoteMetadataProvider)).Select(i => new MetadataPlugin + list.AddRange(providers.Where(i => i is IRemoteMetadataProvider).Select(i => new MetadataPlugin { Name = i.Name, Type = MetadataPluginType.MetadataFetcher @@ -615,11 +615,10 @@ namespace MediaBrowser.Providers.Manager })); } - private void AddImagePlugins(List list, T item, List imageProviders) - where T : BaseItem + private void AddImagePlugins(List list, List imageProviders) { // Locals - list.AddRange(imageProviders.Where(i => (i is ILocalImageProvider)).Select(i => new MetadataPlugin + list.AddRange(imageProviders.Where(i => i is ILocalImageProvider).Select(i => new MetadataPlugin { Name = i.Name, Type = MetadataPluginType.LocalImageProvider @@ -1166,12 +1165,32 @@ namespace MediaBrowser.Providers.Manager /// public void Dispose() { - _disposed = true; + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and optionally managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } if (!_disposeCancellationTokenSource.IsCancellationRequested) { _disposeCancellationTokenSource.Cancel(); } + + if (disposing) + { + _disposeCancellationTokenSource.Dispose(); + } + + _disposed = true; } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 69c6fd7222..77f03580ab 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -2,11 +2,9 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -17,7 +15,6 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.MediaInfo { @@ -25,19 +22,17 @@ namespace MediaBrowser.Providers.MediaInfo { private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; - private readonly IApplicationPaths _appPaths; - private readonly IJsonSerializer _json; private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public FFProbeAudioInfo(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json, ILibraryManager libraryManager) + public FFProbeAudioInfo( + IMediaSourceManager mediaSourceManager, + IMediaEncoder mediaEncoder, + IItemRepository itemRepo, + ILibraryManager libraryManager) { _mediaEncoder = mediaEncoder; _itemRepo = itemRepo; - _appPaths = appPaths; - _json = json; _libraryManager = libraryManager; _mediaSourceManager = mediaSourceManager; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 4fabe709be..9926275ae7 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -40,19 +40,15 @@ namespace MediaBrowser.Providers.MediaInfo IHasItemChangeMonitor { private readonly ILogger _logger; - private readonly IIsoManager _isoManager; private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; private readonly IBlurayExaminer _blurayExaminer; private readonly ILocalizationManager _localization; - private readonly IApplicationPaths _appPaths; - private readonly IJsonSerializer _json; private readonly IEncodingManager _encodingManager; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; private readonly IChapterManager _chapterManager; private readonly ILibraryManager _libraryManager; - private readonly IChannelManager _channelManager; private readonly IMediaSourceManager _mediaSourceManager; public string Name => "ffprobe"; @@ -126,14 +122,10 @@ namespace MediaBrowser.Providers.MediaInfo public FFProbeProvider( ILogger logger, IMediaSourceManager mediaSourceManager, - IChannelManager channelManager, - IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, - IApplicationPaths appPaths, - IJsonSerializer json, IEncodingManager encodingManager, IServerConfigurationManager config, ISubtitleManager subtitleManager, @@ -141,19 +133,15 @@ namespace MediaBrowser.Providers.MediaInfo ILibraryManager libraryManager) { _logger = logger; - _isoManager = isoManager; _mediaEncoder = mediaEncoder; _itemRepo = itemRepo; _blurayExaminer = blurayExaminer; _localization = localization; - _appPaths = appPaths; - _json = json; _encodingManager = encodingManager; _config = config; _subtitleManager = subtitleManager; _chapterManager = chapterManager; _libraryManager = libraryManager; - _channelManager = channelManager; _mediaSourceManager = mediaSourceManager; _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); @@ -211,9 +199,9 @@ namespace MediaBrowser.Providers.MediaInfo private string NormalizeStrmLine(string line) { - return line.Replace("\t", string.Empty) - .Replace("\r", string.Empty) - .Replace("\n", string.Empty) + return line.Replace("\t", string.Empty, StringComparison.Ordinal) + .Replace("\r", string.Empty, StringComparison.Ordinal) + .Replace("\n", string.Empty, StringComparison.Ordinal) .Trim(); } @@ -242,10 +230,11 @@ namespace MediaBrowser.Providers.MediaInfo FetchShortcutInfo(item); } - var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager); + var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _libraryManager); return prober.Probe(item, options, cancellationToken); } + // Run last public int Order => 100; } diff --git a/MediaBrowser.Providers/Music/Extensions.cs b/MediaBrowser.Providers/Music/Extensions.cs index b57d352560..dddfd02e4f 100644 --- a/MediaBrowser.Providers/Music/Extensions.cs +++ b/MediaBrowser.Providers/Music/Extensions.cs @@ -6,7 +6,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Providers.Music { - public static class Extensions + public static class AlbumInfoExtensions { public static string GetAlbumArtist(this AlbumInfo info) { @@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Music return id; } - return info.AlbumArtists.FirstOrDefault(); + return info.AlbumArtists.Count > 0 ? info.AlbumArtists[0] : default; } public static string GetReleaseGroupId(this AlbumInfo info) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs index 23acb7fd6a..3550614dd3 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs @@ -276,7 +276,7 @@ namespace MediaBrowser.Providers.Music private async Task GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken) { - var url = string.Format("/ws/2/release/?query=\"{0}\" AND arid:{1}", + var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/release/?query=\"{0}\" AND arid:{1}", WebUtility.UrlEncode(albumName), artistId); diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs index b829ed3780..781b716406 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.Music // They seem to throw bad request failures on any term with a slash var nameToSearch = searchInfo.Name.Replace('/', ' '); - var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch)); + var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch)); using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) await using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) @@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.Music if (HasDiacritics(searchInfo.Name)) { // Try again using the search with accent characters url - url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); + url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs index 41e664aace..c18725e0aa 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Net.Http; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; @@ -70,7 +71,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb list.Add(new RemoteImageInfo { ProviderName = Name, - Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId) + Url = string.Format(CultureInfo.InvariantCulture, "https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId) }); } } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index d2823a08c3..102ad82e17 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -127,7 +127,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var url = OmdbProvider.GetOmdbUrl(urlQuery, _appHost, cancellationToken); + var url = OmdbProvider.GetOmdbUrl(urlQuery); using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 6ad5298deb..c45149c3a7 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -256,16 +256,16 @@ namespace MediaBrowser.Providers.Plugins.Omdb return false; } - public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken) + public static string GetOmdbUrl(string query) { - const string url = "https://www.omdbapi.com?apikey=2c9d9507"; + const string Url = "https://www.omdbapi.com?apikey=2c9d9507"; if (string.IsNullOrWhiteSpace(query)) { - return url; + return Url; } - return url + "&" + query; + return Url + "&" + query; } private async Task EnsureItemInfo(string imdbId, CancellationToken cancellationToken) @@ -290,7 +290,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), _appHost, cancellationToken); + var url = GetOmdbUrl( + string.Format( + CultureInfo.InvariantCulture, + "i={0}&plot=short&tomatoes=true&r=json", + imdbParam)); using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); @@ -323,7 +327,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), _appHost, cancellationToken); + var url = GetOmdbUrl( + string.Format( + CultureInfo.InvariantCulture, + "i={0}&season={1}&detail=full", + imdbParam, + seasonId)); using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); @@ -348,7 +357,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb"); - var filename = string.Format("{0}.json", imdbId); + var filename = string.Format(CultureInfo.InvariantCulture, "{0}.json", imdbId); return Path.Combine(dataPath, filename); } @@ -362,7 +371,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb"); - var filename = string.Format("{0}_season_{1}.json", imdbId, seasonId); + var filename = string.Format(CultureInfo.InvariantCulture, "{0}_season_{1}.json", imdbId, seasonId); return Path.Combine(dataPath, filename); } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs index cd2f96f14a..f22d484abc 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -19,7 +20,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { private const string DefaultLanguage = "en"; - private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1); private readonly IMemoryCache _cache; private readonly TvDbClient _tvDbClient; private DateTime _tokenCreatedAt; @@ -176,7 +176,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb string language, CancellationToken cancellationToken) { - searchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), + searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb), out var seriesTvdbId); var episodeQuery = new EpisodeQuery(); @@ -203,10 +203,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb else if (searchInfo.PremiereDate.HasValue) { // tvdb expects yyyy-mm-dd format - episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd"); + episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); } - return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken); + return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), episodeQuery, language, cancellationToken); } public async Task GetEpisodeTvdbId( @@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken) .ConfigureAwait(false); - return episodePage.Data.FirstOrDefault()?.Id.ToString(); + return episodePage.Data.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture); } public Task> GetEpisodesPageAsync( @@ -276,23 +276,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb return cachedValue; } - await _cacheWriteLock.WaitAsync().ConfigureAwait(false); - try - { - if (_cache.TryGetValue(key, out cachedValue)) - { - return cachedValue; - } - - _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; - var result = await resultFactory.Invoke().ConfigureAwait(false); - _cache.Set(key, result, TimeSpan.FromHours(1)); - return result; - } - finally - { - _cacheWriteLock.Release(); - } + _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; + var result = await resultFactory.Invoke().ConfigureAwait(false); + _cache.Set(key, result, TimeSpan.FromHours(1)); + return result; } private static string GenerateKey(params object[] objects) diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs index 4d38d38dca..4e7c0e5a60 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; @@ -76,7 +77,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb var episodeResult = await _tvdbClientManager - .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken) + .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId, CultureInfo.InvariantCulture), language, cancellationToken) .ConfigureAwait(false); var image = GetImageInfo(episodeResult.Data); @@ -103,8 +104,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb return new RemoteImageInfo { - Width = Convert.ToInt32(episode.ThumbWidth), - Height = Convert.ToInt32(episode.ThumbHeight), + Width = Convert.ToInt32(episode.ThumbWidth, CultureInfo.InvariantCulture), + Height = Convert.ToInt32(episode.ThumbHeight, CultureInfo.InvariantCulture), ProviderName = Name, Url = TvdbUtils.BannerUrl + episode.Filename, Type = ImageType.Primary diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index 4f86a0293a..4da2c042f6 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -180,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); + url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); // Get images in english and with no language url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); @@ -250,7 +250,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { var path = GetDataPath(appPaths, tmdbId); - var filename = string.Format("all-{0}.json", preferredLanguage ?? string.Empty); + var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage ?? string.Empty); return Path.Combine(path, filename); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs index 27ca3759eb..01a887eed5 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs @@ -300,7 +300,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { movie.RemoteTrailers = movieData.Trailers.Youtube.Select(i => new MediaUrl { - Url = string.Format("https://www.youtube.com/watch?v={0}", i.Source), + Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", i.Source), Name = i.Name }).ToArray(); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs index 48f2a68a63..b7c4a56430 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs @@ -37,7 +37,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies ).* # Match rest of string", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); - private const string _searchURL = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; + private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; + private const string SearchUrlWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}&first_air_date_year={4}"; private readonly ILogger _logger; private readonly IJsonSerializer _json; @@ -124,7 +125,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies name2 = name2.Trim(); // Search again if the new name is different - if (!string.Equals(name2, name) && !string.IsNullOrWhiteSpace(name2)) + if (!string.Equals(name2, name, StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(name2)) { _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name2, year); results = await GetSearchResults(name2, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); @@ -164,10 +165,30 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { if (string.IsNullOrWhiteSpace(name)) { - throw new ArgumentException("name"); + throw new ArgumentException("String can't be null or empty.", nameof(name)); } - var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type); + string url3; + if (year != null && string.Equals(type, "movie", StringComparison.OrdinalIgnoreCase)) + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrl, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + type) + "&primary_release_year=" + year; + } + else + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrl, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + type); + } var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); foreach (var header in TmdbUtils.AcceptHeaders) @@ -207,10 +228,31 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { if (string.IsNullOrWhiteSpace(name)) { - throw new ArgumentException("name"); + throw new ArgumentException("String can't be null or empty.", nameof(name)); } - var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv"); + string url3; + if (year == null) + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrl, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + "tv"); + } + else + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrlWithYear, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + "tv", + year); + } var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); foreach (var header in TmdbUtils.AcceptHeaders) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index b4aef4542d..90e3cea932 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -131,7 +131,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { if (video.Site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase)) { - var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.Key); + var videoUrl = string.Format(CultureInfo.InvariantCulture, "http://www.youtube.com/watch?v={0}", video.Key); item.AddTrailerUrl(videoUrl); } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs index 154664321f..5705885b46 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - var filename = string.Format("season-{0}-episode-{1}-{2}.json", + var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-episode-{1}-{2}.json", seasonNumber.ToString(CultureInfo.InvariantCulture), episodeNumber.ToString(CultureInfo.InvariantCulture), preferredLanguage); @@ -116,7 +116,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", language); + url += string.Format(CultureInfo.InvariantCulture, "&language={0}", language); } var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 2b9077f557..e59504cc6d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -180,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - var filename = string.Format("season-{0}-{1}.json", + var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-{1}.json", seasonNumber.ToString(CultureInfo.InvariantCulture), preferredLanguage); @@ -203,7 +203,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); + url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); } var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index ac577b125a..0eded32330 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -496,7 +496,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var path = GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - var filename = string.Format("series-{0}.json", preferredLanguage ?? string.Empty); + var filename = string.Format(CultureInfo.InvariantCulture, "series-{0}.json", preferredLanguage ?? string.Empty); return Path.Combine(path, filename); } diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index dad155c815..321153c6be 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; @@ -100,7 +101,7 @@ namespace MediaBrowser.Providers.Studios private string GetUrl(string image, string filename) { - return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename); + return string.Format(CultureInfo.InvariantCulture, "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename); } private Task EnsureThumbsList(string file, CancellationToken cancellationToken) From e77a45adcf5241cb8c72d6786344228ae9d61c8e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 19 Aug 2020 17:52:14 +0200 Subject: [PATCH 075/102] Minor change --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index f1e120a649..04b208fdd1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -157,10 +157,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) { - var defaultValue = "HDHR"; + const string DefaultValue = "HDHR"; var response = new DiscoverResponse { - ModelNumber = defaultValue + ModelNumber = DefaultValue }; if (!string.IsNullOrEmpty(cacheKey)) { From 68edccd9f4e9137b13ad4006c19bb6582341aec6 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 19 Aug 2020 18:02:34 +0200 Subject: [PATCH 076/102] More warn --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 3 ++- .../Entities/IHasSpecialFeatures.cs | 5 ++++- MediaBrowser.Controller/Entities/Movies/Movie.cs | 15 ++++++++++----- .../Entities/UserViewBuilder.cs | 14 ++++++-------- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 04b208fdd1..2b5f69d41a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -37,6 +37,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly INetworkManager _networkManager; private readonly IStreamHelper _streamHelper; + private readonly Dictionary _modelCache = new Dictionary(); + public HdHomerunHost( IServerConfigurationManager config, ILogger logger, @@ -114,7 +116,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun }).Cast().ToList(); } - private readonly Dictionary _modelCache = new Dictionary(); private async Task GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken) { var cacheKey = info.Id; diff --git a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs index 688439e6c5..6a350212b1 100644 --- a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs +++ b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs @@ -1,4 +1,7 @@ +#pragma warning disable CS1591 + using System; +using System.Collections.Generic; namespace MediaBrowser.Controller.Entities { @@ -8,6 +11,6 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the special feature ids. /// /// The special feature ids. - Guid[] SpecialFeatureIds { get; set; } + IReadOnlyList SpecialFeatureIds { get; set; } } } diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 5ae396e68b..8b67aaccc6 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -1,3 +1,4 @@ +#pragma warning disable CS1591 using System; using System.Collections.Generic; @@ -19,8 +20,6 @@ namespace MediaBrowser.Controller.Entities.Movies /// public class Movie : Video, IHasSpecialFeatures, IHasTrailers, IHasLookupInfo, ISupportsBoxSetGrouping { - public Guid[] SpecialFeatureIds { get; set; } - public Movie() { SpecialFeatureIds = Array.Empty(); @@ -29,6 +28,9 @@ namespace MediaBrowser.Controller.Entities.Movies RemoteTrailerIds = Array.Empty(); } + /// + public IReadOnlyList SpecialFeatureIds { get; set; } + /// public IReadOnlyList LocalTrailerIds { get; set; } @@ -48,6 +50,9 @@ namespace MediaBrowser.Controller.Entities.Movies set => TmdbCollectionName = value; } + [JsonIgnore] + public override bool StopRefreshIfLocalMetadataFound => false; + public override double GetDefaultPrimaryImageAspectRatio() { // hack for tv plugins @@ -107,6 +112,7 @@ namespace MediaBrowser.Controller.Entities.Movies return itemsChanged; } + /// public override UnratedItem GetBlockUnratedType() { return UnratedItem.Movie; @@ -135,6 +141,7 @@ namespace MediaBrowser.Controller.Entities.Movies return info; } + /// public override bool BeforeMetadataRefresh(bool replaceAllMetdata) { var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata); @@ -171,6 +178,7 @@ namespace MediaBrowser.Controller.Entities.Movies return hasChanges; } + /// public override List GetRelatedUrls() { var list = base.GetRelatedUrls(); @@ -187,8 +195,5 @@ namespace MediaBrowser.Controller.Entities.Movies return list; } - - [JsonIgnore] - public override bool StopRefreshIfLocalMetadataFound => false; } } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 22bb7fd550..e3f4025bbe 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -672,9 +674,7 @@ namespace MediaBrowser.Controller.Entities var isPlaceHolder = false; - var hasPlaceHolder = item as ISupportsPlaceHolders; - - if (hasPlaceHolder != null) + if (item is ISupportsPlaceHolders hasPlaceHolder) { isPlaceHolder = hasPlaceHolder.IsPlaceHolder; } @@ -689,13 +689,11 @@ namespace MediaBrowser.Controller.Entities { var filterValue = query.HasSpecialFeature.Value; - var movie = item as IHasSpecialFeatures; - - if (movie != null) + if (item is IHasSpecialFeatures movie) { var ok = filterValue - ? movie.SpecialFeatureIds.Length > 0 - : movie.SpecialFeatureIds.Length == 0; + ? movie.SpecialFeatureIds.Count > 0 + : movie.SpecialFeatureIds.Count == 0; if (!ok) { From ce352e4a1d272e3d81ce2c41704e8564db5cec48 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 19 Aug 2020 18:06:27 +0200 Subject: [PATCH 077/102] Address comments --- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 14 +++++++------- .../Plugins/Tmdb/Movies/TmdbSearch.cs | 13 ++++++------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 685561e2dc..51c0b7e83b 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -30,6 +30,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// public class TmdbMovieProvider : IRemoteMetadataProvider, IHasOrder { + private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}"; + private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; + internal static TmdbMovieProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; @@ -157,9 +160,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies return _tmdbSettings; } - private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}"; - private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; - /// /// Gets the movie data path. /// @@ -334,7 +334,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var mainResponse = await GetMovieDbResponse(requestMessage); + using var mainResponse = await GetMovieDbResponse(requestMessage).ConfigureAwait(false); if (mainResponse.StatusCode == HttpStatusCode.NotFound) { return null; @@ -367,7 +367,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var langResponse = await GetMovieDbResponse(langRequestMessage); + using var langResponse = await GetMovieDbResponse(langRequestMessage).ConfigureAwait(false); await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); var langResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); @@ -380,10 +380,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// /// Gets the movie db response. /// - internal async Task GetMovieDbResponse(HttpRequestMessage message) + internal Task GetMovieDbResponse(HttpRequestMessage message) { message.Headers.UserAgent.Add(new ProductInfoHeaderValue(_appHost.ApplicationUserAgent)); - return await _httpClientFactory.CreateClient().SendAsync(message); + return _httpClientFactory.CreateClient().SendAsync(message); } /// diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs index b7c4a56430..1332ad87c3 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs @@ -38,8 +38,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; - private const string SearchUrlWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}&first_air_date_year={4}"; - + private const string SearchUrlTvWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/tv?api_key={1}&query={0}&language={2}&first_air_date_year={3}"; + private const string SearchUrlMovieWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/movie?api_key={1}&query={0}&language={2}&primary_release_year={3}"; private readonly ILogger _logger; private readonly IJsonSerializer _json; private readonly ILibraryManager _libraryManager; @@ -173,11 +173,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { url3 = string.Format( CultureInfo.InvariantCulture, - SearchUrl, + SearchUrlMovieWithYear, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, - type) + "&primary_release_year=" + year; + year); } else { @@ -246,15 +246,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { url3 = string.Format( CultureInfo.InvariantCulture, - SearchUrlWithYear, + SearchUrlTvWithYear, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, - "tv", year); } - var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); foreach (var header in TmdbUtils.AcceptHeaders) { requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); From 5d0a17fd422c983776aa6d2e3a698ad9ffd2c250 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 19 Aug 2020 18:57:47 +0200 Subject: [PATCH 078/102] More fixes --- .../Plugins/Tmdb/Movies/TmdbSearch.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs index 1332ad87c3..2a6c6d0350 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs @@ -22,11 +22,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { public class TmdbSearch { + private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; + private const string SearchUrlTvWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/tv?api_key={1}&query={0}&language={2}&first_air_date_year={3}"; + private const string SearchUrlMovieWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/movie?api_key={1}&query={0}&language={2}&primary_release_year={3}"; + private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); private static readonly Regex _cleanEnclosed = new Regex(@"\p{Ps}.*\p{Pe}", RegexOptions.Compiled); private static readonly Regex _cleanNonWord = new Regex(@"[\W_]+", RegexOptions.Compiled); - private static readonly Regex _cleanStopWords = new Regex(@"\b( # Start at word boundary + private static readonly Regex _cleanStopWords = new Regex( + @"\b( # Start at word boundary 19[0-9]{2}|20[0-9]{2}| # 1900-2099 S[0-9]{2}| # Season E[0-9]{2}| # Episode @@ -37,9 +42,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies ).* # Match rest of string", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); - private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; - private const string SearchUrlTvWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/tv?api_key={1}&query={0}&language={2}&first_air_date_year={3}"; - private const string SearchUrlMovieWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/movie?api_key={1}&query={0}&language={2}&primary_release_year={3}"; private readonly ILogger _logger; private readonly IJsonSerializer _json; private readonly ILibraryManager _libraryManager; @@ -190,7 +192,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies type); } - var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); foreach (var header in TmdbUtils.AcceptHeaders) { requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); @@ -268,7 +270,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies return results .Select(i => { - var remoteResult = new RemoteSearchResult {SearchProviderName = TmdbMovieProvider.Current.Name, Name = i.Name ?? i.Original_Name, ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path}; + var remoteResult = new RemoteSearchResult + { + SearchProviderName = TmdbMovieProvider.Current.Name, + Name = i.Name ?? i.Original_Name, + ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path + }; if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) { From 11e3cdb3cba8932ee2aca39fec9275329c6f5c59 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 19 Aug 2020 15:08:28 -0600 Subject: [PATCH 079/102] Fix conflicting audio routes --- Jellyfin.Api/Controllers/AudioController.cs | 4 ++-- Jellyfin.Api/Controllers/UniversalAudioController.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index d300b4891e..802cd026e0 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -83,9 +83,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The streaming options. /// Audio stream returned. /// A containing the audio file. - [HttpGet("{itemId}/{stream=stream}.{container?}", Name = "GetAudioStreamByContainer")] + [HttpGet("{itemId}/stream.{container}", Name = "GetAudioStreamByContainer")] [HttpGet("{itemId}/stream", Name = "GetAudioStream")] - [HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadAudioStreamByContainer")] + [HttpHead("{itemId}/stream.{container}", Name = "HeadAudioStreamByContainer")] [HttpHead("{itemId}/stream", Name = "HeadAudioStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetAudioStream( diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 3994fb9958..b13cf9fa51 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -85,9 +85,9 @@ namespace Jellyfin.Api.Controllers /// Redirected to remote audio stream. /// A containing the audio file. [HttpGet("Audio/{itemId}/universal")] - [HttpGet("Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")] + [HttpGet("Audio/{itemId}/universal.{container}", Name = "GetUniversalAudioStream_2")] [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")] - [HttpHead("Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")] + [HttpHead("Audio/{itemId}/universal.{container}", Name = "HeadUniversalAudioStream_2")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] From 3d840473209bcf1d0a706c779a367fc45e90da40 Mon Sep 17 00:00:00 2001 From: IR0NCaT Date: Wed, 19 Aug 2020 21:52:40 +0000 Subject: [PATCH 080/102] 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 71ee6446ce..648aa384b0 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -21,7 +21,7 @@ "HeaderFavoriteAlbums": "Избранные альбомы", "HeaderFavoriteArtists": "Избранные исполнители", "HeaderFavoriteEpisodes": "Избранные эпизоды", - "HeaderFavoriteShows": "Избранные передачи", + "HeaderFavoriteShows": "Избранные сериалы", "HeaderFavoriteSongs": "Избранные композиции", "HeaderLiveTV": "Эфир", "HeaderNextUp": "Очередное", From ab2147751f9079bc104da068909a485fc9402a64 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 12:16:24 +0200 Subject: [PATCH 081/102] Make MediaBrowser.MediaEncoding warnings free --- Emby.Dlna/Main/DlnaEntryPoint.cs | 4 +- Emby.Dlna/PlayTo/uBaseObject.cs | 2 +- Emby.Dlna/PlayTo/uPnpNamespaces.cs | 2 +- .../Data/SqliteItemRepository.cs | 7 +- .../IO/LibraryMonitor.cs | 27 +--- .../IO/LibraryMonitorStartup.cs | 35 +++++ .../Library/LibraryManager.cs | 2 +- .../LiveTv/EmbyTV/EntryPoint.cs | 2 +- .../LiveTv/Listings/SchedulesDirect.cs | 2 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 148 +++++++++--------- .../ScheduledTasks/TaskManager.cs | 1 + .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 64 ++++---- .../Services/ServicePath.cs | 8 +- .../Entities/UserViewBuilder.cs | 1 - .../Attachments/AttachmentExtractor.cs | 4 +- .../Encoder/EncodingUtils.cs | 5 +- .../Encoder/MediaEncoder.cs | 4 +- .../MediaBrowser.MediaEncoding.csproj | 2 +- .../Probing/ProbeResultNormalizer.cs | 9 +- .../Subtitles/AssParser.cs | 8 +- .../Subtitles/SsaParser.cs | 95 +++++------ .../Subtitles/SubtitleEncoder.cs | 17 +- 22 files changed, 236 insertions(+), 213 deletions(-) create mode 100644 Emby.Server.Implementations/IO/LibraryMonitorStartup.cs diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index a21d4cc11d..191763de4f 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -30,7 +30,7 @@ using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Dlna.Main { - public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup + public sealed class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup { private readonly IServerConfigurationManager _config; private readonly ILogger _logger; @@ -60,7 +60,7 @@ namespace Emby.Dlna.Main public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; } - public static DlnaEntryPoint Current; + public static DlnaEntryPoint Current { get; private set; }; public DlnaEntryPoint( IServerConfigurationManager config, diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs index 05c19299f1..f2dc31f6dd 100644 --- a/Emby.Dlna/PlayTo/uBaseObject.cs +++ b/Emby.Dlna/PlayTo/uBaseObject.cs @@ -31,7 +31,7 @@ namespace Emby.Dlna.PlayTo throw new ArgumentNullException(nameof(obj)); } - return string.Equals(Id, obj.Id); + return string.Equals(Id, obj.Id, StringComparison.Ordinal); } public string MediaType diff --git a/Emby.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs index dc65cdf43c..6ea7dc9cf7 100644 --- a/Emby.Dlna/PlayTo/uPnpNamespaces.cs +++ b/Emby.Dlna/PlayTo/uPnpNamespaces.cs @@ -4,7 +4,7 @@ using System.Xml.Linq; namespace Emby.Dlna.PlayTo { - public class uPnpNamespaces + public static class uPnpNamespaces { public static XNamespace dc = "http://purl.org/dc/elements/1.1/"; public static XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 331ffc1349..5bf740cfcc 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4308,7 +4308,7 @@ namespace Emby.Server.Implementations.Data whereClauses.Add("ProductionYear=@Years"); if (statement != null) { - statement.TryBind("@Years", query.Years[0].ToString()); + statement.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture)); } } else if (query.Years.Length > 1) @@ -5170,7 +5170,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type insertText.Append(','); } - insertText.AppendFormat("(@ItemId, @AncestorId{0}, @AncestorIdText{0})", i.ToString(CultureInfo.InvariantCulture)); + insertText.AppendFormat( + CultureInfo.InvariantCulture, + "(@ItemId, @AncestorId{0}, @AncestorIdText{0})", + i.ToString(CultureInfo.InvariantCulture)); } using (var statement = PrepareStatement(db, insertText.ToString())) diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index a32b03aaa9..9290dfcd0e 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -6,12 +6,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Emby.Server.Implementations.Library; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.IO; -using Emby.Server.Implementations.Library; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.IO @@ -38,6 +37,8 @@ namespace Emby.Server.Implementations.IO /// private readonly ConcurrentDictionary _tempIgnoredPaths = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private bool _disposed = false; + /// /// Add the path to our temporary ignore list. Use when writing to a path within our listening scope. /// @@ -492,8 +493,6 @@ namespace Emby.Server.Implementations.IO } } - private bool _disposed = false; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -522,24 +521,4 @@ namespace Emby.Server.Implementations.IO _disposed = true; } } - - public class LibraryMonitorStartup : IServerEntryPoint - { - private readonly ILibraryMonitor _monitor; - - public LibraryMonitorStartup(ILibraryMonitor monitor) - { - _monitor = monitor; - } - - public Task RunAsync() - { - _monitor.Start(); - return Task.CompletedTask; - } - - public void Dispose() - { - } - } } diff --git a/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs b/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs new file mode 100644 index 0000000000..c51cf05459 --- /dev/null +++ b/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Plugins; + +namespace Emby.Server.Implementations.IO +{ + /// + /// which is responsible for starting the library monitor. + /// + public sealed class LibraryMonitorStartup : IServerEntryPoint + { + private readonly ILibraryMonitor _monitor; + + /// + /// Initializes a new instance of the class. + /// + /// The library monitor. + public LibraryMonitorStartup(ILibraryMonitor monitor) + { + _monitor = monitor; + } + + /// + public Task RunAsync() + { + _monitor.Start(); + return Task.CompletedTask; + } + + /// + public void Dispose() + { + } + } +} diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 7b770d9400..7ed8f0bbfb 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -729,7 +729,7 @@ namespace Emby.Server.Implementations.Library Directory.CreateDirectory(rootFolderPath); var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? - ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath))) + ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath))) .DeepCopy(); // In case program data folder was moved diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs index 69a9cb78aa..a2ec2df375 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs @@ -5,7 +5,7 @@ using MediaBrowser.Controller.Plugins; namespace Emby.Server.Implementations.LiveTv.EmbyTV { - public class EntryPoint : IServerEntryPoint + public sealed class EntryPoint : IServerEntryPoint { /// public Task RunAsync() diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 77a7069eb8..c4d5cc58aa 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -929,7 +929,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings private static string NormalizeName(string value) { - return value.Replace(" ", string.Empty).Replace("-", string.Empty); + return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal); } public class ScheduleDirect diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 8a900f42cd..1ef083d048 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -10,7 +10,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; using MediaBrowser.Model.Events; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; @@ -22,37 +21,53 @@ namespace Emby.Server.Implementations.ScheduledTasks /// public class ScheduledTaskWorker : IScheduledTaskWorker { - public event EventHandler> TaskProgress; - - /// - /// Gets the scheduled task. - /// - /// The scheduled task. - public IScheduledTask ScheduledTask { get; private set; } - /// /// Gets or sets the json serializer. /// /// The json serializer. - private IJsonSerializer JsonSerializer { get; set; } + private readonly IJsonSerializer _jsonSerializer; /// /// Gets or sets the application paths. /// /// The application paths. - private IApplicationPaths ApplicationPaths { get; set; } + private readonly IApplicationPaths _applicationPaths; /// - /// Gets the logger. + /// Gets or sets the logger. /// /// The logger. - private ILogger Logger { get; set; } + private readonly ILogger _logger; /// - /// Gets the task manager. + /// Gets or sets the task manager. /// /// The task manager. - private ITaskManager TaskManager { get; set; } + private readonly ITaskManager _taskManager; + + /// + /// The _last execution result sync lock. + /// + private readonly object _lastExecutionResultSyncLock = new object(); + + private bool _readFromFile = false; + + /// + /// The _last execution result. + /// + private TaskResult _lastExecutionResult; + + private Task _currentTask; + + /// + /// The _triggers. + /// + private Tuple[] _triggers; + + /// + /// The _id. + /// + private string _id; /// /// Initializes a new instance of the class. @@ -71,7 +86,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// or /// jsonSerializer /// or - /// logger + /// logger. /// public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger) { @@ -101,23 +116,22 @@ namespace Emby.Server.Implementations.ScheduledTasks } ScheduledTask = scheduledTask; - ApplicationPaths = applicationPaths; - TaskManager = taskManager; - JsonSerializer = jsonSerializer; - Logger = logger; + _applicationPaths = applicationPaths; + _taskManager = taskManager; + _jsonSerializer = jsonSerializer; + _logger = logger; InitTriggerEvents(); } - private bool _readFromFile = false; - /// - /// The _last execution result. - /// - private TaskResult _lastExecutionResult; + public event EventHandler> TaskProgress; + /// - /// The _last execution result sync lock. + /// Gets the scheduled task. /// - private readonly object _lastExecutionResultSyncLock = new object(); + /// The scheduled task. + public IScheduledTask ScheduledTask { get; private set; } + /// /// Gets the last execution result. /// @@ -136,11 +150,11 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - _lastExecutionResult = JsonSerializer.DeserializeFromFile(path); + _lastExecutionResult = _jsonSerializer.DeserializeFromFile(path); } catch (Exception ex) { - Logger.LogError(ex, "Error deserializing {File}", path); + _logger.LogError(ex, "Error deserializing {File}", path); } } @@ -160,7 +174,7 @@ namespace Emby.Server.Implementations.ScheduledTasks lock (_lastExecutionResultSyncLock) { - JsonSerializer.SerializeToFile(value, path); + _jsonSerializer.SerializeToFile(value, path); } } } @@ -184,7 +198,7 @@ namespace Emby.Server.Implementations.ScheduledTasks public string Category => ScheduledTask.Category; /// - /// Gets the current cancellation token. + /// Gets or sets the current cancellation token. /// /// The current cancellation token source. private CancellationTokenSource CurrentCancellationTokenSource { get; set; } @@ -221,12 +235,7 @@ namespace Emby.Server.Implementations.ScheduledTasks public double? CurrentProgress { get; private set; } /// - /// The _triggers. - /// - private Tuple[] _triggers; - - /// - /// Gets the triggers that define when the task will run. + /// Gets or sets the triggers that define when the task will run. /// /// The triggers. private Tuple[] InternalTriggers @@ -255,7 +264,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// Gets the triggers that define when the task will run. /// /// The triggers. - /// value + /// value is null. public TaskTriggerInfo[] Triggers { get @@ -280,11 +289,6 @@ namespace Emby.Server.Implementations.ScheduledTasks } } - /// - /// The _id. - /// - private string _id; - /// /// Gets the unique id. /// @@ -325,9 +329,9 @@ namespace Emby.Server.Implementations.ScheduledTasks trigger.Stop(); - trigger.Triggered -= trigger_Triggered; - trigger.Triggered += trigger_Triggered; - trigger.Start(LastExecutionResult, Logger, Name, isApplicationStartup); + trigger.Triggered -= OnTriggerTriggered; + trigger.Triggered += OnTriggerTriggered; + trigger.Start(LastExecutionResult, _logger, Name, isApplicationStartup); } } @@ -336,7 +340,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// /// The source of the event. /// The instance containing the event data. - async void trigger_Triggered(object sender, EventArgs e) + private async void OnTriggerTriggered(object sender, EventArgs e) { var trigger = (ITaskTrigger)sender; @@ -347,19 +351,17 @@ namespace Emby.Server.Implementations.ScheduledTasks return; } - Logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name); + _logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name); trigger.Stop(); - TaskManager.QueueScheduledTask(ScheduledTask, trigger.TaskOptions); + _taskManager.QueueScheduledTask(ScheduledTask, trigger.TaskOptions); await Task.Delay(1000).ConfigureAwait(false); - trigger.Start(LastExecutionResult, Logger, Name, false); + trigger.Start(LastExecutionResult, _logger, Name, false); } - private Task _currentTask; - /// /// Executes the task. /// @@ -395,9 +397,9 @@ namespace Emby.Server.Implementations.ScheduledTasks CurrentCancellationTokenSource = new CancellationTokenSource(); - Logger.LogInformation("Executing {0}", Name); + _logger.LogInformation("Executing {0}", Name); - ((TaskManager)TaskManager).OnTaskExecuting(this); + ((TaskManager)_taskManager).OnTaskExecuting(this); progress.ProgressChanged += OnProgressChanged; @@ -423,7 +425,7 @@ namespace Emby.Server.Implementations.ScheduledTasks } catch (Exception ex) { - Logger.LogError(ex, "Error"); + _logger.LogError(ex, "Error"); failureException = ex; @@ -476,7 +478,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { if (State == TaskState.Running) { - Logger.LogInformation("Attempting to cancel Scheduled Task {0}", Name); + _logger.LogInformation("Attempting to cancel Scheduled Task {0}", Name); CurrentCancellationTokenSource.Cancel(); } } @@ -487,7 +489,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// System.String. private string GetScheduledTasksConfigurationDirectory() { - return Path.Combine(ApplicationPaths.ConfigurationDirectoryPath, "ScheduledTasks"); + return Path.Combine(_applicationPaths.ConfigurationDirectoryPath, "ScheduledTasks"); } /// @@ -496,7 +498,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// System.String. private string GetScheduledTasksDataDirectory() { - return Path.Combine(ApplicationPaths.DataPath, "ScheduledTasks"); + return Path.Combine(_applicationPaths.DataPath, "ScheduledTasks"); } /// @@ -535,7 +537,7 @@ namespace Emby.Server.Implementations.ScheduledTasks TaskTriggerInfo[] list = null; if (File.Exists(path)) { - list = JsonSerializer.DeserializeFromFile(path); + list = _jsonSerializer.DeserializeFromFile(path); } // Return defaults if file doesn't exist. @@ -571,7 +573,7 @@ namespace Emby.Server.Implementations.ScheduledTasks Directory.CreateDirectory(Path.GetDirectoryName(path)); - JsonSerializer.SerializeToFile(triggers, path); + _jsonSerializer.SerializeToFile(triggers, path); } /// @@ -585,7 +587,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { var elapsedTime = endTime - startTime; - Logger.LogInformation("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds); + _logger.LogInformation("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds); var result = new TaskResult { @@ -606,7 +608,7 @@ namespace Emby.Server.Implementations.ScheduledTasks LastExecutionResult = result; - ((TaskManager)TaskManager).OnTaskCompleted(this, result); + ((TaskManager)_taskManager).OnTaskCompleted(this, result); } /// @@ -615,6 +617,7 @@ namespace Emby.Server.Implementations.ScheduledTasks public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } /// @@ -635,12 +638,12 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - Logger.LogInformation(Name + ": Cancelling"); + _logger.LogInformation(Name + ": Cancelling"); token.Cancel(); } catch (Exception ex) { - Logger.LogError(ex, "Error calling CancellationToken.Cancel();"); + _logger.LogError(ex, "Error calling CancellationToken.Cancel();"); } } @@ -649,21 +652,21 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - Logger.LogInformation(Name + ": Waiting on Task"); + _logger.LogInformation(Name + ": Waiting on Task"); var exited = Task.WaitAll(new[] { task }, 2000); if (exited) { - Logger.LogInformation(Name + ": Task exited"); + _logger.LogInformation(Name + ": Task exited"); } else { - Logger.LogInformation(Name + ": Timed out waiting for task to stop"); + _logger.LogInformation(Name + ": Timed out waiting for task to stop"); } } catch (Exception ex) { - Logger.LogError(ex, "Error calling Task.WaitAll();"); + _logger.LogError(ex, "Error calling Task.WaitAll();"); } } @@ -671,12 +674,12 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - Logger.LogDebug(Name + ": Disposing CancellationToken"); + _logger.LogDebug(Name + ": Disposing CancellationToken"); token.Dispose(); } catch (Exception ex) { - Logger.LogError(ex, "Error calling CancellationToken.Dispose();"); + _logger.LogError(ex, "Error calling CancellationToken.Dispose();"); } } @@ -692,8 +695,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// /// The info. /// BaseTaskTrigger. - /// - /// Invalid trigger type: + info.Type + /// Invalid trigger type: + info.Type. private ITaskTrigger GetTrigger(TaskTriggerInfo info) { var options = new TaskOptions @@ -765,7 +767,7 @@ namespace Emby.Server.Implementations.ScheduledTasks foreach (var triggerInfo in InternalTriggers) { var trigger = triggerInfo.Item2; - trigger.Triggered -= trigger_Triggered; + trigger.Triggered -= OnTriggerTriggered; trigger.Stop(); } } diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index 81096026bd..6d2b4ffc8d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -207,6 +207,7 @@ namespace Emby.Server.Implementations.ScheduledTasks public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } /// diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index 402b39a263..54e18eaea0 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks.Tasks { @@ -15,12 +16,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask { - /// - /// Gets or sets the configuration manager. - /// - /// The configuration manager. - private IConfigurationManager ConfigurationManager { get; set; } - + private readonly IConfigurationManager _configurationManager; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; @@ -32,18 +28,43 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// The localization manager. public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization) { - ConfigurationManager = configurationManager; + _configurationManager = configurationManager; _fileSystem = fileSystem; _localization = localization; } + /// + public string Name => _localization.GetLocalizedString("TaskCleanLogs"); + + /// + public string Description => string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("TaskCleanLogsDescription"), + _configurationManager.CommonConfiguration.LogFileRetentionDays); + + /// + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); + + /// + public string Key => "CleanLogFiles"; + + /// + public bool IsHidden => false; + + /// + public bool IsEnabled => true; + + /// + public bool IsLogged => true; + /// /// Creates the triggers that define when the task will run. /// /// IEnumerable{BaseTaskTrigger}. public IEnumerable GetDefaultTriggers() { - return new[] { + return new[] + { new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} }; } @@ -57,10 +78,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks public Task Execute(CancellationToken cancellationToken, IProgress progress) { // Delete log files more than n days old - var minDateModified = DateTime.UtcNow.AddDays(-ConfigurationManager.CommonConfiguration.LogFileRetentionDays); + var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays); // Only delete the .txt log files, the *.log files created by serilog get managed by itself - var filesToDelete = _fileSystem.GetFiles(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true) + var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true) .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified) .ToList(); @@ -83,26 +104,5 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks return Task.CompletedTask; } - - /// - public string Name => _localization.GetLocalizedString("TaskCleanLogs"); - - /// - public string Description => string.Format(_localization.GetLocalizedString("TaskCleanLogsDescription"), ConfigurationManager.CommonConfiguration.LogFileRetentionDays); - - /// - public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); - - /// - public string Key => "CleanLogFiles"; - - /// - public bool IsHidden => false; - - /// - public bool IsEnabled => true; - - /// - public bool IsLogged => true; } } diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs index 442b2ab1c5..0d4728b43c 100644 --- a/Emby.Server.Implementations/Services/ServicePath.cs +++ b/Emby.Server.Implementations/Services/ServicePath.cs @@ -80,8 +80,8 @@ namespace Emby.Server.Implementations.Services public static List GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching) { - const string hashPrefix = WildCard + PathSeperator; - return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching); + const string HashPrefix = WildCard + PathSeperator; + return GetPotentialMatchesWithPrefix(HashPrefix, pathPartsForMatching); } private static List GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching) @@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Services { list.Add(hashPrefix + part); - if (part.IndexOf(ComponentSeperator) == -1) + if (part.IndexOf(ComponentSeperator, StringComparison.Ordinal) == -1) { continue; } @@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.Services } if (component.IndexOf(VariablePrefix, StringComparison.OrdinalIgnoreCase) != -1 - && component.IndexOf(ComponentSeperator) != -1) + && component.IndexOf(ComponentSeperator, StringComparison.Ordinal) != -1) { hasSeparators.Add(true); componentsList.AddRange(component.Split(ComponentSeperator)); diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index e3f4025bbe..b384b27d1a 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -10,7 +10,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.TV; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index a8ebe6bc54..21b5d0c5be 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -240,11 +240,11 @@ namespace MediaBrowser.MediaEncoding.Attachments if (protocol == MediaProtocol.File) { var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); - filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); + filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture); } else { - filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); + filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture); } var prefix = filename.Substring(0, 1); diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index 082ae2888d..63310fdf6b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -48,7 +49,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// System.String. private static string GetFileInputArgument(string path) { - if (path.IndexOf("://") != -1) + if (path.IndexOf("://", StringComparison.Ordinal) != -1) { return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", path); } @@ -67,7 +68,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private static string NormalizePath(string path) { // Quotes are valid path characters in linux and they need to be escaped here with a leading \ - return path.Replace("\"", "\\\""); + return path.Replace("\"", "\\\"", StringComparison.Ordinal); } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index b9a6432ad2..7449e4a624 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -377,7 +377,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = extractChapters ? "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_chapters -show_format" : "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_format"; - args = string.Format(args, probeSizeArgument, inputPath).Trim(); + args = string.Format(CultureInfo.InvariantCulture, args, probeSizeArgument, inputPath).Trim(); var process = new Process { @@ -856,7 +856,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping // We need to double escape - return path.Replace('\\', '/').Replace(":", "\\:").Replace("'", "'\\\\\\''"); + return path.Replace('\\', '/').Replace(":", "\\:", StringComparison.Ordinal).Replace("'", "'\\\\\\''", StringComparison.Ordinal); } /// diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 017f917e27..814edd7323 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -34,7 +34,7 @@ - + diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 19e3bd8e60..40a3b43e1c 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -42,7 +42,8 @@ namespace MediaBrowser.MediaEncoding.Probing var info = new MediaInfo { Path = path, - Protocol = protocol + Protocol = protocol, + VideoType = videoType }; FFProbeHelpers.NormalizeFFProbeResult(data); @@ -1133,7 +1134,7 @@ namespace MediaBrowser.MediaEncoding.Probing { // Only use the comma as a delimeter if there are no slashes or pipes. // We want to be careful not to split names that have commas in them - var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i) != -1) ? + var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i, StringComparison.Ordinal) != -1) ? _nameDelimiters : new[] { ',' }; @@ -1377,8 +1378,8 @@ namespace MediaBrowser.MediaEncoding.Probing if (subtitle.Contains('/', StringComparison.Ordinal)) // It contains a episode number and season number { string[] numbers = subtitle.Split(' '); - video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0]); - int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1]); + video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0], CultureInfo.InvariantCulture); + int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1], CultureInfo.InvariantCulture); description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it } diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs index 308b62886c..86b87fddd8 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -86,9 +86,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles private void RemoteNativeFormatting(SubtitleTrackEvent p) { - int indexOfBegin = p.Text.IndexOf('{'); + int indexOfBegin = p.Text.IndexOf('{', StringComparison.Ordinal); string pre = string.Empty; - while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin) + while (indexOfBegin >= 0 && p.Text.IndexOf('}', StringComparison.Ordinal) > indexOfBegin) { string s = p.Text.Substring(indexOfBegin); if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || @@ -116,10 +116,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles pre = s.Substring(0, 5) + "}"; } - int indexOfEnd = p.Text.IndexOf('}'); + int indexOfEnd = p.Text.IndexOf('}', StringComparison.Ordinal); p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1); - indexOfBegin = p.Text.IndexOf('{'); + indexOfBegin = p.Text.IndexOf('{', StringComparison.Ordinal); } p.Text = pre + p.Text; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs index 6b7a81e6eb..a5d6417473 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text; using System.Threading; @@ -50,14 +51,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles { eventsStarted = true; } - else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(";")) + else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(";", StringComparison.Ordinal)) { // skip comment lines } else if (eventsStarted && line.Trim().Length > 0) { string s = line.Trim().ToLowerInvariant(); - if (s.StartsWith("format:")) + if (s.StartsWith("format:", StringComparison.Ordinal)) { if (line.Length > 10) { @@ -103,7 +104,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles string[] splittedLine; - if (s.StartsWith("dialogue:")) + if (s.StartsWith("dialogue:", StringComparison.Ordinal)) { splittedLine = line.Substring(10).Split(','); } @@ -181,10 +182,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles string[] timeCode = time.Split(':', '.'); return new TimeSpan( 0, - int.Parse(timeCode[0]), - int.Parse(timeCode[1]), - int.Parse(timeCode[2]), - int.Parse(timeCode[3]) * 10).Ticks; + int.Parse(timeCode[0], CultureInfo.InvariantCulture), + int.Parse(timeCode[1], CultureInfo.InvariantCulture), + int.Parse(timeCode[2], CultureInfo.InvariantCulture), + int.Parse(timeCode[3], CultureInfo.InvariantCulture) * 10).Ticks; } private static string GetFormattedText(string text) @@ -193,11 +194,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles for (int i = 0; i < 10; i++) // just look ten times... { - if (text.Contains(@"{\fn")) + if (text.Contains(@"{\fn", StringComparison.Ordinal)) { - int start = text.IndexOf(@"{\fn"); + int start = text.IndexOf(@"{\fn", StringComparison.Ordinal); int end = text.IndexOf('}', start); - if (end > 0 && !text.Substring(start).StartsWith("{\\fn}")) + if (end > 0 && !text.Substring(start).StartsWith("{\\fn}", StringComparison.Ordinal)) { string fontName = text.Substring(start + 4, end - (start + 4)); string extraTags = string.Empty; @@ -212,7 +213,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles text = text.Insert(start, ""); } - int indexOfEndTag = text.IndexOf("{\\fn}", start); + int indexOfEndTag = text.IndexOf("{\\fn}", start, StringComparison.Ordinal); if (indexOfEndTag > 0) { text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, ""); @@ -224,11 +225,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - if (text.Contains(@"{\fs")) + if (text.Contains(@"{\fs", StringComparison.Ordinal)) { - int start = text.IndexOf(@"{\fs"); + int start = text.IndexOf(@"{\fs", StringComparison.Ordinal); int end = text.IndexOf('}', start); - if (end > 0 && !text.Substring(start).StartsWith("{\\fs}")) + if (end > 0 && !text.Substring(start).StartsWith("{\\fs}", StringComparison.Ordinal)) { string fontSize = text.Substring(start + 4, end - (start + 4)); string extraTags = string.Empty; @@ -245,7 +246,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles text = text.Insert(start, ""); } - int indexOfEndTag = text.IndexOf("{\\fs}", start); + int indexOfEndTag = text.IndexOf("{\\fs}", start, StringComparison.Ordinal); if (indexOfEndTag > 0) { text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, ""); @@ -258,17 +259,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - if (text.Contains(@"{\c")) + if (text.Contains(@"{\c", StringComparison.Ordinal)) { - int start = text.IndexOf(@"{\c"); + int start = text.IndexOf(@"{\c", StringComparison.Ordinal); int end = text.IndexOf('}', start); - if (end > 0 && !text.Substring(start).StartsWith("{\\c}")) + if (end > 0 && !text.Substring(start).StartsWith("{\\c}", StringComparison.Ordinal)) { string color = text.Substring(start + 4, end - (start + 4)); string extraTags = string.Empty; CheckAndAddSubTags(ref color, ref extraTags, out bool italic); - color = color.Replace("&", string.Empty).TrimStart('H'); + color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H'); color = color.PadLeft(6, '0'); // switch to rrggbb from bbggrr @@ -285,7 +286,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles text = text.Insert(start, ""); } - int indexOfEndTag = text.IndexOf("{\\c}", start); + int indexOfEndTag = text.IndexOf("{\\c}", start, StringComparison.Ordinal); if (indexOfEndTag > 0) { text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, ""); @@ -297,17 +298,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - if (text.Contains(@"{\1c")) // "1" specifices primary color + if (text.Contains(@"{\1c", StringComparison.Ordinal)) // "1" specifices primary color { - int start = text.IndexOf(@"{\1c"); + int start = text.IndexOf(@"{\1c", StringComparison.Ordinal); int end = text.IndexOf('}', start); - if (end > 0 && !text.Substring(start).StartsWith("{\\1c}")) + if (end > 0 && !text.Substring(start).StartsWith("{\\1c}", StringComparison.Ordinal)) { string color = text.Substring(start + 5, end - (start + 5)); string extraTags = string.Empty; CheckAndAddSubTags(ref color, ref extraTags, out bool italic); - color = color.Replace("&", string.Empty).TrimStart('H'); + color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H'); color = color.PadLeft(6, '0'); // switch to rrggbb from bbggrr @@ -329,25 +330,25 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - text = text.Replace(@"{\i1}", ""); - text = text.Replace(@"{\i0}", ""); - text = text.Replace(@"{\i}", ""); + text = text.Replace(@"{\i1}", "", StringComparison.Ordinal); + text = text.Replace(@"{\i0}", "", StringComparison.Ordinal); + text = text.Replace(@"{\i}", "", StringComparison.Ordinal); if (CountTagInText(text, "") > CountTagInText(text, "")) { text += ""; } - text = text.Replace(@"{\u1}", ""); - text = text.Replace(@"{\u0}", ""); - text = text.Replace(@"{\u}", ""); + text = text.Replace(@"{\u1}", "", StringComparison.Ordinal); + text = text.Replace(@"{\u0}", "", StringComparison.Ordinal); + text = text.Replace(@"{\u}", "", StringComparison.Ordinal); if (CountTagInText(text, "") > CountTagInText(text, "")) { text += ""; } - text = text.Replace(@"{\b1}", ""); - text = text.Replace(@"{\b0}", ""); - text = text.Replace(@"{\b}", ""); + text = text.Replace(@"{\b1}", "", StringComparison.Ordinal); + text = text.Replace(@"{\b0}", "", StringComparison.Ordinal); + text = text.Replace(@"{\b}", "", StringComparison.Ordinal); if (CountTagInText(text, "") > CountTagInText(text, "")) { text += ""; @@ -362,7 +363,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles private static int CountTagInText(string text, string tag) { int count = 0; - int index = text.IndexOf(tag); + int index = text.IndexOf(tag, StringComparison.Ordinal); while (index >= 0) { count++; @@ -371,7 +372,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles return count; } - index = text.IndexOf(tag, index + 1); + index = text.IndexOf(tag, index + 1, StringComparison.Ordinal); } return count; @@ -380,7 +381,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out bool italic) { italic = false; - int indexOfSPlit = tagName.IndexOf(@"\"); + int indexOfSPlit = tagName.IndexOf('\\', StringComparison.Ordinal); if (indexOfSPlit > 0) { string rest = tagName.Substring(indexOfSPlit).TrimStart('\\'); @@ -388,9 +389,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles for (int i = 0; i < 10; i++) { - if (rest.StartsWith("fs") && rest.Length > 2) + if (rest.StartsWith("fs", StringComparison.Ordinal) && rest.Length > 2) { - indexOfSPlit = rest.IndexOf(@"\"); + indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal); string fontSize = rest; if (indexOfSPlit > 0) { @@ -404,9 +405,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles extraTags += " size=\"" + fontSize.Substring(2) + "\""; } - else if (rest.StartsWith("fn") && rest.Length > 2) + else if (rest.StartsWith("fn", StringComparison.Ordinal) && rest.Length > 2) { - indexOfSPlit = rest.IndexOf(@"\"); + indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal); string fontName = rest; if (indexOfSPlit > 0) { @@ -420,9 +421,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles extraTags += " face=\"" + fontName.Substring(2) + "\""; } - else if (rest.StartsWith("c") && rest.Length > 2) + else if (rest.StartsWith("c", StringComparison.Ordinal) && rest.Length > 2) { - indexOfSPlit = rest.IndexOf(@"\"); + indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal); string fontColor = rest; if (indexOfSPlit > 0) { @@ -435,7 +436,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } string color = fontColor.Substring(2); - color = color.Replace("&", string.Empty).TrimStart('H'); + color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H'); color = color.PadLeft(6, '0'); // switch to rrggbb from bbggrr color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2); @@ -443,9 +444,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles extraTags += " color=\"" + color + "\""; } - else if (rest.StartsWith("i1") && rest.Length > 1) + else if (rest.StartsWith("i1", StringComparison.Ordinal) && rest.Length > 1) { - indexOfSPlit = rest.IndexOf(@"\"); + indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal); italic = true; if (indexOfSPlit > 0) { @@ -456,9 +457,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles rest = string.Empty; } } - else if (rest.Length > 0 && rest.Contains("\\")) + else if (rest.Length > 0 && rest.Contains('\\', StringComparison.Ordinal)) { - indexOfSPlit = rest.IndexOf(@"\"); + indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal); rest = rest.Substring(indexOfSPlit).TrimStart('\\'); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index fbe8bd69f7..6ac5ac2ff1 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -415,7 +415,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles // FFmpeg automatically convert character encoding when it is UTF-16 // If we specify character encoding, it rejects with "do not specify a character encoding" and "Unable to recode subtitle event" - if ((inputPath.EndsWith(".smi") || inputPath.EndsWith(".sami")) && + if ((inputPath.EndsWith(".smi", StringComparison.Ordinal) || inputPath.EndsWith(".sami", StringComparison.Ordinal)) && (encodingParam.Equals("UTF-16BE", StringComparison.OrdinalIgnoreCase) || encodingParam.Equals("UTF-16LE", StringComparison.OrdinalIgnoreCase))) { @@ -506,7 +506,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle conversion failed for {0}", inputPath)); } - await SetAssFont(outputPath).ConfigureAwait(false); + await SetAssFont(outputPath, cancellationToken).ConfigureAwait(false); _logger.LogInformation("ffmpeg subtitle conversion succeeded for {Path}", inputPath); } @@ -668,7 +668,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (string.Equals(outputCodec, "ass", StringComparison.OrdinalIgnoreCase)) { - await SetAssFont(outputPath).ConfigureAwait(false); + await SetAssFont(outputPath, cancellationToken).ConfigureAwait(false); } } @@ -676,8 +676,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Sets the ass font. /// /// The file. + /// The token to monitor for cancellation requests. The default value is System.Threading.CancellationToken.None. /// Task. - private async Task SetAssFont(string file) + private async Task SetAssFont(string file, CancellationToken cancellationToken = default) { _logger.LogInformation("Setting ass font within {File}", file); @@ -692,14 +693,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles text = await reader.ReadToEndAsync().ConfigureAwait(false); } - var newText = text.Replace(",Arial,", ",Arial Unicode MS,"); + var newText = text.Replace(",Arial,", ",Arial Unicode MS,", StringComparison.Ordinal); - if (!string.Equals(text, newText)) + if (!string.Equals(text, newText, StringComparison.Ordinal)) { using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var writer = new StreamWriter(fileStream, encoding)) { - writer.Write(newText); + await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false); } } } @@ -736,7 +737,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName; // UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding - if ((path.EndsWith(".ass") || path.EndsWith(".ssa") || path.EndsWith(".srt")) + if ((path.EndsWith(".ass", StringComparison.Ordinal) || path.EndsWith(".ssa", StringComparison.Ordinal) || path.EndsWith(".srt", StringComparison.Ordinal)) && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase) || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase))) { From 6123e2e848331548823e80fab80813125db71b96 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 12:24:34 +0200 Subject: [PATCH 082/102] Fix build --- Emby.Dlna/Main/DlnaEntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 191763de4f..7d656541fb 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -60,7 +60,7 @@ namespace Emby.Dlna.Main public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; } - public static DlnaEntryPoint Current { get; private set; }; + public static DlnaEntryPoint Current { get; private set; } public DlnaEntryPoint( IServerConfigurationManager config, From d63f4bce5d380afc4cf8c97285d0ac86989554d5 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 20 Aug 2020 07:53:53 -0600 Subject: [PATCH 083/102] Conver all remaining form request to body --- .../Controllers/ConfigurationController.cs | 2 +- Jellyfin.Api/Controllers/StartupController.cs | 27 ++++++++----------- .../StartupDtos/StartupRemoteAccessDto.cs | 22 +++++++++++++++ 3 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index 019703dae9..20fb0ec875 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -117,7 +117,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("MediaEncoder/Path")] [Authorize(Policy = Policies.FirstTimeSetupOrElevated)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult UpdateMediaEncoderPath([FromForm, Required] MediaEncoderPathDto mediaEncoderPath) + public ActionResult UpdateMediaEncoderPath([FromBody, Required] MediaEncoderPathDto mediaEncoderPath) { _mediaEncoder.UpdateEncoderPath(mediaEncoderPath.Path, mediaEncoderPath.PathType); return NoContent(); diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index c8e3cc4f52..9c259cc198 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Constants; @@ -64,21 +65,16 @@ namespace Jellyfin.Api.Controllers /// /// Sets the initial startup wizard configuration. /// - /// The UI language culture. - /// The metadata country code. - /// The preferred language for metadata. + /// The updated startup configuration. /// Configuration saved. /// A indicating success. [HttpPost("Configuration")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult UpdateInitialConfiguration( - [FromForm] string? uiCulture, - [FromForm] string? metadataCountryCode, - [FromForm] string? preferredMetadataLanguage) + public ActionResult UpdateInitialConfiguration([FromBody, Required] StartupConfigurationDto startupConfiguration) { - _config.Configuration.UICulture = uiCulture; - _config.Configuration.MetadataCountryCode = metadataCountryCode; - _config.Configuration.PreferredMetadataLanguage = preferredMetadataLanguage; + _config.Configuration.UICulture = startupConfiguration.UICulture; + _config.Configuration.MetadataCountryCode = startupConfiguration.MetadataCountryCode; + _config.Configuration.PreferredMetadataLanguage = startupConfiguration.PreferredMetadataLanguage; _config.SaveConfiguration(); return NoContent(); } @@ -86,16 +82,15 @@ namespace Jellyfin.Api.Controllers /// /// Sets remote access and UPnP. /// - /// Enable remote access. - /// Enable UPnP. + /// The startup remote access dto. /// Configuration saved. /// A indicating success. [HttpPost("RemoteAccess")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping) + public ActionResult SetRemoteAccess([FromBody, Required] StartupRemoteAccessDto startupRemoteAccessDto) { - _config.Configuration.EnableRemoteAccess = enableRemoteAccess; - _config.Configuration.EnableUPnP = enableAutomaticPortMapping; + _config.Configuration.EnableRemoteAccess = startupRemoteAccessDto.EnableRemoteAccess; + _config.Configuration.EnableUPnP = startupRemoteAccessDto.EnableAutomaticPortMapping; _config.SaveConfiguration(); return NoContent(); } @@ -131,7 +126,7 @@ namespace Jellyfin.Api.Controllers /// [HttpPost("User")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task UpdateStartupUser([FromForm] StartupUserDto startupUserDto) + public async Task UpdateStartupUser([FromBody] StartupUserDto startupUserDto) { var user = _userManager.Users.First(); diff --git a/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs b/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs new file mode 100644 index 0000000000..4027ba41ae --- /dev/null +++ b/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace Jellyfin.Api.Models.StartupDtos +{ + /// + /// Startup remote access dto. + /// + public class StartupRemoteAccessDto + { + /// + /// Gets or sets a value indicating whether enable remote access. + /// + [Required] + public bool EnableRemoteAccess { get; set; } + + /// + /// Gets or sets a value indicating whether enable automatic port mapping. + /// + [Required] + public bool EnableAutomaticPortMapping { get; set; } + } +} From ca2b36bdb05c3757378ea0e8a4551befc2668bff Mon Sep 17 00:00:00 2001 From: David Date: Thu, 20 Aug 2020 17:01:04 +0200 Subject: [PATCH 084/102] Reduce warnings in Emby.Dlna --- Emby.Dlna/ConfigurationExtension.cs | 4 +- .../ConnectionManagerXmlBuilder.cs | 4 +- .../ContentDirectoryXmlBuilder.cs | 5 +- Emby.Dlna/ContentDirectory/ControlHandler.cs | 6 +- Emby.Dlna/ControlRequest.cs | 10 +- Emby.Dlna/Didl/DidlBuilder.cs | 92 ++++++++++--------- Emby.Dlna/Didl/StringWriterWithEncoding.cs | 6 +- Emby.Dlna/DlnaManager.cs | 1 + Emby.Dlna/Eventing/EventManager.cs | 8 +- Emby.Dlna/Main/DlnaEntryPoint.cs | 57 ++++++++---- .../MediaReceiverRegistrarXmlBuilder.cs | 3 +- Emby.Dlna/PlayTo/Device.cs | 22 ++--- Emby.Dlna/PlayTo/DeviceInfo.cs | 5 +- Emby.Dlna/PlayTo/MediaChangedEventArgs.cs | 13 +++ Emby.Dlna/PlayTo/PlayToController.cs | 27 +++--- Emby.Dlna/PlayTo/PlayToManager.cs | 53 +++++------ Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs | 7 -- Emby.Dlna/PlayTo/TRANSPORTSTATE.cs | 13 --- Emby.Dlna/PlayTo/TransportState.cs | 13 +++ Emby.Dlna/PlayTo/uBaseObject.cs | 20 ++-- Emby.Dlna/Profiles/DefaultProfile.cs | 4 +- Emby.Dlna/Profiles/DishHopperJoeyProfile.cs | 12 +-- Emby.Dlna/Profiles/LgTvProfile.cs | 4 +- Emby.Dlna/Profiles/LinksysDMA2100Profile.cs | 2 +- Emby.Dlna/Profiles/PanasonicVieraProfile.cs | 2 +- Emby.Dlna/Profiles/PopcornHourProfile.cs | 14 +-- Emby.Dlna/Profiles/SamsungSmartTvProfile.cs | 2 +- Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs | 6 +- Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs | 6 +- Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs | 6 +- Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs | 6 +- Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs | 10 +- Emby.Dlna/Profiles/SonyBravia2010Profile.cs | 44 ++++----- Emby.Dlna/Profiles/SonyBravia2011Profile.cs | 42 ++++----- Emby.Dlna/Profiles/SonyBravia2012Profile.cs | 34 +++---- Emby.Dlna/Profiles/SonyBravia2013Profile.cs | 37 ++++---- Emby.Dlna/Profiles/SonyBravia2014Profile.cs | 37 ++++---- Emby.Dlna/Profiles/SonyPs3Profile.cs | 14 +-- Emby.Dlna/Profiles/SonyPs4Profile.cs | 14 +-- Emby.Dlna/Profiles/WdtvLiveProfile.cs | 8 +- Emby.Dlna/Profiles/XboxOneProfile.cs | 14 +-- Emby.Dlna/Service/BaseControlHandler.cs | 16 ++-- Emby.Dlna/Service/BaseService.cs | 14 +-- Emby.Dlna/Service/ControlErrorHandler.cs | 10 +- Emby.Dlna/Ssdp/DeviceDiscovery.cs | 17 ++-- 45 files changed, 390 insertions(+), 354 deletions(-) create mode 100644 Emby.Dlna/PlayTo/MediaChangedEventArgs.cs delete mode 100644 Emby.Dlna/PlayTo/TRANSPORTSTATE.cs create mode 100644 Emby.Dlna/PlayTo/TransportState.cs diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs index dba9019677..3dcfe60d9d 100644 --- a/Emby.Dlna/ConfigurationExtension.cs +++ b/Emby.Dlna/ConfigurationExtension.cs @@ -19,12 +19,12 @@ namespace Emby.Dlna { public IEnumerable GetConfigurations() { - return new ConfigurationStore[] + return new[] { new ConfigurationStore { Key = "dlna", - ConfigurationType = typeof (DlnaOptions) + ConfigurationType = typeof(DlnaOptions) } }; } diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs index b31d437c3b..c8db5a3674 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs @@ -44,7 +44,7 @@ namespace Emby.Dlna.ConnectionManager DataType = "string", SendsEvents = false, - AllowedValues = new string[] + AllowedValues = new[] { "OK", "ContentFormatMismatch", @@ -67,7 +67,7 @@ namespace Emby.Dlna.ConnectionManager DataType = "string", SendsEvents = false, - AllowedValues = new string[] + AllowedValues = new[] { "Output", "Input" diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs index 6db4d7cb66..743dcc5161 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs @@ -10,7 +10,8 @@ namespace Emby.Dlna.ContentDirectory { public string GetXml() { - return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(), + return new ServiceXmlBuilder().GetXml( + new ServiceActionListBuilder().GetActions(), GetStateVariables()); } @@ -101,7 +102,7 @@ namespace Emby.Dlna.ContentDirectory DataType = "string", SendsEvents = false, - AllowedValues = new string[] + AllowedValues = new[] { "BrowseMetadata", "BrowseDirectChildren" diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 00821bf780..72f2e7dd47 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -253,7 +253,7 @@ namespace Emby.Dlna.ContentDirectory var id = sparams["ObjectID"]; var flag = sparams["BrowseFlag"]; var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*")); - var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", "")); + var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty)); var provided = 0; @@ -362,8 +362,8 @@ namespace Emby.Dlna.ContentDirectory private void HandleSearch(XmlWriter xmlWriter, IDictionary sparams, string deviceId) { - var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", "")); - var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", "")); + var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", string.Empty)); + var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty)); var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*")); // sort example: dc:title, dc:date diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs index a6e03b7e6a..c82e002ce5 100644 --- a/Emby.Dlna/ControlRequest.cs +++ b/Emby.Dlna/ControlRequest.cs @@ -7,6 +7,11 @@ namespace Emby.Dlna { public class ControlRequest { + public ControlRequest() + { + Headers = new HeaderDictionary(); + } + public IHeaderDictionary Headers { get; set; } public Stream InputXml { get; set; } @@ -14,10 +19,5 @@ namespace Emby.Dlna public string TargetServerUuId { get; set; } public string RequestedUrl { get; set; } - - public ControlRequest() - { - Headers = new HeaderDictionary(); - } } } diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 70e358019a..74007c92b7 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -34,12 +34,12 @@ namespace Emby.Dlna.Didl { public class DidlBuilder { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; + private const string NsDc = "http://purl.org/dc/elements/1.1/"; + private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/"; + private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/"; - private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; - private const string NS_DC = "http://purl.org/dc/elements/1.1/"; - private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/"; - private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/"; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly DeviceProfile _profile; private readonly IImageProcessor _imageProcessor; @@ -100,11 +100,11 @@ namespace Emby.Dlna.Didl { // writer.WriteStartDocument(); - writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); + writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl); - writer.WriteAttributeString("xmlns", "dc", null, NS_DC); - writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); - writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); + writer.WriteAttributeString("xmlns", "dc", null, NsDc); + writer.WriteAttributeString("xmlns", "dlna", null, NsDlna); + writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp); // didl.SetAttribute("xmlns:sec", NS_SEC); WriteXmlRootAttributes(_profile, writer); @@ -147,7 +147,7 @@ namespace Emby.Dlna.Didl { var clientId = GetClientId(item, null); - writer.WriteStartElement(string.Empty, "item", NS_DIDL); + writer.WriteStartElement(string.Empty, "item", NsDidl); writer.WriteAttributeString("restricted", "1"); writer.WriteAttributeString("id", clientId); @@ -207,7 +207,8 @@ namespace Emby.Dlna.Didl var targetWidth = streamInfo.TargetWidth; var targetHeight = streamInfo.TargetHeight; - var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container, + var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader( + streamInfo.Container, streamInfo.TargetVideoCodec.FirstOrDefault(), streamInfo.TargetAudioCodec.FirstOrDefault(), targetWidth, @@ -279,7 +280,7 @@ namespace Emby.Dlna.Didl } else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase)) { - writer.WriteStartElement(string.Empty, "res", NS_DIDL); + writer.WriteStartElement(string.Empty, "res", NsDidl); writer.WriteAttributeString("protocolInfo", "http-get:*:smi/caption:*"); @@ -288,7 +289,7 @@ namespace Emby.Dlna.Didl } else { - writer.WriteStartElement(string.Empty, "res", NS_DIDL); + writer.WriteStartElement(string.Empty, "res", NsDidl); var protocolInfo = string.Format( CultureInfo.InvariantCulture, "http-get:*:text/{0}:*", @@ -304,7 +305,7 @@ namespace Emby.Dlna.Didl private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo) { - writer.WriteStartElement(string.Empty, "res", NS_DIDL); + writer.WriteStartElement(string.Empty, "res", NsDidl); var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken)); @@ -526,7 +527,7 @@ namespace Emby.Dlna.Didl private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null) { - writer.WriteStartElement(string.Empty, "res", NS_DIDL); + writer.WriteStartElement(string.Empty, "res", NsDidl); if (streamInfo == null) { @@ -583,7 +584,8 @@ namespace Emby.Dlna.Didl writer.WriteAttributeString("bitrate", targetAudioBitrate.Value.ToString(_usCulture)); } - var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container, + var mediaProfile = _profile.GetAudioMediaProfile( + streamInfo.Container, streamInfo.TargetAudioCodec.FirstOrDefault(), targetChannels, targetAudioBitrate, @@ -596,7 +598,8 @@ namespace Emby.Dlna.Didl ? MimeTypes.GetMimeType(filename) : mediaProfile.MimeType; - var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container, + var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader( + streamInfo.Container, streamInfo.TargetAudioCodec.FirstOrDefault(), targetAudioBitrate, targetSampleRate, @@ -627,7 +630,7 @@ namespace Emby.Dlna.Didl public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null) { - writer.WriteStartElement(string.Empty, "container", NS_DIDL); + writer.WriteStartElement(string.Empty, "container", NsDidl); writer.WriteAttributeString("restricted", "1"); writer.WriteAttributeString("searchable", "1"); @@ -714,7 +717,7 @@ namespace Emby.Dlna.Didl // MediaMonkey for example won't display content without a title // if (filter.Contains("dc:title")) { - AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC); + AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NsDc); } WriteObjectClass(writer, item, itemStubType); @@ -723,7 +726,7 @@ namespace Emby.Dlna.Didl { if (item.PremiereDate.HasValue) { - AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NS_DC); + AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NsDc); } } @@ -731,13 +734,13 @@ namespace Emby.Dlna.Didl { foreach (var genre in item.Genres) { - AddValue(writer, "upnp", "genre", genre, NS_UPNP); + AddValue(writer, "upnp", "genre", genre, NsUpnp); } } foreach (var studio in item.Studios) { - AddValue(writer, "upnp", "publisher", studio, NS_UPNP); + AddValue(writer, "upnp", "publisher", studio, NsUpnp); } if (!(item is Folder)) @@ -748,28 +751,29 @@ namespace Emby.Dlna.Didl if (!string.IsNullOrWhiteSpace(desc)) { - AddValue(writer, "dc", "description", desc, NS_DC); + AddValue(writer, "dc", "description", desc, NsDc); } } + // if (filter.Contains("upnp:longDescription")) - //{ + // { // if (!string.IsNullOrWhiteSpace(item.Overview)) // { - // AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP); + // AddValue(writer, "upnp", "longDescription", item.Overview, NsUpnp); // } - //} + // } } if (!string.IsNullOrEmpty(item.OfficialRating)) { if (filter.Contains("dc:rating")) { - AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC); + AddValue(writer, "dc", "rating", item.OfficialRating, NsDc); } if (filter.Contains("upnp:rating")) { - AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP); + AddValue(writer, "upnp", "rating", item.OfficialRating, NsUpnp); } } @@ -781,7 +785,7 @@ namespace Emby.Dlna.Didl // More types here // http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs - writer.WriteStartElement("upnp", "class", NS_UPNP); + writer.WriteStartElement("upnp", "class", NsUpnp); if (item.IsDisplayedAsFolder || stubType.HasValue) { @@ -882,7 +886,7 @@ namespace Emby.Dlna.Didl var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) ?? PersonType.Actor; - AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP); + AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NsUpnp); } } @@ -896,8 +900,8 @@ namespace Emby.Dlna.Didl { foreach (var artist in hasArtists.Artists) { - AddValue(writer, "upnp", "artist", artist, NS_UPNP); - AddValue(writer, "dc", "creator", artist, NS_DC); + AddValue(writer, "upnp", "artist", artist, NsUpnp); + AddValue(writer, "dc", "creator", artist, NsDc); // If it doesn't support album artists (musicvideo), then tag as both if (hasAlbumArtists == null) @@ -917,16 +921,16 @@ namespace Emby.Dlna.Didl if (!string.IsNullOrWhiteSpace(item.Album)) { - AddValue(writer, "upnp", "album", item.Album, NS_UPNP); + AddValue(writer, "upnp", "album", item.Album, NsUpnp); } if (item.IndexNumber.HasValue) { - AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP); + AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NsUpnp); if (item is Episode) { - AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP); + AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NsUpnp); } } } @@ -935,7 +939,7 @@ namespace Emby.Dlna.Didl { try { - writer.WriteStartElement("upnp", "artist", NS_UPNP); + writer.WriteStartElement("upnp", "artist", NsUpnp); writer.WriteAttributeString("role", "AlbumArtist"); writer.WriteString(name); @@ -971,14 +975,14 @@ namespace Emby.Dlna.Didl var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg"); - writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP); - writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn); + writer.WriteStartElement("upnp", "albumArtURI", NsUpnp); + writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn); writer.WriteString(albumartUrlInfo.Url); writer.WriteFullEndElement(); // TOOD: Remove these default values var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg"); - writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url); + writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.Url); if (!_profile.EnableAlbumArtInDidl) { @@ -1021,7 +1025,7 @@ namespace Emby.Dlna.Didl var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format); - writer.WriteStartElement(string.Empty, "res", NS_DIDL); + writer.WriteStartElement(string.Empty, "res", NsDidl); // Images must have a reported size or many clients (Bubble upnp), will only use the first thumbnail // rather than using a larger one when available @@ -1150,16 +1154,16 @@ namespace Emby.Dlna.Didl } // try - //{ + // { // var size = _imageProcessor.GetImageSize(imageInfo); // width = size.Width; // height = size.Height; - //} + // } // catch - //{ + // { - //} + // } var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty) .TrimStart('.') diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs index 896fe992bf..d5276321f2 100644 --- a/Emby.Dlna/Didl/StringWriterWithEncoding.cs +++ b/Emby.Dlna/Didl/StringWriterWithEncoding.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Globalization; using System.IO; using System.Text; @@ -20,7 +21,7 @@ namespace Emby.Dlna.Didl } public StringWriterWithEncoding(StringBuilder sb) - : base(sb) + : base(sb, CultureInfo.InvariantCulture) { } @@ -29,7 +30,6 @@ namespace Emby.Dlna.Didl { } - public StringWriterWithEncoding(Encoding encoding) { _encoding = encoding; @@ -42,7 +42,7 @@ namespace Emby.Dlna.Didl } public StringWriterWithEncoding(StringBuilder sb, Encoding encoding) - : base(sb) + : base(sb, CultureInfo.InvariantCulture) { _encoding = encoding; } diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index ce4be7b514..a6a4740d7a 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -541,6 +541,7 @@ namespace Emby.Dlna }; } } + /* class DlnaProfileEntryPoint : IServerEntryPoint { diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs index 7d02f5e960..cc8e0ae06b 100644 --- a/Emby.Dlna/Eventing/EventManager.cs +++ b/Emby.Dlna/Eventing/EventManager.cs @@ -22,6 +22,8 @@ namespace Emby.Dlna.Eventing private readonly ILogger _logger; private readonly IHttpClient _httpClient; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + public EventManager(ILogger logger, IHttpClient httpClient) { _httpClient = httpClient; @@ -58,7 +60,8 @@ namespace Emby.Dlna.Eventing var timeout = ParseTimeout(requestedTimeoutString) ?? 300; var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); - _logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}", + _logger.LogDebug( + "Creating event subscription for {0} with timeout of {1} to {2}", notificationType, timeout, callbackUrl); @@ -94,7 +97,7 @@ namespace Emby.Dlna.Eventing { _logger.LogDebug("Cancelling event subscription {0}", subscriptionId); - _subscriptions.TryRemove(subscriptionId, out EventSubscription sub); + _subscriptions.TryRemove(subscriptionId, out _); return new EventSubscriptionResponse { @@ -103,7 +106,6 @@ namespace Emby.Dlna.Eventing }; } - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds) { var response = new EventSubscriptionResponse diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index a21d4cc11d..09932b747c 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -32,6 +32,8 @@ namespace Emby.Dlna.Main { public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup { + public static DlnaEntryPoint Current; + private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly IServerApplicationHost _appHost; @@ -54,13 +56,7 @@ namespace Emby.Dlna.Main private SsdpDevicePublisher _publisher; private ISsdpCommunicationsServer _communicationsServer; - public IContentDirectory ContentDirectory { get; private set; } - - public IConnectionManager ConnectionManager { get; private set; } - - public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; } - - public static DlnaEntryPoint Current; + private bool _disposed; public DlnaEntryPoint( IServerConfigurationManager config, @@ -127,6 +123,12 @@ namespace Emby.Dlna.Main Current = this; } + public IContentDirectory ContentDirectory { get; private set; } + + public IConnectionManager ConnectionManager { get; private set; } + + public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; } + public async Task RunAsync() { await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false); @@ -399,11 +401,36 @@ namespace Emby.Dlna.Main } } + public void DisposeDevicePublisher() + { + if (_publisher != null) + { + _logger.LogInformation("Disposing SsdpDevicePublisher"); + _publisher.Dispose(); + _publisher = null; + } + } + + /// public void Dispose() { - DisposeDevicePublisher(); - DisposePlayToManager(); - DisposeDeviceDiscovery(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + DisposeDevicePublisher(); + DisposePlayToManager(); + DisposeDeviceDiscovery(); + } if (_communicationsServer != null) { @@ -416,16 +443,8 @@ namespace Emby.Dlna.Main ConnectionManager = null; MediaReceiverRegistrar = null; Current = null; - } - public void DisposeDevicePublisher() - { - if (_publisher != null) - { - _logger.LogInformation("Disposing SsdpDevicePublisher"); - _publisher.Dispose(); - _publisher = null; - } + _disposed = true; } } } diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs index 8497025461..26994925d1 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs @@ -10,7 +10,8 @@ namespace Emby.Dlna.MediaReceiverRegistrar { public string GetXml() { - return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(), + return new ServiceXmlBuilder().GetXml( + new ServiceActionListBuilder().GetActions(), GetStateVariables()); } diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 86dd52a1b6..2708f20c3d 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -45,13 +45,13 @@ namespace Emby.Dlna.PlayTo public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0); - public TRANSPORTSTATE TransportState { get; private set; } + public TransportState TransportState { get; private set; } - public bool IsPlaying => TransportState == TRANSPORTSTATE.PLAYING; + public bool IsPlaying => TransportState == TransportState.Playing; - public bool IsPaused => TransportState == TRANSPORTSTATE.PAUSED || TransportState == TRANSPORTSTATE.PAUSED_PLAYBACK; + public bool IsPaused => TransportState == TransportState.Paused || TransportState == TransportState.PausedPlayback; - public bool IsStopped => TransportState == TRANSPORTSTATE.STOPPED; + public bool IsStopped => TransportState == TransportState.Stopped; private readonly IHttpClient _httpClient; @@ -400,7 +400,7 @@ namespace Emby.Dlna.PlayTo await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1)) .ConfigureAwait(false); - TransportState = TRANSPORTSTATE.PAUSED; + TransportState = TransportState.Paused; RestartTimer(true); } @@ -435,7 +435,7 @@ namespace Emby.Dlna.PlayTo if (transportState.HasValue) { // If we're not playing anything no need to get additional data - if (transportState.Value == TRANSPORTSTATE.STOPPED) + if (transportState.Value == TransportState.Stopped) { UpdateMediaInfo(null, transportState.Value); } @@ -464,7 +464,7 @@ namespace Emby.Dlna.PlayTo } // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive - if (transportState.Value == TRANSPORTSTATE.STOPPED) + if (transportState.Value == TransportState.Stopped) { RestartTimerInactive(); } @@ -595,7 +595,7 @@ namespace Emby.Dlna.PlayTo IsMuted = string.Equals(valueNode?.Value, "1", StringComparison.OrdinalIgnoreCase); } - private async Task GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) + private async Task GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo"); if (command == null) @@ -627,7 +627,7 @@ namespace Emby.Dlna.PlayTo var transportStateValue = transportState?.Value; if (transportStateValue != null - && Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state)) + && Enum.TryParse(transportStateValue, true, out TransportState state)) { return state; } @@ -1121,7 +1121,7 @@ namespace Emby.Dlna.PlayTo public uBaseObject CurrentMediaInfo { get; private set; } - private void UpdateMediaInfo(uBaseObject mediaInfo, TRANSPORTSTATE state) + private void UpdateMediaInfo(uBaseObject mediaInfo, TransportState state) { TransportState = state; @@ -1130,7 +1130,7 @@ namespace Emby.Dlna.PlayTo if (previousMediaInfo == null && mediaInfo != null) { - if (state != TRANSPORTSTATE.STOPPED) + if (state != TransportState.Stopped) { OnPlaybackStart(mediaInfo); } diff --git a/Emby.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs index f3aaaebc4a..d3daab9e0a 100644 --- a/Emby.Dlna/PlayTo/DeviceInfo.cs +++ b/Emby.Dlna/PlayTo/DeviceInfo.cs @@ -8,6 +8,9 @@ namespace Emby.Dlna.PlayTo { public class DeviceInfo { + private readonly List _services = new List(); + private string _baseUrl = string.Empty; + public DeviceInfo() { Name = "Generic Device"; @@ -33,7 +36,6 @@ namespace Emby.Dlna.PlayTo public string PresentationUrl { get; set; } - private string _baseUrl = string.Empty; public string BaseUrl { get => _baseUrl; @@ -42,7 +44,6 @@ namespace Emby.Dlna.PlayTo public DeviceIcon Icon { get; set; } - private readonly List _services = new List(); public List Services => _services; public DeviceIdentification ToDeviceIdentification() diff --git a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs new file mode 100644 index 0000000000..a8043d4ead --- /dev/null +++ b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs @@ -0,0 +1,13 @@ +#pragma warning disable CS1591 + +using System; + +namespace Emby.Dlna.PlayTo +{ + public class MediaChangedEventArgs : EventArgs + { + public uBaseObject OldMediaInfo { get; set; } + + public uBaseObject NewMediaInfo { get; set; } + } +} diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 92a93d4349..5182fa5098 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -31,7 +31,6 @@ namespace Emby.Dlna.PlayTo { private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); - private Device _device; private readonly SessionInfo _session; private readonly ISessionManager _sessionManager; private readonly ILibraryManager _libraryManager; @@ -50,6 +49,7 @@ namespace Emby.Dlna.PlayTo private readonly string _accessToken; private readonly List _playlist = new List(); + private Device _device; private int _currentPlaylistIndex; private bool _disposed; @@ -372,8 +372,12 @@ namespace Emby.Dlna.PlayTo if (!command.ControllingUserId.Equals(Guid.Empty)) { - _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId, - _session.DeviceName, _session.RemoteEndPoint, user); + _sessionManager.LogSessionActivity( + _session.Client, + _session.ApplicationVersion, + _session.DeviceId, + _session.DeviceName, + _session.RemoteEndPoint, user); } return PlayItems(playlist, cancellationToken); @@ -778,7 +782,7 @@ namespace Emby.Dlna.PlayTo const int maxWait = 15000000; const int interval = 500; var currentWait = 0; - while (_device.TransportState != TRANSPORTSTATE.PLAYING && currentWait < maxWait) + while (_device.TransportState != TransportState.Playing && currentWait < maxWait) { await Task.Delay(interval).ConfigureAwait(false); currentWait += interval; @@ -789,6 +793,9 @@ namespace Emby.Dlna.PlayTo private class StreamParams { + private MediaSourceInfo mediaSource; + private IMediaSourceManager _mediaSourceManager; + public Guid ItemId { get; set; } public bool IsDirectStream { get; set; } @@ -809,15 +816,11 @@ namespace Emby.Dlna.PlayTo public BaseItem Item { get; set; } - private MediaSourceInfo MediaSource; - - private IMediaSourceManager _mediaSourceManager; - public async Task GetMediaSource(CancellationToken cancellationToken) { - if (MediaSource != null) + if (mediaSource != null) { - return MediaSource; + return mediaSource; } var hasMediaSources = Item as IHasMediaSources; @@ -827,9 +830,9 @@ namespace Emby.Dlna.PlayTo return null; } - MediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false); + mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false); - return MediaSource; + return mediaSource; } private static Guid GetItemId(string url) diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 512589e4d7..ff28ce3e05 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -92,7 +92,7 @@ namespace Emby.Dlna.PlayTo // It has to report that it's a media renderer if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 && - nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1) + nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1) { // _logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location); return; @@ -192,20 +192,20 @@ namespace Emby.Dlna.PlayTo controller = new PlayToController( sessionInfo, - _sessionManager, - _libraryManager, - _logger, - _dlnaManager, - _userManager, - _imageProcessor, - serverAddress, - null, - _deviceDiscovery, - _userDataManager, - _localization, - _mediaSourceManager, - _config, - _mediaEncoder); + _sessionManager, + _libraryManager, + _logger, + _dlnaManager, + _userManager, + _imageProcessor, + serverAddress, + null, + _deviceDiscovery, + _userDataManager, + _localization, + _mediaSourceManager, + _config, + _mediaEncoder); sessionInfo.AddController(controller); @@ -218,17 +218,17 @@ namespace Emby.Dlna.PlayTo { PlayableMediaTypes = profile.GetSupportedMediaTypes(), - SupportedCommands = new string[] + SupportedCommands = new[] { - GeneralCommandType.VolumeDown.ToString(), - GeneralCommandType.VolumeUp.ToString(), - GeneralCommandType.Mute.ToString(), - GeneralCommandType.Unmute.ToString(), - GeneralCommandType.ToggleMute.ToString(), - GeneralCommandType.SetVolume.ToString(), - GeneralCommandType.SetAudioStreamIndex.ToString(), - GeneralCommandType.SetSubtitleStreamIndex.ToString(), - GeneralCommandType.PlayMediaSource.ToString() + GeneralCommandType.VolumeDown.ToString(), + GeneralCommandType.VolumeUp.ToString(), + GeneralCommandType.Mute.ToString(), + GeneralCommandType.Unmute.ToString(), + GeneralCommandType.ToggleMute.ToString(), + GeneralCommandType.SetVolume.ToString(), + GeneralCommandType.SetAudioStreamIndex.ToString(), + GeneralCommandType.SetSubtitleStreamIndex.ToString(), + GeneralCommandType.PlayMediaSource.ToString() }, SupportsMediaControl = true @@ -247,8 +247,9 @@ namespace Emby.Dlna.PlayTo { _disposeCancellationTokenSource.Cancel(); } - catch + catch (Exception ex) { + _logger.LogDebug("Error while disposing PlayToManager", ex); } _sessionLock.Dispose(); diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs index fa42b80e8b..5944d936cd 100644 --- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs @@ -8,11 +8,4 @@ namespace Emby.Dlna.PlayTo { public uBaseObject MediaInfo { get; set; } } - - public class MediaChangedEventArgs : EventArgs - { - public uBaseObject OldMediaInfo { get; set; } - - public uBaseObject NewMediaInfo { get; set; } - } } diff --git a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs deleted file mode 100644 index 7daefeca86..0000000000 --- a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs +++ /dev/null @@ -1,13 +0,0 @@ -#pragma warning disable CS1591 - -namespace Emby.Dlna.PlayTo -{ - public enum TRANSPORTSTATE - { - STOPPED, - PLAYING, - TRANSITIONING, - PAUSED_PLAYBACK, - PAUSED - } -} diff --git a/Emby.Dlna/PlayTo/TransportState.cs b/Emby.Dlna/PlayTo/TransportState.cs new file mode 100644 index 0000000000..2058e9dc79 --- /dev/null +++ b/Emby.Dlna/PlayTo/TransportState.cs @@ -0,0 +1,13 @@ +#pragma warning disable CS1591 + +namespace Emby.Dlna.PlayTo +{ + public enum TransportState + { + Stopped, + Playing, + Transitioning, + PausedPlayback, + Paused + } +} diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs index 05c19299f1..3301ad3e0a 100644 --- a/Emby.Dlna/PlayTo/uBaseObject.cs +++ b/Emby.Dlna/PlayTo/uBaseObject.cs @@ -24,16 +24,6 @@ namespace Emby.Dlna.PlayTo public string UpnpClass { get; set; } - public bool Equals(uBaseObject obj) - { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } - - return string.Equals(Id, obj.Id); - } - public string MediaType { get @@ -58,5 +48,15 @@ namespace Emby.Dlna.PlayTo return null; } } + + public bool Equals(uBaseObject obj) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + return string.Equals(Id, obj.Id, StringComparison.InvariantCulture); + } } } diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs index 90a23a4a22..d4af72b626 100644 --- a/Emby.Dlna/Profiles/DefaultProfile.cs +++ b/Emby.Dlna/Profiles/DefaultProfile.cs @@ -64,14 +64,14 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { // play all - Container = "", + Container = string.Empty, Type = DlnaProfileType.Video }, new DirectPlayProfile { // play all - Container = "", + Container = string.Empty, Type = DlnaProfileType.Audio } }; diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs index 942e369309..2a7524a6a2 100644 --- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs +++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs @@ -24,7 +24,7 @@ namespace Emby.Dlna.Profiles { Match = HeaderMatchType.Substring, Name = "User-Agent", - Value ="Zip_" + Value = "Zip_" } } }; @@ -81,7 +81,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -124,7 +124,7 @@ namespace Emby.Dlna.Profiles new CodecProfile { Type = CodecType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -161,7 +161,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "ac3,he-aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -177,7 +177,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -192,7 +192,7 @@ namespace Emby.Dlna.Profiles new CodecProfile { Type = CodecType.VideoAudio, - Conditions = new [] + Conditions = new[] { // The device does not have any audio switching capabilities new ProfileCondition diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs index 02301764c0..fbb368d3e3 100644 --- a/Emby.Dlna/Profiles/LgTvProfile.cs +++ b/Emby.Dlna/Profiles/LgTvProfile.cs @@ -84,7 +84,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -191,7 +191,7 @@ namespace Emby.Dlna.Profiles } }; - ResponseProfiles = new ResponseProfile[] + ResponseProfiles = new[] { new ResponseProfile { diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs index 1b1423520c..1a510dfecf 100644 --- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs +++ b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs @@ -32,7 +32,7 @@ namespace Emby.Dlna.Profiles } }; - ResponseProfiles = new ResponseProfile[] + ResponseProfiles = new[] { new ResponseProfile { diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs index 44c35e1425..0d536acf39 100644 --- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs +++ b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs @@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs index 9e9f6966fc..7fbf8c1649 100644 --- a/Emby.Dlna/Profiles/PopcornHourProfile.cs +++ b/Emby.Dlna/Profiles/PopcornHourProfile.cs @@ -93,8 +93,8 @@ namespace Emby.Dlna.Profiles new CodecProfile { Type = CodecType.Video, - Codec="h264", - Conditions = new [] + Codec = "h264", + Conditions = new[] { new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, "baseline|constrained baseline"), new ProfileCondition @@ -122,7 +122,7 @@ namespace Emby.Dlna.Profiles new CodecProfile { Type = CodecType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Audio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -182,7 +182,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Audio, Codec = "mp3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -202,7 +202,7 @@ namespace Emby.Dlna.Profiles } }; - ResponseProfiles = new ResponseProfile[] + ResponseProfiles = new[] { new ResponseProfile { diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs index 4ff2ab9be2..ddbebba2a4 100644 --- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -139,7 +139,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs index 238fe9f6bc..765c125045 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs +++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs @@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -197,7 +197,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs index 812a481513..390c8a3e43 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs +++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs @@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -197,7 +197,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs index 6bfff322ef..25adc4d022 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs +++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs @@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs index ec2529574c..0a39a5f401 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs +++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs @@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs index ecdd2e7a4e..05c8ab1c14 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs +++ b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs @@ -114,7 +114,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -156,7 +156,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -172,7 +172,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -191,7 +191,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -217,7 +217,7 @@ namespace Emby.Dlna.Profiles VideoCodec = "h264,mpeg4,vc1", AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", + OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", Type = DlnaProfileType.Video }, diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs index 68365ba4ae..8ab4acd1be 100644 --- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs @@ -102,13 +102,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", + OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -128,13 +128,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/mpeg", - OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", + OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -148,28 +148,28 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", + OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "ts,mpegts", - VideoCodec="mpeg2video", + VideoCodec = "mpeg2video", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", + OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "mpeg", - VideoCodec="mpeg1video,mpeg2video", + VideoCodec = "mpeg1video,mpeg2video", MimeType = "video/mpeg", - OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", + OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video } }; @@ -180,7 +180,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -204,7 +204,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -243,7 +243,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "mpeg2video", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -275,7 +275,7 @@ namespace Emby.Dlna.Profiles new CodecProfile { Type = CodecType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -303,7 +303,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -319,7 +319,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -341,7 +341,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "mp3,mp2", - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs index b34af04a50..42d253394c 100644 --- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs @@ -120,7 +120,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -143,13 +143,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", + OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -169,13 +169,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/mpeg", - OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", + OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -189,28 +189,28 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", + OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "ts,mpegts", - VideoCodec="mpeg2video", + VideoCodec = "mpeg2video", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", + OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "mpeg", - VideoCodec="mpeg1video,mpeg2video", + VideoCodec = "mpeg1video,mpeg2video", MimeType = "video/mpeg", - OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", + OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video }, new ResponseProfile @@ -227,7 +227,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -266,7 +266,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "mpeg2video", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -298,7 +298,7 @@ namespace Emby.Dlna.Profiles new CodecProfile { Type = CodecType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -326,7 +326,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -364,7 +364,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "mp3,mp2", - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs index 0e75d0cb5e..0598e83422 100644 --- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs @@ -131,13 +131,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", + OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -157,13 +157,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/mpeg", - OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", + OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -177,28 +177,28 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", + OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "ts,mpegts", - VideoCodec="mpeg2video", + VideoCodec = "mpeg2video", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", + OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "mpeg", - VideoCodec="mpeg1video,mpeg2video", + VideoCodec = "mpeg1video,mpeg2video", MimeType = "video/mpeg", - OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", + OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video }, new ResponseProfile @@ -215,7 +215,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -282,7 +282,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "mp3,mp2", - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs index 3300863c90..3d90a1e72d 100644 --- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs @@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -187,13 +187,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", + OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -213,13 +213,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/mpeg", - OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", + OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -233,28 +233,28 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", + OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "ts,mpegts", - VideoCodec="mpeg2video", + VideoCodec = "mpeg2video", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", + OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "mpeg", - VideoCodec="mpeg1video,mpeg2video", + VideoCodec = "mpeg1video,mpeg2video", MimeType = "video/mpeg", - OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", + OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video }, new ResponseProfile @@ -265,14 +265,13 @@ namespace Emby.Dlna.Profiles } }; - CodecProfiles = new[] { new CodecProfile { Type = CodecType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -300,7 +299,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "mp3,mp2", - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs index 4e833441cc..9188f73ef1 100644 --- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs @@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -187,13 +187,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", + OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -213,13 +213,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/mpeg", - OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", + OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", Type = DlnaProfileType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -233,28 +233,28 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "ts,mpegts", - VideoCodec="h264", - AudioCodec="ac3,aac,mp3", + VideoCodec = "h264", + AudioCodec = "ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", + OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "ts,mpegts", - VideoCodec="mpeg2video", + VideoCodec = "mpeg2video", MimeType = "video/vnd.dlna.mpeg-tts", - OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", + OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", Type = DlnaProfileType.Video }, new ResponseProfile { Container = "mpeg", - VideoCodec="mpeg1video,mpeg2video", + VideoCodec = "mpeg1video,mpeg2video", MimeType = "video/mpeg", - OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", + OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video }, new ResponseProfile @@ -265,14 +265,13 @@ namespace Emby.Dlna.Profiles } }; - CodecProfiles = new[] { new CodecProfile { Type = CodecType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -300,7 +299,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "mp3,mp2", - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs index 7f72356bdc..d56b1df507 100644 --- a/Emby.Dlna/Profiles/SonyPs3Profile.cs +++ b/Emby.Dlna/Profiles/SonyPs3Profile.cs @@ -108,7 +108,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -133,7 +133,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -176,7 +176,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -201,7 +201,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "wmapro", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -217,7 +217,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -235,7 +235,7 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "mp4,mov", - AudioCodec="aac", + AudioCodec = "aac", MimeType = "video/mp4", Type = DlnaProfileType.Video }, @@ -244,7 +244,7 @@ namespace Emby.Dlna.Profiles { Container = "avi", MimeType = "video/divx", - OrgPn="AVI", + OrgPn = "AVI", Type = DlnaProfileType.Video }, diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs index 411bfe2b0c..db56094e2b 100644 --- a/Emby.Dlna/Profiles/SonyPs4Profile.cs +++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs @@ -110,7 +110,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -135,7 +135,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "ac3", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -203,7 +203,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "wmapro", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -219,7 +219,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -237,7 +237,7 @@ namespace Emby.Dlna.Profiles new ResponseProfile { Container = "mp4,mov", - AudioCodec="aac", + AudioCodec = "aac", MimeType = "video/mp4", Type = DlnaProfileType.Video }, @@ -246,7 +246,7 @@ namespace Emby.Dlna.Profiles { Container = "avi", MimeType = "video/divx", - OrgPn="AVI", + OrgPn = "AVI", Type = DlnaProfileType.Video }, diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs index 2de9a8cd9e..937ca0f425 100644 --- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs +++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs @@ -20,7 +20,7 @@ namespace Emby.Dlna.Profiles Headers = new[] { - new HttpHeaderInfo {Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring}, + new HttpHeaderInfo { Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring }, new HttpHeaderInfo { Name = "User-Agent", @@ -168,7 +168,7 @@ namespace Emby.Dlna.Profiles { Type = DlnaProfileType.Photo, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -193,7 +193,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -221,7 +221,7 @@ namespace Emby.Dlna.Profiles Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs index 2cbe4e6acb..84d8184a22 100644 --- a/Emby.Dlna/Profiles/XboxOneProfile.cs +++ b/Emby.Dlna/Profiles/XboxOneProfile.cs @@ -119,7 +119,7 @@ namespace Emby.Dlna.Profiles Type = DlnaProfileType.Video, Container = "mp4,mov", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "mpeg4", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -187,7 +187,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "h264", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -236,7 +236,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.Video, Codec = "wmv2,wmv3,vc1", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -284,7 +284,7 @@ namespace Emby.Dlna.Profiles new CodecProfile { Type = CodecType.Video, - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -307,7 +307,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "ac3,wmav2,wmapro", - Conditions = new [] + Conditions = new[] { new ProfileCondition { @@ -323,7 +323,7 @@ namespace Emby.Dlna.Profiles { Type = CodecType.VideoAudio, Codec = "aac", - Conditions = new [] + Conditions = new[] { new ProfileCondition { diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 699d325eac..a795d45215 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -15,11 +15,7 @@ namespace Emby.Dlna.Service { public abstract class BaseControlHandler { - private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; - - protected IServerConfigurationManager Config { get; } - - protected ILogger Logger { get; } + private const string NsSoapenv = "http://schemas.xmlsoap.org/soap/envelope/"; protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) { @@ -27,6 +23,10 @@ namespace Emby.Dlna.Service Logger = logger; } + protected IServerConfigurationManager Config { get; } + + protected ILogger Logger { get; } + public async Task ProcessControlRequestAsync(ControlRequest request) { try @@ -80,10 +80,10 @@ namespace Emby.Dlna.Service { writer.WriteStartDocument(true); - writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV); - writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/"); + writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapenv); + writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapenv, "http://schemas.xmlsoap.org/soap/encoding/"); - writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV); + writer.WriteStartElement("SOAP-ENV", "Body", NsSoapenv); writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI); WriteResult(requestInfo.LocalName, requestInfo.Headers, writer); diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs index 8794ec26a8..88c62f86c1 100644 --- a/Emby.Dlna/Service/BaseService.cs +++ b/Emby.Dlna/Service/BaseService.cs @@ -8,31 +8,31 @@ namespace Emby.Dlna.Service { public class BaseService : IEventManager { - protected IEventManager EventManager; - protected IHttpClient HttpClient; + protected IEventManager _eventManager; + protected IHttpClient _httpClient; protected ILogger Logger; protected BaseService(ILogger logger, IHttpClient httpClient) { Logger = logger; - HttpClient = httpClient; + _httpClient = httpClient; - EventManager = new EventManager(logger, HttpClient); + _eventManager = new EventManager(logger, _httpClient); } public EventSubscriptionResponse CancelEventSubscription(string subscriptionId) { - return EventManager.CancelEventSubscription(subscriptionId); + return _eventManager.CancelEventSubscription(subscriptionId); } public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string timeoutString, string callbackUrl) { - return EventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); + return _eventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); } public EventSubscriptionResponse CreateEventSubscription(string notificationType, string timeoutString, string callbackUrl) { - return EventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); + return _eventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); } } } diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs index 047e9f0142..253a48d05d 100644 --- a/Emby.Dlna/Service/ControlErrorHandler.cs +++ b/Emby.Dlna/Service/ControlErrorHandler.cs @@ -10,7 +10,7 @@ namespace Emby.Dlna.Service { public static class ControlErrorHandler { - private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; + private const string NsSoapenv = "http://schemas.xmlsoap.org/soap/envelope/"; public static ControlResponse GetResponse(Exception ex) { @@ -26,11 +26,11 @@ namespace Emby.Dlna.Service { writer.WriteStartDocument(true); - writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV); - writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/"); + writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapenv); + writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapenv, "http://schemas.xmlsoap.org/soap/encoding/"); - writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV); - writer.WriteStartElement("SOAP-ENV", "Fault", NS_SOAPENV); + writer.WriteStartElement("SOAP-ENV", "Body", NsSoapenv); + writer.WriteStartElement("SOAP-ENV", "Fault", NsSoapenv); writer.WriteElementString("faultcode", "500"); writer.WriteElementString("faultstring", ex.Message); diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index 7daac96d1d..7c4c0f6d9b 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs @@ -17,9 +17,17 @@ namespace Emby.Dlna.Ssdp private readonly IServerConfigurationManager _config; + private SsdpDeviceLocator _deviceLocator; + private ISsdpCommunicationsServer _commsServer; + private int _listenerCount; private bool _disposed; + public DeviceDiscovery(IServerConfigurationManager config) + { + _config = config; + } + private event EventHandler> DeviceDiscoveredInternal; /// @@ -49,15 +57,6 @@ namespace Emby.Dlna.Ssdp /// public event EventHandler> DeviceLeft; - private SsdpDeviceLocator _deviceLocator; - - private ISsdpCommunicationsServer _commsServer; - - public DeviceDiscovery(IServerConfigurationManager config) - { - _config = config; - } - // Call this method from somewhere in your code to start the search. public void Start(ISsdpCommunicationsServer communicationsServer) { From cb5cb075a9803e296f6de594307994b88cb156d0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 17:16:09 +0200 Subject: [PATCH 085/102] Simplify FFmpeg detection code --- .../Encoder/EncoderValidator.cs | 58 +++---------------- .../EncoderValidatorTests.cs | 2 + .../EncoderValidatorTestsData.cs | 12 ++++ 3 files changed, 21 insertions(+), 51 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 0e9a5e502a..1c389c2377 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -100,20 +100,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { "libpostproc", new Version(55, 1) } }; - // This lookup table is to be maintained with the following command line: - // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' - private static readonly IReadOnlyDictionary _ffmpegVersionMap = new Dictionary - { - { "libavutil=56.51,libavcodec=58.91,libavformat=58.45,libavdevice=58.10,libavfilter=7.85,libswscale=5.7,libswresample=3.7,libpostproc=55.7,", new Version(4, 3) }, - { "libavutil=56.31,libavcodec=58.54,libavformat=58.29,libavdevice=58.8,libavfilter=7.57,libswscale=5.5,libswresample=3.5,libpostproc=55.5,", new Version(4, 2) }, - { "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3,", new Version(4, 1) }, - { "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1,", new Version(4, 0) }, - { "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7,", new Version(3, 4) }, - { "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5,", new Version(3, 3) }, - { "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1,", new Version(3, 2) }, - { "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3,", new Version(2, 8) } - }; - private readonly ILogger _logger; private readonly string _encoderPath; @@ -130,6 +116,7 @@ namespace MediaBrowser.MediaEncoding.Encoder Decoder } + // When changing this, also change the minimum library versions in _ffmpegMinimumLibraryVersions public static Version MinVersion { get; } = new Version(4, 0); public static Version MaxVersion { get; } = null; @@ -227,28 +214,9 @@ namespace MediaBrowser.MediaEncoding.Encoder { return new Version(match.Groups[1].Value); } - else - { - if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) - { - _logger.LogError("No ffmpeg library versions found"); - return null; - } - - // First try to lookup the full version string - if (_ffmpegVersionMap.TryGetValue(versionString, out Version version)) - { - return version; - } + var versionMap = GetFFmpegLibraryVersions(output); - // Then try to test for minimum library versions - return TestMinimumFFmpegLibraryVersions(versionMap); - } - } - - private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) - { var allVersionsValidated = true; foreach (var minimumVersion in _ffmpegMinimumLibraryVersions) @@ -281,10 +249,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// The 'ffmpeg -version' output. /// The library names and major.minor version numbers. - private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) + private static IReadOnlyDictionary GetFFmpegLibraryVersions(string output) { - var sb = new StringBuilder(144); - var map = new Dictionary(); foreach (Match match in Regex.Matches( @@ -292,24 +258,14 @@ namespace MediaBrowser.MediaEncoding.Encoder @"((?lib\w+)\s+(?[0-9]+)\.\s*(?[0-9]+))", RegexOptions.Multiline)) { - sb.Append(match.Groups["name"]) - .Append('=') - .Append(match.Groups["major"]) - .Append('.') - .Append(match.Groups["minor"]) - .Append(','); - - var str = $"{match.Groups["major"]}.{match.Groups["minor"]}"; - - var version = Version.Parse(str); + var version = new Version( + int.Parse(match.Groups["major"].Value), + int.Parse(match.Groups["minor"].Value)); map.Add(match.Groups["name"].Value, version); } - versionString = sb.ToString(); - versionMap = map; - - return sb.Length > 0; + return map; } private IEnumerable GetHwaccelTypes() diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index c6b9cf67c3..26db2dab0e 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -18,6 +18,7 @@ namespace Jellyfin.MediaEncoding.Tests } [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] @@ -34,6 +35,7 @@ namespace Jellyfin.MediaEncoding.Tests { public IEnumerator GetEnumerator() { + 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) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index f5ff3d723a..f781471cc6 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests { internal static class EncoderValidatorTestsData { + 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 +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 FFmpegV43Output = @"ffmpeg version 4.3 Copyright (c) 2000-2020 the FFmpeg developers built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04) configuration: --prefix=/usr/lib/jellyfin-ffmpeg --target-os=linux --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-vdpau --disable-sdl2 --disable-xlib --enable-gpl --enable-version3 --enable-static --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --arch=amd64 --enable-amf --enable-nvenc --enable-nvdec --enable-vaapi --enable-opencl From 301e029d42356d156a04d6269806d74916f80cc8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 17:45:40 +0200 Subject: [PATCH 086/102] Add unsupported ffmpeg version to tests --- .../Encoder/EncoderValidator.cs | 1 - .../EncoderValidatorTests.cs | 6 ++++-- .../EncoderValidatorTestsData.cs | 14 +++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 1c389c2377..3c19109a75 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index 26db2dab0e..39fd8afda1 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -24,7 +24,8 @@ namespace Jellyfin.MediaEncoding.Tests [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, true)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, true)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { var val = new EncoderValidator(new NullLogger()); @@ -41,7 +42,8 @@ namespace Jellyfin.MediaEncoding.Tests yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, new Version(4, 0) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 0) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index f781471cc6..9f5bef9a88 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -75,7 +75,7 @@ libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 libpostproc 55. 1.100 / 55. 1.100"; - public const string FFmpegGitUnknownOutput = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers + public const string FFmpegGitUnknownOutput2 = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9.1.1 (GCC) 20190716 configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt libavutil 56. 30.100 / 56. 30.100 @@ -86,5 +86,17 @@ libavfilter 7. 56.101 / 7. 56.101 libswscale 5. 4.101 / 5. 4.101 libswresample 3. 4.100 / 3. 4.100 libpostproc 55. 4.100 / 55. 4.100"; + + public const string FFmpegGitUnknownOutput = @"ffmpeg version N-45325-gb173e0353-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers +built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516 +configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libfribidi --enable-libass --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libzimg +libavutil 56. 9.100 / 56. 9.100 +libavcodec 58. 14.100 / 58. 14.100 +libavformat 58. 10.100 / 58. 10.100 +libavdevice 58. 2.100 / 58. 2.100 +libavfilter 7. 13.100 / 7. 13.100 +libswscale 5. 0.102 / 5. 0.102 +libswresample 3. 0.101 / 3. 0.101 +libpostproc 55. 0.100 / 55. 0.100"; } } From 1337a0dcecfcb275484fc79f1ad317b812cfe483 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 20 Aug 2020 17:59:27 +0200 Subject: [PATCH 087/102] Code review --- Emby.Dlna/Didl/StringWriterWithEncoding.cs | 6 +++--- Emby.Dlna/Main/DlnaEntryPoint.cs | 4 ++++ Emby.Dlna/PlayTo/Device.cs | 4 ++++ Emby.Dlna/PlayTo/PlayToController.cs | 4 ++++ Emby.Dlna/PlayTo/PlayToManager.cs | 2 +- Emby.Dlna/Service/BaseControlHandler.cs | 8 ++++---- Emby.Dlna/Service/ControlErrorHandler.cs | 10 +++++----- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs index d5276321f2..2b86ea333f 100644 --- a/Emby.Dlna/Didl/StringWriterWithEncoding.cs +++ b/Emby.Dlna/Didl/StringWriterWithEncoding.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 +#pragma warning disable CA1305 using System; -using System.Globalization; using System.IO; using System.Text; @@ -21,7 +21,7 @@ namespace Emby.Dlna.Didl } public StringWriterWithEncoding(StringBuilder sb) - : base(sb, CultureInfo.InvariantCulture) + : base(sb) { } @@ -42,7 +42,7 @@ namespace Emby.Dlna.Didl } public StringWriterWithEncoding(StringBuilder sb, Encoding encoding) - : base(sb, CultureInfo.InvariantCulture) + : base(sb) { _encoding = encoding; } diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 09932b747c..09285e7a26 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -418,6 +418,10 @@ namespace Emby.Dlna.Main GC.SuppressFinalize(this); } + /// + /// Releases unmanaged and optionally managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 2708f20c3d..9f925eed53 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -1200,6 +1200,10 @@ namespace Emby.Dlna.PlayTo GC.SuppressFinalize(this); } + /// + /// Releases unmanaged and optionally managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 5182fa5098..f8ce197577 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -637,6 +637,10 @@ namespace Emby.Dlna.PlayTo GC.SuppressFinalize(this); } + /// + /// Releases unmanaged and optionally managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index ff28ce3e05..99b9105cc5 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -249,7 +249,7 @@ namespace Emby.Dlna.PlayTo } catch (Exception ex) { - _logger.LogDebug("Error while disposing PlayToManager", ex); + _logger.LogDebug(ex, "Error while disposing PlayToManager"); } _sessionLock.Dispose(); diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index a795d45215..91a21a21d2 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -15,7 +15,7 @@ namespace Emby.Dlna.Service { public abstract class BaseControlHandler { - private const string NsSoapenv = "http://schemas.xmlsoap.org/soap/envelope/"; + private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/"; protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) { @@ -80,10 +80,10 @@ namespace Emby.Dlna.Service { writer.WriteStartDocument(true); - writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapenv); - writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapenv, "http://schemas.xmlsoap.org/soap/encoding/"); + writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv); + writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/"); - writer.WriteStartElement("SOAP-ENV", "Body", NsSoapenv); + writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv); writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI); WriteResult(requestInfo.LocalName, requestInfo.Headers, writer); diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs index 253a48d05d..f2b5dd9ca8 100644 --- a/Emby.Dlna/Service/ControlErrorHandler.cs +++ b/Emby.Dlna/Service/ControlErrorHandler.cs @@ -10,7 +10,7 @@ namespace Emby.Dlna.Service { public static class ControlErrorHandler { - private const string NsSoapenv = "http://schemas.xmlsoap.org/soap/envelope/"; + private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/"; public static ControlResponse GetResponse(Exception ex) { @@ -26,11 +26,11 @@ namespace Emby.Dlna.Service { writer.WriteStartDocument(true); - writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapenv); - writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapenv, "http://schemas.xmlsoap.org/soap/encoding/"); + writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv); + writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/"); - writer.WriteStartElement("SOAP-ENV", "Body", NsSoapenv); - writer.WriteStartElement("SOAP-ENV", "Fault", NsSoapenv); + writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv); + writer.WriteStartElement("SOAP-ENV", "Fault", NsSoapEnv); writer.WriteElementString("faultcode", "500"); writer.WriteElementString("faultstring", ex.Message); From 170e434f92142a8e4c17d3a5eec0bd9c2d6a0037 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 21:04:57 +0200 Subject: [PATCH 088/102] Fix all warnings in Emby.Dlna --- Emby.Dlna/Common/ServiceAction.cs | 2 +- Emby.Dlna/Common/StateVariable.cs | 3 +- Emby.Dlna/ConfigurationExtension.cs | 16 -- ...Manager.cs => ConnectionManagerService.cs} | 10 +- ...irectory.cs => ContentDirectoryService.cs} | 6 +- Emby.Dlna/ContentDirectory/ControlHandler.cs | 152 +++++------- Emby.Dlna/ContentDirectory/ServerItem.cs | 23 ++ Emby.Dlna/ContentDirectory/StubType.cs | 28 +++ Emby.Dlna/ControlRequest.cs | 6 +- Emby.Dlna/ControlResponse.cs | 2 +- Emby.Dlna/Didl/DidlBuilder.cs | 75 +++--- Emby.Dlna/Didl/Filter.cs | 4 +- Emby.Dlna/DlnaConfigurationFactory.cs | 24 ++ Emby.Dlna/DlnaManager.cs | 18 +- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Dlna/EventSubscriptionResponse.cs | 2 +- Emby.Dlna/IEventManager.cs | 10 + Emby.Dlna/Main/DlnaEntryPoint.cs | 33 +-- ...ar.cs => MediaReceiverRegistrarService.cs} | 6 +- Emby.Dlna/PlayTo/Device.cs | 167 ++++++------- Emby.Dlna/PlayTo/MediaChangedEventArgs.cs | 4 +- Emby.Dlna/PlayTo/PlayToController.cs | 220 +++++++++--------- Emby.Dlna/PlayTo/PlayToManager.cs | 2 +- Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs | 2 +- Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs | 2 +- Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs | 2 +- Emby.Dlna/PlayTo/TransportCommands.cs | 53 ++--- Emby.Dlna/PlayTo/TransportState.cs | 1 + Emby.Dlna/PlayTo/UpnpContainer.cs | 16 +- Emby.Dlna/PlayTo/uBaseObject.cs | 7 +- Emby.Dlna/PlayTo/uPnpNamespaces.cs | 90 ++++--- Emby.Dlna/Service/BaseControlHandler.cs | 18 +- Emby.Dlna/Service/BaseService.cs | 20 +- Emby.Dlna/Service/ServiceXmlBuilder.cs | 2 +- .../Ssdp/{Extensions.cs => SsdpExtensions.cs} | 2 +- .../Controllers/DlnaServerController.cs | 3 +- 36 files changed, 529 insertions(+), 504 deletions(-) rename Emby.Dlna/ConnectionManager/{ConnectionManager.cs => ConnectionManagerService.cs} (76%) rename Emby.Dlna/ContentDirectory/{ContentDirectory.cs => ContentDirectoryService.cs} (96%) create mode 100644 Emby.Dlna/ContentDirectory/ServerItem.cs create mode 100644 Emby.Dlna/ContentDirectory/StubType.cs create mode 100644 Emby.Dlna/DlnaConfigurationFactory.cs rename Emby.Dlna/MediaReceiverRegistrar/{MediaReceiverRegistrar.cs => MediaReceiverRegistrarService.cs} (82%) rename Emby.Dlna/Ssdp/{Extensions.cs => SsdpExtensions.cs} (94%) diff --git a/Emby.Dlna/Common/ServiceAction.cs b/Emby.Dlna/Common/ServiceAction.cs index db4f270633..d458d7f3f6 100644 --- a/Emby.Dlna/Common/ServiceAction.cs +++ b/Emby.Dlna/Common/ServiceAction.cs @@ -13,7 +13,7 @@ namespace Emby.Dlna.Common public string Name { get; set; } - public List ArgumentList { get; set; } + public List ArgumentList { get; } /// public override string ToString() diff --git a/Emby.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs index a2c2bf5ddc..6daf7ab6b2 100644 --- a/Emby.Dlna/Common/StateVariable.cs +++ b/Emby.Dlna/Common/StateVariable.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Collections.Generic; namespace Emby.Dlna.Common { @@ -17,7 +18,7 @@ namespace Emby.Dlna.Common public bool SendsEvents { get; set; } - public string[] AllowedValues { get; set; } + public IReadOnlyList AllowedValues { get; set; } /// public override string ToString() diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs index 3dcfe60d9d..fc02e17515 100644 --- a/Emby.Dlna/ConfigurationExtension.cs +++ b/Emby.Dlna/ConfigurationExtension.cs @@ -1,7 +1,6 @@ #nullable enable #pragma warning disable CS1591 -using System.Collections.Generic; using Emby.Dlna.Configuration; using MediaBrowser.Common.Configuration; @@ -14,19 +13,4 @@ namespace Emby.Dlna return manager.GetConfiguration("dlna"); } } - - public class DlnaConfigurationFactory : IConfigurationFactory - { - public IEnumerable GetConfigurations() - { - return new[] - { - new ConfigurationStore - { - Key = "dlna", - ConfigurationType = typeof(DlnaOptions) - } - }; - } - } } diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs similarity index 76% rename from Emby.Dlna/ConnectionManager/ConnectionManager.cs rename to Emby.Dlna/ConnectionManager/ConnectionManagerService.cs index e32cc11bfa..12338e2b4b 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs @@ -9,22 +9,20 @@ using Microsoft.Extensions.Logging; namespace Emby.Dlna.ConnectionManager { - public class ConnectionManager : BaseService, IConnectionManager + public class ConnectionManagerService : BaseService, IConnectionManager { private readonly IDlnaManager _dlna; - private readonly ILogger _logger; private readonly IServerConfigurationManager _config; - public ConnectionManager( + public ConnectionManagerService( IDlnaManager dlna, IServerConfigurationManager config, - ILogger logger, + ILogger logger, IHttpClient httpClient) : base(logger, httpClient) { _dlna = dlna; _config = config; - _logger = logger; } /// @@ -39,7 +37,7 @@ namespace Emby.Dlna.ConnectionManager var profile = _dlna.GetProfile(request.Headers) ?? _dlna.GetDefaultProfile(); - return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request); + return new ControlHandler(_config, Logger, profile).ProcessControlRequestAsync(request); } } } diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs similarity index 96% rename from Emby.Dlna/ContentDirectory/ContentDirectory.cs rename to Emby.Dlna/ContentDirectory/ContentDirectoryService.cs index b1ce7e8ecb..72732823ac 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs @@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Dlna.ContentDirectory { - public class ContentDirectory : BaseService, IContentDirectory + public class ContentDirectoryService : BaseService, IContentDirectory { private readonly ILibraryManager _libraryManager; private readonly IImageProcessor _imageProcessor; @@ -33,14 +33,14 @@ namespace Emby.Dlna.ContentDirectory private readonly IMediaEncoder _mediaEncoder; private readonly ITVSeriesManager _tvSeriesManager; - public ContentDirectory( + public ContentDirectoryService( IDlnaManager dlna, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILibraryManager libraryManager, IServerConfigurationManager config, IUserManager userManager, - ILogger logger, + ILogger logger, IHttpClient httpClient, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 72f2e7dd47..be1ed7872e 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -40,6 +40,11 @@ namespace Emby.Dlna.ContentDirectory { public class ControlHandler : BaseControlHandler { + private const string NsDc = "http://purl.org/dc/elements/1.1/"; + private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; + private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/"; + private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/"; + private readonly ILibraryManager _libraryManager; private readonly IUserDataManager _userDataManager; private readonly IServerConfigurationManager _config; @@ -47,11 +52,6 @@ namespace Emby.Dlna.ContentDirectory private readonly IUserViewManager _userViewManager; private readonly ITVSeriesManager _tvSeriesManager; - private const string NS_DC = "http://purl.org/dc/elements/1.1/"; - private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; - private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/"; - private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/"; - private readonly int _systemUpdateId; private readonly DidlBuilder _didlBuilder; @@ -181,7 +181,11 @@ namespace Emby.Dlna.ContentDirectory userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks; - _userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed, + _userDataManager.SaveUserData( + _user, + item, + userdata, + UserDataSaveReason.TogglePlayed, CancellationToken.None); } @@ -286,18 +290,17 @@ namespace Emby.Dlna.ContentDirectory using (var writer = XmlWriter.Create(builder, settings)) { - writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); + writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl); - writer.WriteAttributeString("xmlns", "dc", null, NS_DC); - writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); - writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); + writer.WriteAttributeString("xmlns", "dc", null, NsDc); + writer.WriteAttributeString("xmlns", "dlna", null, NsDlna); + writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp); DidlBuilder.WriteXmlRootAttributes(_profile, writer); var serverItem = GetItemFromObjectId(id); var item = serverItem.Item; - if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal)) { totalCount = 1; @@ -397,11 +400,11 @@ namespace Emby.Dlna.ContentDirectory using (var writer = XmlWriter.Create(builder, settings)) { - writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); + writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl); - writer.WriteAttributeString("xmlns", "dc", null, NS_DC); - writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); - writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); + writer.WriteAttributeString("xmlns", "dc", null, NsDc); + writer.WriteAttributeString("xmlns", "dlna", null, NsDlna); + writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp); DidlBuilder.WriteXmlRootAttributes(_profile, writer); @@ -783,11 +786,14 @@ namespace Emby.Dlna.ContentDirectory }) .ToArray(); - return ApplyPaging(new QueryResult - { - Items = folders, - TotalRecordCount = folders.Length - }, startIndex, limit); + return ApplyPaging( + new QueryResult + { + Items = folders, + TotalRecordCount = folders.Length + }, + startIndex, + limit); } private QueryResult GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) @@ -1135,14 +1141,16 @@ namespace Emby.Dlna.ContentDirectory { query.OrderBy = Array.Empty<(string, SortOrder)>(); - var items = _userViewManager.GetLatestItems(new LatestItemsQuery - { - UserId = user.Id, - Limit = 50, - IncludeItemTypes = new[] { nameof(Audio) }, - ParentId = parent?.Id ?? Guid.Empty, - GroupItems = true - }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); + var items = _userViewManager.GetLatestItems( + new LatestItemsQuery + { + UserId = user.Id, + Limit = 50, + IncludeItemTypes = new[] { nameof(Audio) }, + ParentId = parent?.Id ?? Guid.Empty, + GroupItems = true + }, + query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); return ToResult(items); } @@ -1151,12 +1159,15 @@ namespace Emby.Dlna.ContentDirectory { query.OrderBy = Array.Empty<(string, SortOrder)>(); - var result = _tvSeriesManager.GetNextUp(new NextUpQuery - { - Limit = query.Limit, - StartIndex = query.StartIndex, - UserId = query.User.Id - }, new[] { parent }, query.DtoOptions); + var result = _tvSeriesManager.GetNextUp( + new NextUpQuery + { + Limit = query.Limit, + StartIndex = query.StartIndex, + UserId = query.User.Id + }, + new[] { parent }, + query.DtoOptions); return ToResult(result); } @@ -1165,14 +1176,16 @@ namespace Emby.Dlna.ContentDirectory { query.OrderBy = Array.Empty<(string, SortOrder)>(); - var items = _userViewManager.GetLatestItems(new LatestItemsQuery - { - UserId = user.Id, - Limit = 50, - IncludeItemTypes = new[] { typeof(Episode).Name }, - ParentId = parent == null ? Guid.Empty : parent.Id, - GroupItems = false - }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); + var items = _userViewManager.GetLatestItems( + new LatestItemsQuery + { + UserId = user.Id, + Limit = 50, + IncludeItemTypes = new[] { typeof(Episode).Name }, + ParentId = parent == null ? Guid.Empty : parent.Id, + GroupItems = false + }, + query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); return ToResult(items); } @@ -1183,13 +1196,14 @@ namespace Emby.Dlna.ContentDirectory var items = _userViewManager.GetLatestItems( new LatestItemsQuery - { - UserId = user.Id, - Limit = 50, - IncludeItemTypes = new[] { nameof(Movie) }, - ParentId = parent?.Id ?? Guid.Empty, - GroupItems = true - }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); + { + UserId = user.Id, + Limit = 50, + IncludeItemTypes = new[] { nameof(Movie) }, + ParentId = parent?.Id ?? Guid.Empty, + GroupItems = true + }, + query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); return ToResult(items); } @@ -1354,44 +1368,4 @@ namespace Emby.Dlna.ContentDirectory return new ServerItem(_libraryManager.GetUserRootFolder()); } } - - internal class ServerItem - { - public BaseItem Item { get; set; } - - public StubType? StubType { get; set; } - - public ServerItem(BaseItem item) - { - Item = item; - - if (item is IItemByName && !(item is Folder)) - { - StubType = Dlna.ContentDirectory.StubType.Folder; - } - } - } - - public enum StubType - { - Folder = 0, - Latest = 2, - Playlists = 3, - Albums = 4, - AlbumArtists = 5, - Artists = 6, - Songs = 7, - Genres = 8, - FavoriteSongs = 9, - FavoriteArtists = 10, - FavoriteAlbums = 11, - ContinueWatching = 12, - Movies = 13, - Collections = 14, - Favorites = 15, - NextUp = 16, - Series = 17, - FavoriteSeries = 18, - FavoriteEpisodes = 19 - } } diff --git a/Emby.Dlna/ContentDirectory/ServerItem.cs b/Emby.Dlna/ContentDirectory/ServerItem.cs new file mode 100644 index 0000000000..e406054149 --- /dev/null +++ b/Emby.Dlna/ContentDirectory/ServerItem.cs @@ -0,0 +1,23 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities; + +namespace Emby.Dlna.ContentDirectory +{ + internal class ServerItem + { + public ServerItem(BaseItem item) + { + Item = item; + + if (item is IItemByName && !(item is Folder)) + { + StubType = Dlna.ContentDirectory.StubType.Folder; + } + } + + public BaseItem Item { get; set; } + + public StubType? StubType { get; set; } + } +} diff --git a/Emby.Dlna/ContentDirectory/StubType.cs b/Emby.Dlna/ContentDirectory/StubType.cs new file mode 100644 index 0000000000..eee405d3e7 --- /dev/null +++ b/Emby.Dlna/ContentDirectory/StubType.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1602 + +namespace Emby.Dlna.ContentDirectory +{ + public enum StubType + { + Folder = 0, + Latest = 2, + Playlists = 3, + Albums = 4, + AlbumArtists = 5, + Artists = 6, + Songs = 7, + Genres = 8, + FavoriteSongs = 9, + FavoriteArtists = 10, + FavoriteAlbums = 11, + ContinueWatching = 12, + Movies = 13, + Collections = 14, + Favorites = 15, + NextUp = 16, + Series = 17, + FavoriteSeries = 18, + FavoriteEpisodes = 19 + } +} diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs index c82e002ce5..4ea4e4e48c 100644 --- a/Emby.Dlna/ControlRequest.cs +++ b/Emby.Dlna/ControlRequest.cs @@ -7,12 +7,12 @@ namespace Emby.Dlna { public class ControlRequest { - public ControlRequest() + public ControlRequest(IHeaderDictionary headers) { - Headers = new HeaderDictionary(); + Headers = headers; } - public IHeaderDictionary Headers { get; set; } + public IHeaderDictionary Headers { get; } public Stream InputXml { get; set; } diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs index 243b097867..d827eef26c 100644 --- a/Emby.Dlna/ControlResponse.cs +++ b/Emby.Dlna/ControlResponse.cs @@ -11,7 +11,7 @@ namespace Emby.Dlna Headers = new Dictionary(); } - public IDictionary Headers { get; set; } + public IDictionary Headers { get; } public string Xml { get; set; } diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 74007c92b7..bd09a80511 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -977,12 +977,12 @@ namespace Emby.Dlna.Didl writer.WriteStartElement("upnp", "albumArtURI", NsUpnp); writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn); - writer.WriteString(albumartUrlInfo.Url); + writer.WriteString(albumartUrlInfo.url); writer.WriteFullEndElement(); // TOOD: Remove these default values var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg"); - writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.Url); + writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.url); if (!_profile.EnableAlbumArtInDidl) { @@ -1029,8 +1029,8 @@ namespace Emby.Dlna.Didl // Images must have a reported size or many clients (Bubble upnp), will only use the first thumbnail // rather than using a larger one when available - var width = albumartUrlInfo.Width ?? maxWidth; - var height = albumartUrlInfo.Height ?? maxHeight; + var width = albumartUrlInfo.width ?? maxWidth; + var height = albumartUrlInfo.height ?? maxHeight; var contentFeatures = new ContentFeatureBuilder(_profile) .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn); @@ -1047,7 +1047,7 @@ namespace Emby.Dlna.Didl "resolution", string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height)); - writer.WriteString(albumartUrlInfo.Url); + writer.WriteString(albumartUrlInfo.url); writer.WriteFullEndElement(); } @@ -1143,7 +1143,6 @@ namespace Emby.Dlna.Didl if (width == 0 || height == 0) { - // _imageProcessor.GetImageSize(item, imageInfo); width = null; height = null; } @@ -1153,18 +1152,6 @@ namespace Emby.Dlna.Didl height = null; } - // try - // { - // var size = _imageProcessor.GetImageSize(imageInfo); - - // width = size.Width; - // height = size.Height; - // } - // catch - // { - - // } - var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty) .TrimStart('.') .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase); @@ -1181,30 +1168,6 @@ namespace Emby.Dlna.Didl }; } - private class ImageDownloadInfo - { - internal Guid ItemId; - internal string ImageTag; - internal ImageType Type; - - internal int? Width; - internal int? Height; - - internal bool IsDirectStream; - - internal string Format; - - internal ItemImageInfo ItemImageInfo; - } - - private class ImageUrlInfo - { - internal string Url; - - internal int? Width; - internal int? Height; - } - public static string GetClientId(BaseItem item, StubType? stubType) { return GetClientId(item.Id, stubType); @@ -1222,7 +1185,7 @@ namespace Emby.Dlna.Didl return id; } - private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format) + private (string url, int? width, int? height) GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format) { var url = string.Format( CultureInfo.InvariantCulture, @@ -1260,12 +1223,26 @@ namespace Emby.Dlna.Didl // just lie info.IsDirectStream = true; - return new ImageUrlInfo - { - Url = url, - Width = width, - Height = height - }; + return (url, width, height); + } + + private class ImageDownloadInfo + { + internal Guid ItemId { get; set; } + + internal string ImageTag { get; set; } + + internal ImageType Type { get; set; } + + internal int? Width { get; set; } + + internal int? Height { get; set; } + + internal bool IsDirectStream { get; set; } + + internal string Format { get; set; } + + internal ItemImageInfo ItemImageInfo { get; set; } } } } diff --git a/Emby.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs index b730d9db25..b58fdff2c9 100644 --- a/Emby.Dlna/Didl/Filter.cs +++ b/Emby.Dlna/Didl/Filter.cs @@ -23,9 +23,7 @@ namespace Emby.Dlna.Didl public bool Contains(string field) { - // Don't bother with this. Some clients (media monkey) use the filter and then don't display very well when very little data comes back. - return true; - // return _all || ListHelper.ContainsIgnoreCase(_fields, field); + return _all || Array.Exists(_fields, x => x.Equals(field, StringComparison.OrdinalIgnoreCase)); } } } diff --git a/Emby.Dlna/DlnaConfigurationFactory.cs b/Emby.Dlna/DlnaConfigurationFactory.cs new file mode 100644 index 0000000000..4c6ca869aa --- /dev/null +++ b/Emby.Dlna/DlnaConfigurationFactory.cs @@ -0,0 +1,24 @@ +#nullable enable +#pragma warning disable CS1591 + +using System.Collections.Generic; +using Emby.Dlna.Configuration; +using MediaBrowser.Common.Configuration; + +namespace Emby.Dlna +{ + public class DlnaConfigurationFactory : IConfigurationFactory + { + public IEnumerable GetConfigurations() + { + return new[] + { + new ConfigurationStore + { + Key = "dlna", + ConfigurationType = typeof(DlnaOptions) + } + }; + } + } +} diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index a6a4740d7a..d5629684c3 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -495,8 +495,8 @@ namespace Emby.Dlna /// Recreates the object using serialization, to ensure it's not a subclass. /// If it's a subclass it may not serlialize properly to xml (different root element tag name). /// - /// - /// + /// The device profile. + /// The reserialized device profile. private DeviceProfile ReserializeProfile(DeviceProfile profile) { if (profile.GetType() == typeof(DeviceProfile)) @@ -509,13 +509,6 @@ namespace Emby.Dlna return _jsonSerializer.DeserializeFromString(json); } - private class InternalProfileInfo - { - internal DeviceProfileInfo Info { get; set; } - - internal string Path { get; set; } - } - public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress) { var profile = GetProfile(headers) ?? @@ -540,6 +533,13 @@ namespace Emby.Dlna Stream = _assembly.GetManifestResourceStream(resource) }; } + + private class InternalProfileInfo + { + internal DeviceProfileInfo Info { get; set; } + + internal string Path { get; set; } + } } /* diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 42a5f95c14..8538f580ca 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -20,7 +20,7 @@ netstandard2.1 false true - true + true diff --git a/Emby.Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs index fd18343e62..1b1bd426c5 100644 --- a/Emby.Dlna/EventSubscriptionResponse.cs +++ b/Emby.Dlna/EventSubscriptionResponse.cs @@ -15,6 +15,6 @@ namespace Emby.Dlna public string ContentType { get; set; } - public Dictionary Headers { get; set; } + public Dictionary Headers { get; } } } diff --git a/Emby.Dlna/IEventManager.cs b/Emby.Dlna/IEventManager.cs index 2872033892..de87b0709b 100644 --- a/Emby.Dlna/IEventManager.cs +++ b/Emby.Dlna/IEventManager.cs @@ -8,16 +8,26 @@ namespace Emby.Dlna /// Cancels the event subscription. /// /// The subscription identifier. + /// The response. EventSubscriptionResponse CancelEventSubscription(string subscriptionId); /// /// Renews the event subscription. /// + /// The subscription identifier. + /// The notification type. + /// The requested timeout as a sting. + /// The callback url. + /// The response. EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl); /// /// Creates the event subscription. /// + /// The notification type. + /// The requested timeout as a sting. + /// The callback url. + /// The response. EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl); } } diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 344f85f6ba..0ad82022dd 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -93,14 +93,14 @@ namespace Emby.Dlna.Main _networkManager = networkManager; _logger = loggerFactory.CreateLogger(); - ContentDirectory = new ContentDirectory.ContentDirectory( + ContentDirectory = new ContentDirectory.ContentDirectoryService( dlnaManager, userDataManager, imageProcessor, libraryManager, config, userManager, - loggerFactory.CreateLogger(), + loggerFactory.CreateLogger(), httpClient, localizationManager, mediaSourceManager, @@ -108,19 +108,19 @@ namespace Emby.Dlna.Main mediaEncoder, tvSeriesManager); - ConnectionManager = new ConnectionManager.ConnectionManager( + ConnectionManager = new ConnectionManager.ConnectionManagerService( dlnaManager, config, - loggerFactory.CreateLogger(), + loggerFactory.CreateLogger(), httpClient); - MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar( - loggerFactory.CreateLogger(), + MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrarService( + loggerFactory.CreateLogger(), httpClient, config); Current = this; } - + public static DlnaEntryPoint Current { get; private set; } public IContentDirectory ContentDirectory { get; private set; } @@ -413,28 +413,15 @@ namespace Emby.Dlna.Main /// public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and optionally managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) { if (_disposed) { return; } - if (disposing) - { - DisposeDevicePublisher(); - DisposePlayToManager(); - DisposeDeviceDiscovery(); - } + DisposeDevicePublisher(); + DisposePlayToManager(); + DisposeDeviceDiscovery(); if (_communicationsServer != null) { diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs similarity index 82% rename from Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs rename to Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs index 64dfc840a8..28de2fef52 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs @@ -8,12 +8,12 @@ using Microsoft.Extensions.Logging; namespace Emby.Dlna.MediaReceiverRegistrar { - public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar + public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar { private readonly IServerConfigurationManager _config; - public MediaReceiverRegistrar( - ILogger logger, + public MediaReceiverRegistrarService( + ILogger logger, IHttpClient httpClient, IServerConfigurationManager config) : base(logger, httpClient) diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 9f925eed53..5462e7abc5 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -21,15 +21,38 @@ namespace Emby.Dlna.PlayTo { private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private readonly IHttpClient _httpClient; + + private readonly ILogger _logger; + + private readonly object _timerLock = new object(); private Timer _timer; + private int _muteVol; + private int _volume; + private DateTime _lastVolumeRefresh; + private bool _volumeRefreshActive; + private int _connectFailureCount; + private bool _disposed; + + public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger) + { + Properties = deviceProperties; + _httpClient = httpClient; + _logger = logger; + } + + public event EventHandler PlaybackStart; + + public event EventHandler PlaybackProgress; + + public event EventHandler PlaybackStopped; + + public event EventHandler MediaChanged; public DeviceInfo Properties { get; set; } - private int _muteVol; public bool IsMuted { get; set; } - private int _volume; - public int Volume { get @@ -53,18 +76,13 @@ namespace Emby.Dlna.PlayTo public bool IsStopped => TransportState == TransportState.Stopped; - private readonly IHttpClient _httpClient; + public Action OnDeviceUnavailable { get; set; } - private readonly ILogger _logger; + private TransportCommands AvCommands { get; set; } - public Action OnDeviceUnavailable { get; set; } + private TransportCommands RendererCommands { get; set; } - public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger) - { - Properties = deviceProperties; - _httpClient = httpClient; - _logger = logger; - } + public UBaseObject CurrentMediaInfo { get; private set; } public void Start() { @@ -72,8 +90,6 @@ namespace Emby.Dlna.PlayTo _timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite); } - private DateTime _lastVolumeRefresh; - private bool _volumeRefreshActive; private Task RefreshVolumeIfNeeded() { if (_volumeRefreshActive @@ -104,7 +120,6 @@ namespace Emby.Dlna.PlayTo } } - private readonly object _timerLock = new object(); private void RestartTimer(bool immediate = false) { lock (_timerLock) @@ -232,6 +247,9 @@ namespace Emby.Dlna.PlayTo /// /// Sets volume on a scale of 0-100. /// + /// The volume on a scale of 0-100. + /// The cancellation token to cancel operation. + /// A representing the asynchronous operation. public async Task SetVolume(int value, CancellationToken cancellationToken) { var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); @@ -405,8 +423,6 @@ namespace Emby.Dlna.PlayTo RestartTimer(true); } - private int _connectFailureCount; - private async void TimerCallback(object sender) { if (_disposed) @@ -538,7 +554,7 @@ namespace Emby.Dlna.PlayTo return; } - var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null); + var volume = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null); var volumeValue = volume?.Value; if (string.IsNullOrWhiteSpace(volumeValue)) @@ -588,7 +604,7 @@ namespace Emby.Dlna.PlayTo return; } - var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse") + var valueNode = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetMuteResponse") .Select(i => i.Element("CurrentMute")) .FirstOrDefault(i => i != null); @@ -622,7 +638,7 @@ namespace Emby.Dlna.PlayTo } var transportState = - result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null); + result.Document.Descendants(UPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null); var transportStateValue = transportState?.Value; @@ -635,7 +651,7 @@ namespace Emby.Dlna.PlayTo return null; } - private async Task GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken) + private async Task GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo"); if (command == null) @@ -670,7 +686,7 @@ namespace Emby.Dlna.PlayTo return null; } - var e = track.Element(uPnpNamespaces.items) ?? track; + var e = track.Element(UPnpNamespaces.Items) ?? track; var elementString = (string)e; @@ -686,13 +702,13 @@ namespace Emby.Dlna.PlayTo return null; } - e = track.Element(uPnpNamespaces.items) ?? track; + e = track.Element(UPnpNamespaces.Items) ?? track; elementString = (string)e; if (!string.IsNullOrWhiteSpace(elementString)) { - return new uBaseObject + return new UBaseObject { Url = elementString }; @@ -701,7 +717,7 @@ namespace Emby.Dlna.PlayTo return null; } - private async Task<(bool, uBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) + private async Task<(bool, UBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo"); if (command == null) @@ -730,10 +746,10 @@ namespace Emby.Dlna.PlayTo return (false, null); } - var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null); + var trackUriElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null); var trackUri = trackUriElem?.Value; - var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null); + var durationElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null); var duration = durationElem?.Value; if (!string.IsNullOrWhiteSpace(duration) @@ -746,7 +762,7 @@ namespace Emby.Dlna.PlayTo Duration = null; } - var positionElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null); + var positionElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null); var position = positionElem?.Value; if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase)) @@ -786,7 +802,7 @@ namespace Emby.Dlna.PlayTo return (true, null); } - var e = uPnpResponse.Element(uPnpNamespaces.items); + var e = uPnpResponse.Element(UPnpNamespaces.Items); var uTrack = CreateUBaseObject(e, trackUri); @@ -827,26 +843,26 @@ namespace Emby.Dlna.PlayTo return null; } - private static uBaseObject CreateUBaseObject(XElement container, string trackUri) + private static UBaseObject CreateUBaseObject(XElement container, string trackUri) { if (container == null) { throw new ArgumentNullException(nameof(container)); } - var url = container.GetValue(uPnpNamespaces.Res); + var url = container.GetValue(UPnpNamespaces.Res); if (string.IsNullOrWhiteSpace(url)) { url = trackUri; } - return new uBaseObject + return new UBaseObject { - Id = container.GetAttributeValue(uPnpNamespaces.Id), - ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId), - Title = container.GetValue(uPnpNamespaces.title), - IconUrl = container.GetValue(uPnpNamespaces.Artwork), + Id = container.GetAttributeValue(UPnpNamespaces.Id), + ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId), + Title = container.GetValue(UPnpNamespaces.Title), + IconUrl = container.GetValue(UPnpNamespaces.Artwork), SecondText = string.Empty, Url = url, ProtocolInfo = GetProtocolInfo(container), @@ -861,11 +877,11 @@ namespace Emby.Dlna.PlayTo throw new ArgumentNullException(nameof(container)); } - var resElement = container.Element(uPnpNamespaces.Res); + var resElement = container.Element(UPnpNamespaces.Res); if (resElement != null) { - var info = resElement.Attribute(uPnpNamespaces.ProtocolInfo); + var info = resElement.Attribute(UPnpNamespaces.ProtocolInfo); if (info != null && !string.IsNullOrWhiteSpace(info.Value)) { @@ -953,11 +969,7 @@ namespace Emby.Dlna.PlayTo return baseUrl + url; } - private TransportCommands AvCommands { get; set; } - - private TransportCommands RendererCommands { get; set; } - - public static async Task CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken) + public static async Task CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, ILogger logger, CancellationToken cancellationToken) { var ssdpHttpClient = new SsdpHttpClient(httpClient); @@ -965,13 +977,13 @@ namespace Emby.Dlna.PlayTo var friendlyNames = new List(); - var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault(); + var name = document.Descendants(UPnpNamespaces.Ud.GetName("friendlyName")).FirstOrDefault(); if (name != null && !string.IsNullOrWhiteSpace(name.Value)) { friendlyNames.Add(name.Value); } - var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault(); + var room = document.Descendants(UPnpNamespaces.Ud.GetName("roomName")).FirstOrDefault(); if (room != null && !string.IsNullOrWhiteSpace(room.Value)) { friendlyNames.Add(room.Value); @@ -983,74 +995,74 @@ namespace Emby.Dlna.PlayTo BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port) }; - var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault(); + var model = document.Descendants(UPnpNamespaces.Ud.GetName("modelName")).FirstOrDefault(); if (model != null) { deviceProperties.ModelName = model.Value; } - var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault(); + var modelNumber = document.Descendants(UPnpNamespaces.Ud.GetName("modelNumber")).FirstOrDefault(); if (modelNumber != null) { deviceProperties.ModelNumber = modelNumber.Value; } - var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault(); + var uuid = document.Descendants(UPnpNamespaces.Ud.GetName("UDN")).FirstOrDefault(); if (uuid != null) { deviceProperties.UUID = uuid.Value; } - var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault(); + var manufacturer = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturer")).FirstOrDefault(); if (manufacturer != null) { deviceProperties.Manufacturer = manufacturer.Value; } - var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault(); + var manufacturerUrl = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturerURL")).FirstOrDefault(); if (manufacturerUrl != null) { deviceProperties.ManufacturerUrl = manufacturerUrl.Value; } - var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault(); + var presentationUrl = document.Descendants(UPnpNamespaces.Ud.GetName("presentationURL")).FirstOrDefault(); if (presentationUrl != null) { deviceProperties.PresentationUrl = presentationUrl.Value; } - var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault(); + var modelUrl = document.Descendants(UPnpNamespaces.Ud.GetName("modelURL")).FirstOrDefault(); if (modelUrl != null) { deviceProperties.ModelUrl = modelUrl.Value; } - var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault(); + var serialNumber = document.Descendants(UPnpNamespaces.Ud.GetName("serialNumber")).FirstOrDefault(); if (serialNumber != null) { deviceProperties.SerialNumber = serialNumber.Value; } - var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault(); + var modelDescription = document.Descendants(UPnpNamespaces.Ud.GetName("modelDescription")).FirstOrDefault(); if (modelDescription != null) { deviceProperties.ModelDescription = modelDescription.Value; } - var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault(); + var icon = document.Descendants(UPnpNamespaces.Ud.GetName("icon")).FirstOrDefault(); if (icon != null) { deviceProperties.Icon = CreateIcon(icon); } - foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList"))) + foreach (var services in document.Descendants(UPnpNamespaces.Ud.GetName("serviceList"))) { if (services == null) { continue; } - var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service")); + var servicesList = services.Descendants(UPnpNamespaces.Ud.GetName("service")); if (servicesList == null) { continue; @@ -1077,11 +1089,11 @@ namespace Emby.Dlna.PlayTo throw new ArgumentNullException(nameof(element)); } - var mimeType = element.GetDescendantValue(uPnpNamespaces.ud.GetName("mimetype")); - var width = element.GetDescendantValue(uPnpNamespaces.ud.GetName("width")); - var height = element.GetDescendantValue(uPnpNamespaces.ud.GetName("height")); - var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth")); - var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url")); + var mimeType = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("mimetype")); + var width = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("width")); + var height = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("height")); + var depth = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("depth")); + var url = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("url")); var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture); var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture); @@ -1098,11 +1110,11 @@ namespace Emby.Dlna.PlayTo private static DeviceService Create(XElement element) { - var type = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceType")); - var id = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceId")); - var scpdUrl = element.GetDescendantValue(uPnpNamespaces.ud.GetName("SCPDURL")); - var controlURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("controlURL")); - var eventSubURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("eventSubURL")); + var type = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceType")); + var id = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceId")); + var scpdUrl = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("SCPDURL")); + var controlURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("controlURL")); + var eventSubURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("eventSubURL")); return new DeviceService { @@ -1114,14 +1126,7 @@ namespace Emby.Dlna.PlayTo }; } - public event EventHandler PlaybackStart; - public event EventHandler PlaybackProgress; - public event EventHandler PlaybackStopped; - public event EventHandler MediaChanged; - - public uBaseObject CurrentMediaInfo { get; private set; } - - private void UpdateMediaInfo(uBaseObject mediaInfo, TransportState state) + private void UpdateMediaInfo(UBaseObject mediaInfo, TransportState state) { TransportState = state; @@ -1149,7 +1154,7 @@ namespace Emby.Dlna.PlayTo } } - private void OnPlaybackStart(uBaseObject mediaInfo) + private void OnPlaybackStart(UBaseObject mediaInfo) { if (string.IsNullOrWhiteSpace(mediaInfo.Url)) { @@ -1162,7 +1167,7 @@ namespace Emby.Dlna.PlayTo }); } - private void OnPlaybackProgress(uBaseObject mediaInfo) + private void OnPlaybackProgress(UBaseObject mediaInfo) { if (string.IsNullOrWhiteSpace(mediaInfo.Url)) { @@ -1175,7 +1180,7 @@ namespace Emby.Dlna.PlayTo }); } - private void OnPlaybackStop(uBaseObject mediaInfo) + private void OnPlaybackStop(UBaseObject mediaInfo) { PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs { @@ -1183,7 +1188,7 @@ namespace Emby.Dlna.PlayTo }); } - private void OnMediaChanged(uBaseObject old, uBaseObject newMedia) + private void OnMediaChanged(UBaseObject old, UBaseObject newMedia) { MediaChanged?.Invoke(this, new MediaChangedEventArgs { @@ -1192,8 +1197,7 @@ namespace Emby.Dlna.PlayTo }); } - bool _disposed; - + /// public void Dispose() { Dispose(true); @@ -1222,6 +1226,7 @@ namespace Emby.Dlna.PlayTo _disposed = true; } + /// public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl); diff --git a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs index a8043d4ead..dabd079afd 100644 --- a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs +++ b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs @@ -6,8 +6,8 @@ namespace Emby.Dlna.PlayTo { public class MediaChangedEventArgs : EventArgs { - public uBaseObject OldMediaInfo { get; set; } + public UBaseObject OldMediaInfo { get; set; } - public uBaseObject NewMediaInfo { get; set; } + public UBaseObject NewMediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index f8ce197577..d9e10f459a 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -377,7 +377,8 @@ namespace Emby.Dlna.PlayTo _session.ApplicationVersion, _session.DeviceId, _session.DeviceName, - _session.RemoteEndPoint, user); + _session.RemoteEndPoint, + user); } return PlayItems(playlist, cancellationToken); @@ -502,42 +503,44 @@ namespace Emby.Dlna.PlayTo if (streamInfo.MediaType == DlnaProfileType.Audio) { return new ContentFeatureBuilder(profile) - .BuildAudioHeader(streamInfo.Container, - streamInfo.TargetAudioCodec.FirstOrDefault(), - streamInfo.TargetAudioBitrate, - streamInfo.TargetAudioSampleRate, - streamInfo.TargetAudioChannels, - streamInfo.TargetAudioBitDepth, - streamInfo.IsDirectStream, - streamInfo.RunTimeTicks ?? 0, - streamInfo.TranscodeSeekInfo); + .BuildAudioHeader( + streamInfo.Container, + streamInfo.TargetAudioCodec.FirstOrDefault(), + streamInfo.TargetAudioBitrate, + streamInfo.TargetAudioSampleRate, + streamInfo.TargetAudioChannels, + streamInfo.TargetAudioBitDepth, + streamInfo.IsDirectStream, + streamInfo.RunTimeTicks ?? 0, + streamInfo.TranscodeSeekInfo); } if (streamInfo.MediaType == DlnaProfileType.Video) { var list = new ContentFeatureBuilder(profile) - .BuildVideoHeader(streamInfo.Container, - streamInfo.TargetVideoCodec.FirstOrDefault(), - streamInfo.TargetAudioCodec.FirstOrDefault(), - streamInfo.TargetWidth, - streamInfo.TargetHeight, - streamInfo.TargetVideoBitDepth, - streamInfo.TargetVideoBitrate, - streamInfo.TargetTimestamp, - streamInfo.IsDirectStream, - streamInfo.RunTimeTicks ?? 0, - streamInfo.TargetVideoProfile, - streamInfo.TargetVideoLevel, - streamInfo.TargetFramerate ?? 0, - streamInfo.TargetPacketLength, - streamInfo.TranscodeSeekInfo, - streamInfo.IsTargetAnamorphic, - streamInfo.IsTargetInterlaced, - streamInfo.TargetRefFrames, - streamInfo.TargetVideoStreamCount, - streamInfo.TargetAudioStreamCount, - streamInfo.TargetVideoCodecTag, - streamInfo.IsTargetAVC); + .BuildVideoHeader( + streamInfo.Container, + streamInfo.TargetVideoCodec.FirstOrDefault(), + streamInfo.TargetAudioCodec.FirstOrDefault(), + streamInfo.TargetWidth, + streamInfo.TargetHeight, + streamInfo.TargetVideoBitDepth, + streamInfo.TargetVideoBitrate, + streamInfo.TargetTimestamp, + streamInfo.IsDirectStream, + streamInfo.RunTimeTicks ?? 0, + streamInfo.TargetVideoProfile, + streamInfo.TargetVideoLevel, + streamInfo.TargetFramerate ?? 0, + streamInfo.TargetPacketLength, + streamInfo.TranscodeSeekInfo, + streamInfo.IsTargetAnamorphic, + streamInfo.IsTargetInterlaced, + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount, + streamInfo.TargetVideoCodecTag, + streamInfo.IsTargetAVC); return list.Count == 0 ? null : list[0]; } @@ -681,48 +684,41 @@ namespace Emby.Dlna.PlayTo case GeneralCommandType.ToggleMute: return _device.ToggleMute(cancellationToken); case GeneralCommandType.SetAudioStreamIndex: + if (command.Arguments.TryGetValue("Index", out string index)) { - if (command.Arguments.TryGetValue("Index", out string arg)) + if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val)) { - if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val)) - { - return SetAudioStreamIndex(val); - } - - throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied."); + return SetAudioStreamIndex(val); } - throw new ArgumentException("SetAudioStreamIndex argument cannot be null"); + throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied."); } + + throw new ArgumentException("SetAudioStreamIndex argument cannot be null"); case GeneralCommandType.SetSubtitleStreamIndex: + if (command.Arguments.TryGetValue("Index", out index)) { - if (command.Arguments.TryGetValue("Index", out string arg)) + if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val)) { - if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val)) - { - return SetSubtitleStreamIndex(val); - } - - throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied."); + return SetSubtitleStreamIndex(val); } - throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null"); + throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied."); } + + throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null"); case GeneralCommandType.SetVolume: + if (command.Arguments.TryGetValue("Volume", out string vol)) { - if (command.Arguments.TryGetValue("Volume", out string arg)) + if (int.TryParse(vol, NumberStyles.Integer, _usCulture, out var volume)) { - if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var volume)) - { - return _device.SetVolume(volume, cancellationToken); - } - - throw new ArgumentException("Unsupported volume value supplied."); + return _device.SetVolume(volume, cancellationToken); } - throw new ArgumentException("Volume argument cannot be null"); + throw new ArgumentException("Unsupported volume value supplied."); } + throw new ArgumentException("Volume argument cannot be null"); default: return Task.CompletedTask; } @@ -795,6 +791,62 @@ namespace Emby.Dlna.PlayTo await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false); } + private static int? GetIntValue(IReadOnlyDictionary values, string name) + { + var value = values.GetValueOrDefault(name); + + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) + { + return result; + } + + return null; + } + + private static long GetLongValue(IReadOnlyDictionary values, string name) + { + var value = values.GetValueOrDefault(name); + + if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) + { + return result; + } + + return 0; + } + + /// + public Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken) + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + + if (_device == null) + { + return Task.CompletedTask; + } + + if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase)) + { + return SendPlayCommand(data as PlayRequest, cancellationToken); + } + + if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) + { + return SendPlaystateCommand(data as PlaystateRequest, cancellationToken); + } + + if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) + { + return SendGeneralCommand(data as GeneralCommand, cancellationToken); + } + + // Not supported or needed right now + return Task.CompletedTask; + } + private class StreamParams { private MediaSourceInfo mediaSource; @@ -908,61 +960,5 @@ namespace Emby.Dlna.PlayTo return request; } } - - private static int? GetIntValue(IReadOnlyDictionary values, string name) - { - var value = values.GetValueOrDefault(name); - - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) - { - return result; - } - - return null; - } - - private static long GetLongValue(IReadOnlyDictionary values, string name) - { - var value = values.GetValueOrDefault(name); - - if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) - { - return result; - } - - return 0; - } - - /// - public Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken) - { - if (_disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (_device == null) - { - return Task.CompletedTask; - } - - if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase)) - { - return SendPlayCommand(data as PlayRequest, cancellationToken); - } - - if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) - { - return SendPlaystateCommand(data as PlaystateRequest, cancellationToken); - } - - if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) - { - return SendGeneralCommand(data as GeneralCommand, cancellationToken); - } - - // Not supported or needed right now - return Task.CompletedTask; - } } } diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 99b9105cc5..54f4fee051 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -174,7 +174,7 @@ namespace Emby.Dlna.PlayTo if (controller == null) { - var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, cancellationToken).ConfigureAwait(false); + var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _logger, cancellationToken).ConfigureAwait(false); string deviceName = device.Properties.Name; diff --git a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs index 795618df23..d14617c8a0 100644 --- a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs @@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo { public class PlaybackProgressEventArgs : EventArgs { - public uBaseObject MediaInfo { get; set; } + public UBaseObject MediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs index 27883ca32a..3f8d552636 100644 --- a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs @@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo { public class PlaybackStartEventArgs : EventArgs { - public uBaseObject MediaInfo { get; set; } + public UBaseObject MediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs index 5944d936cd..deeb47918d 100644 --- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs @@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo { public class PlaybackStoppedEventArgs : EventArgs { - public uBaseObject MediaInfo { get; set; } + public UBaseObject MediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs index dc797a6918..fda17a8b41 100644 --- a/Emby.Dlna/PlayTo/TransportCommands.cs +++ b/Emby.Dlna/PlayTo/TransportCommands.cs @@ -16,34 +16,26 @@ namespace Emby.Dlna.PlayTo private List _stateVariables = new List(); private List _serviceActions = new List(); - public List StateVariables - { - get => _stateVariables; - set => _stateVariables = value; - } + public List StateVariables => _stateVariables; - public List ServiceActions - { - get => _serviceActions; - set => _serviceActions = value; - } + public List ServiceActions => _serviceActions; public static TransportCommands Create(XDocument document) { var command = new TransportCommands(); - var actionList = document.Descendants(uPnpNamespaces.svc + "actionList"); + var actionList = document.Descendants(UPnpNamespaces.Svc + "actionList"); - foreach (var container in actionList.Descendants(uPnpNamespaces.svc + "action")) + foreach (var container in actionList.Descendants(UPnpNamespaces.Svc + "action")) { command.ServiceActions.Add(ServiceActionFromXml(container)); } - var stateValues = document.Descendants(uPnpNamespaces.ServiceStateTable).FirstOrDefault(); + var stateValues = document.Descendants(UPnpNamespaces.ServiceStateTable).FirstOrDefault(); if (stateValues != null) { - foreach (var container in stateValues.Elements(uPnpNamespaces.svc + "stateVariable")) + foreach (var container in stateValues.Elements(UPnpNamespaces.Svc + "stateVariable")) { command.StateVariables.Add(FromXml(container)); } @@ -54,19 +46,19 @@ namespace Emby.Dlna.PlayTo private static ServiceAction ServiceActionFromXml(XElement container) { - var argumentList = new List(); + var serviceAction = new ServiceAction + { + Name = container.GetValue(UPnpNamespaces.Svc + "name"), + }; + + var argumentList = serviceAction.ArgumentList; - foreach (var arg in container.Descendants(uPnpNamespaces.svc + "argument")) + foreach (var arg in container.Descendants(UPnpNamespaces.Svc + "argument")) { argumentList.Add(ArgumentFromXml(arg)); } - return new ServiceAction - { - Name = container.GetValue(uPnpNamespaces.svc + "name"), - - ArgumentList = argumentList - }; + return serviceAction; } private static Argument ArgumentFromXml(XElement container) @@ -78,29 +70,29 @@ namespace Emby.Dlna.PlayTo return new Argument { - Name = container.GetValue(uPnpNamespaces.svc + "name"), - Direction = container.GetValue(uPnpNamespaces.svc + "direction"), - RelatedStateVariable = container.GetValue(uPnpNamespaces.svc + "relatedStateVariable") + Name = container.GetValue(UPnpNamespaces.Svc + "name"), + Direction = container.GetValue(UPnpNamespaces.Svc + "direction"), + RelatedStateVariable = container.GetValue(UPnpNamespaces.Svc + "relatedStateVariable") }; } private static StateVariable FromXml(XElement container) { var allowedValues = new List(); - var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList") + var element = container.Descendants(UPnpNamespaces.Svc + "allowedValueList") .FirstOrDefault(); if (element != null) { - var values = element.Descendants(uPnpNamespaces.svc + "allowedValue"); + var values = element.Descendants(UPnpNamespaces.Svc + "allowedValue"); allowedValues.AddRange(values.Select(child => child.Value)); } return new StateVariable { - Name = container.GetValue(uPnpNamespaces.svc + "name"), - DataType = container.GetValue(uPnpNamespaces.svc + "dataType"), + Name = container.GetValue(UPnpNamespaces.Svc + "name"), + DataType = container.GetValue(UPnpNamespaces.Svc + "dataType"), AllowedValues = allowedValues.ToArray() }; } @@ -183,8 +175,7 @@ namespace Emby.Dlna.PlayTo if (state != null) { var sendValue = state.AllowedValues.FirstOrDefault(a => string.Equals(a, commandParameter, StringComparison.OrdinalIgnoreCase)) ?? - state.AllowedValues.FirstOrDefault() ?? - value; + (state.AllowedValues.Count > 0 ? state.AllowedValues[0] : value); return string.Format(CultureInfo.InvariantCulture, "<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}", argument.Name, state.DataType ?? "string", sendValue); } diff --git a/Emby.Dlna/PlayTo/TransportState.cs b/Emby.Dlna/PlayTo/TransportState.cs index 2058e9dc79..7068a5d24d 100644 --- a/Emby.Dlna/PlayTo/TransportState.cs +++ b/Emby.Dlna/PlayTo/TransportState.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1602 namespace Emby.Dlna.PlayTo { diff --git a/Emby.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs index e2d7a10f02..05f27603fb 100644 --- a/Emby.Dlna/PlayTo/UpnpContainer.cs +++ b/Emby.Dlna/PlayTo/UpnpContainer.cs @@ -6,22 +6,22 @@ using Emby.Dlna.Ssdp; namespace Emby.Dlna.PlayTo { - public class UpnpContainer : uBaseObject + public class UpnpContainer : UBaseObject { - public static uBaseObject Create(XElement container) + public static UBaseObject Create(XElement container) { if (container == null) { throw new ArgumentNullException(nameof(container)); } - return new uBaseObject + return new UBaseObject { - Id = container.GetAttributeValue(uPnpNamespaces.Id), - ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId), - Title = container.GetValue(uPnpNamespaces.title), - IconUrl = container.GetValue(uPnpNamespaces.Artwork), - UpnpClass = container.GetValue(uPnpNamespaces.uClass) + Id = container.GetAttributeValue(UPnpNamespaces.Id), + ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId), + Title = container.GetValue(UPnpNamespaces.Title), + IconUrl = container.GetValue(UPnpNamespaces.Artwork), + UpnpClass = container.GetValue(UPnpNamespaces.Class) }; } } diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs index 1f0b06f689..0d9478e42e 100644 --- a/Emby.Dlna/PlayTo/uBaseObject.cs +++ b/Emby.Dlna/PlayTo/uBaseObject.cs @@ -1,10 +1,11 @@ #pragma warning disable CS1591 using System; +using System.Collections.Generic; namespace Emby.Dlna.PlayTo { - public class uBaseObject + public class UBaseObject { public string Id { get; set; } @@ -20,7 +21,7 @@ namespace Emby.Dlna.PlayTo public string Url { get; set; } - public string[] ProtocolInfo { get; set; } + public IReadOnlyList ProtocolInfo { get; set; } public string UpnpClass { get; set; } @@ -49,7 +50,7 @@ namespace Emby.Dlna.PlayTo } } - public bool Equals(uBaseObject obj) + public bool Equals(UBaseObject obj) { if (obj == null) { diff --git a/Emby.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs index 6ea7dc9cf7..5042d44938 100644 --- a/Emby.Dlna/PlayTo/uPnpNamespaces.cs +++ b/Emby.Dlna/PlayTo/uPnpNamespaces.cs @@ -4,38 +4,64 @@ using System.Xml.Linq; namespace Emby.Dlna.PlayTo { - public static class uPnpNamespaces + public static class UPnpNamespaces { - public static XNamespace dc = "http://purl.org/dc/elements/1.1/"; - public static XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; - public static XNamespace svc = "urn:schemas-upnp-org:service-1-0"; - public static XNamespace ud = "urn:schemas-upnp-org:device-1-0"; - public static XNamespace upnp = "urn:schemas-upnp-org:metadata-1-0/upnp/"; - public static XNamespace RenderingControl = "urn:schemas-upnp-org:service:RenderingControl:1"; - public static XNamespace AvTransport = "urn:schemas-upnp-org:service:AVTransport:1"; - public static XNamespace ContentDirectory = "urn:schemas-upnp-org:service:ContentDirectory:1"; - - public static XName containers = ns + "container"; - public static XName items = ns + "item"; - public static XName title = dc + "title"; - public static XName creator = dc + "creator"; - public static XName artist = upnp + "artist"; - public static XName Id = "id"; - public static XName ParentId = "parentID"; - public static XName uClass = upnp + "class"; - public static XName Artwork = upnp + "albumArtURI"; - public static XName Description = dc + "description"; - public static XName LongDescription = upnp + "longDescription"; - public static XName Album = upnp + "album"; - public static XName Author = upnp + "author"; - public static XName Director = upnp + "director"; - public static XName PlayCount = upnp + "playbackCount"; - public static XName Tracknumber = upnp + "originalTrackNumber"; - public static XName Res = ns + "res"; - public static XName Duration = "duration"; - public static XName ProtocolInfo = "protocolInfo"; - - public static XName ServiceStateTable = svc + "serviceStateTable"; - public static XName StateVariable = svc + "stateVariable"; + public static XNamespace Dc { get; } = "http://purl.org/dc/elements/1.1/"; + + public static XNamespace Ns { get; } = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; + + public static XNamespace Svc { get; } = "urn:schemas-upnp-org:service-1-0"; + + public static XNamespace Ud { get; } = "urn:schemas-upnp-org:device-1-0"; + + public static XNamespace UPnp { get; } = "urn:schemas-upnp-org:metadata-1-0/upnp/"; + + public static XNamespace RenderingControl { get; } = "urn:schemas-upnp-org:service:RenderingControl:1"; + + public static XNamespace AvTransport { get; } = "urn:schemas-upnp-org:service:AVTransport:1"; + + public static XNamespace ContentDirectory { get; } = "urn:schemas-upnp-org:service:ContentDirectory:1"; + + public static XName Containers { get; } = Ns + "container"; + + public static XName Items { get; } = Ns + "item"; + + public static XName Title { get; } = Dc + "title"; + + public static XName Creator { get; } = Dc + "creator"; + + public static XName Artist { get; } = UPnp + "artist"; + + public static XName Id { get; } = "id"; + + public static XName ParentId { get; } = "parentID"; + + public static XName Class { get; } = UPnp + "class"; + + public static XName Artwork { get; } = UPnp + "albumArtURI"; + + public static XName Description { get; } = Dc + "description"; + + public static XName LongDescription { get; } = UPnp + "longDescription"; + + public static XName Album { get; } = UPnp + "album"; + + public static XName Author { get; } = UPnp + "author"; + + public static XName Director { get; } = UPnp + "director"; + + public static XName PlayCount { get; } = UPnp + "playbackCount"; + + public static XName Tracknumber { get; } = UPnp + "originalTrackNumber"; + + public static XName Res { get; } = Ns + "res"; + + public static XName Duration { get; } = "duration"; + + public static XName ProtocolInfo { get; } = "protocolInfo"; + + public static XName ServiceStateTable { get; } = Svc + "serviceStateTable"; + + public static XName StateVariable { get; } = Svc + "stateVariable"; } } diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 91a21a21d2..d160e33393 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -210,15 +210,6 @@ namespace Emby.Dlna.Service } } - private class ControlRequestInfo - { - public string LocalName { get; set; } - - public string NamespaceURI { get; set; } - - public Dictionary Headers { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - protected abstract void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter); private void LogRequest(ControlRequest request) @@ -240,5 +231,14 @@ namespace Emby.Dlna.Service Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml); } + + private class ControlRequestInfo + { + public string LocalName { get; set; } + + public string NamespaceURI { get; set; } + + public Dictionary Headers { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + } } } diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs index 88c62f86c1..3ad1ea9e0e 100644 --- a/Emby.Dlna/Service/BaseService.cs +++ b/Emby.Dlna/Service/BaseService.cs @@ -8,31 +8,33 @@ namespace Emby.Dlna.Service { public class BaseService : IEventManager { - protected IEventManager _eventManager; - protected IHttpClient _httpClient; - protected ILogger Logger; - protected BaseService(ILogger logger, IHttpClient httpClient) { Logger = logger; - _httpClient = httpClient; + HttpClient = httpClient; - _eventManager = new EventManager(logger, _httpClient); + EventManager = new EventManager(logger, HttpClient); } + protected IEventManager EventManager { get; } + + protected IHttpClient HttpClient { get; } + + protected ILogger Logger { get; } + public EventSubscriptionResponse CancelEventSubscription(string subscriptionId) { - return _eventManager.CancelEventSubscription(subscriptionId); + return EventManager.CancelEventSubscription(subscriptionId); } public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string timeoutString, string callbackUrl) { - return _eventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); + return EventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); } public EventSubscriptionResponse CreateEventSubscription(string notificationType, string timeoutString, string callbackUrl) { - return _eventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); + return EventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); } } } diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs index 6c7d6f8462..1e56d09b29 100644 --- a/Emby.Dlna/Service/ServiceXmlBuilder.cs +++ b/Emby.Dlna/Service/ServiceXmlBuilder.cs @@ -87,7 +87,7 @@ namespace Emby.Dlna.Service .Append(SecurityElement.Escape(item.DataType ?? string.Empty)) .Append(""); - if (item.AllowedValues.Length > 0) + if (item.AllowedValues.Count > 0) { builder.Append(""); foreach (var allowedValue in item.AllowedValues) diff --git a/Emby.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/SsdpExtensions.cs similarity index 94% rename from Emby.Dlna/Ssdp/Extensions.cs rename to Emby.Dlna/Ssdp/SsdpExtensions.cs index 613d332b2d..e7a52f168f 100644 --- a/Emby.Dlna/Ssdp/Extensions.cs +++ b/Emby.Dlna/Ssdp/SsdpExtensions.cs @@ -5,7 +5,7 @@ using System.Xml.Linq; namespace Emby.Dlna.Ssdp { - public static class Extensions + public static class SsdpExtensions { public static string GetValue(this XElement container, XName name) { diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 0100d642be..bbaf39eb11 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -221,9 +221,8 @@ namespace Jellyfin.Api.Controllers private Task ProcessControlRequestInternalAsync(string id, Stream requestStream, IUpnpService service) { - return service.ProcessControlRequestAsync(new ControlRequest + return service.ProcessControlRequestAsync(new ControlRequest(Request.Headers) { - Headers = Request.Headers, InputXml = requestStream, TargetServerUuId = id, RequestedUrl = GetAbsoluteUri() From 07e7125d07b8440ce01cabc65edec38e2dee0bf2 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 21 Aug 2020 07:24:44 -0600 Subject: [PATCH 089/102] Fix dlna play to --- Jellyfin.Api/Controllers/SessionController.cs | 16 ++++++---------- MediaBrowser.Model/Session/PlayRequest.cs | 3 --- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 4795fb0cf7..ba8d515988 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -153,7 +153,6 @@ namespace Jellyfin.Api.Controllers /// The ids of the items to play, comma delimited. /// The starting position of the first item. /// The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now. - /// The . /// Instruction sent to session. /// A . [HttpPost("Sessions/{sessionId}/Playing")] @@ -163,17 +162,14 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string? sessionId, [FromQuery] Guid[] itemIds, [FromQuery] long? startPositionTicks, - [FromQuery] PlayCommand playCommand, - [FromBody, Required] PlayRequest playRequest) + [FromQuery] PlayCommand playCommand) { - if (playRequest == null) + var playRequest = new PlayRequest { - throw new ArgumentException("Request Body may not be null"); - } - - playRequest.ItemIds = itemIds; - playRequest.StartPositionTicks = startPositionTicks; - playRequest.PlayCommand = playCommand; + ItemIds = itemIds, + StartPositionTicks = startPositionTicks, + PlayCommand = playCommand + }; _sessionManager.SendPlayCommand( RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id, diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs index d57bed171a..eeb25c2e89 100644 --- a/MediaBrowser.Model/Session/PlayRequest.cs +++ b/MediaBrowser.Model/Session/PlayRequest.cs @@ -15,21 +15,18 @@ namespace MediaBrowser.Model.Session /// Gets or sets the item ids. /// /// The item ids. - [ApiMember(Name = "ItemIds", Description = "The ids of the items to play, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] public Guid[] ItemIds { get; set; } /// /// Gets or sets the start position ticks that the first item should be played at. /// /// The start position ticks. - [ApiMember(Name = "StartPositionTicks", Description = "The starting position of the first item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public long? StartPositionTicks { get; set; } /// /// Gets or sets the play command. /// /// The play command. - [ApiMember(Name = "PlayCommand", Description = "The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public PlayCommand PlayCommand { get; set; } /// From 384a62dc0883549b328dc1f70d19358ae0aa593e Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 21 Aug 2020 10:23:47 -0600 Subject: [PATCH 090/102] Add nullable int32, int64 json converters --- .../Converters/JsonNullableInt32Converter.cs | 55 +++++++++++++++ .../Converters/JsonNullableInt64Converter.cs | 70 +++++++++++++++++++ MediaBrowser.Common/Json/JsonDefaults.cs | 2 + 3 files changed, 127 insertions(+) create mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs create mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs new file mode 100644 index 0000000000..c4579d989d --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs @@ -0,0 +1,55 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Converts a GUID object or value to/from JSON. + /// + public class JsonNullableInt32Converter : JsonConverter + { + /// + public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + ReadOnlySpan span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed) + { + return number; + } + + var stringValue = reader.GetString().AsSpan(); + + // value is null or empty, just return null. + if (stringValue.IsEmpty) + { + return null; + } + + if (int.TryParse(stringValue, out number)) + { + return number; + } + } + + return reader.GetInt32(); + } + + /// + public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteNumberValue(value.Value); + } + } + } +} diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs new file mode 100644 index 0000000000..53e5f6e9df --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs @@ -0,0 +1,70 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Parse JSON string as nullable long. + /// Javascript does not support 64-bit integers. + /// + public class JsonNullableInt64Converter : JsonConverter + { + /// + /// Read JSON string as int64. + /// + /// . + /// Type. + /// Options. + /// Parsed value. + public override long? Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + // try to parse number directly from bytes + var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed) + { + return number; + } + + var stringValue = reader.GetString().AsSpan(); + + // value is null or empty, just return null. + if (stringValue.IsEmpty) + { + return null; + } + + // try to parse from a string if the above failed, this covers cases with other escaped/UTF characters + if (long.TryParse(stringValue, out number)) + { + return number; + } + } + + // fallback to default handling + return reader.GetInt64(); + } + + /// + /// Write long to JSON long. + /// + /// . + /// Value to write. + /// Options. + public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteNumberValue(value.Value); + } + } + } +} diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 36ab6d900a..0a661934e5 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -29,9 +29,11 @@ namespace MediaBrowser.Common.Json options.Converters.Add(new JsonGuidConverter()); options.Converters.Add(new JsonInt32Converter()); + options.Converters.Add(new JsonNullableInt32Converter()); options.Converters.Add(new JsonStringEnumConverter()); options.Converters.Add(new JsonNonStringKeyDictionaryConverterFactory()); options.Converters.Add(new JsonInt64Converter()); + options.Converters.Add(new JsonNullableInt64Converter()); options.Converters.Add(new JsonDoubleConverter()); return options; From 89c9ca68f9ed669609c15084b01394ba33527294 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 21 Aug 2020 11:50:45 -0600 Subject: [PATCH 091/102] bump DotNet.Glob --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- .../Library/IgnorePatternsTests.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 1adef68aa7..60564f7006 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -41,7 +41,7 @@ - + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs index b4e6db8f30..09eb223288 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs @@ -30,8 +30,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("/media/movies/.@__thumb/foo-bar-thumbnail.png", true)] [InlineData("/media/music/Foo B.A.R./epic.flac", false)] [InlineData("/media/music/Foo B.A.R", false)] - // This test is pending an upstream fix: https://github.com/dazinator/DotNet.Glob/issues/78 - // [InlineData("/media/music/Foo B.A.R.", false)] + [InlineData("/media/music/Foo B.A.R.", false)] public void PathIgnored(string path, bool expected) { Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path)); From 119f64f5e7b09aeb4ff8f59237093906c1e08f5f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 21 Aug 2020 22:01:19 +0200 Subject: [PATCH 092/102] Make some methods async --- .../Channels/ChannelManager.cs | 21 +++++--- .../Collections/CollectionManager.cs | 51 ++++++++----------- .../Library/LibraryManager.cs | 39 ++++++-------- .../LiveTv/LiveTvManager.cs | 16 +++--- .../Playlists/PlaylistManager.cs | 16 +++--- .../Controllers/CollectionController.cs | 15 +++--- Jellyfin.Api/Controllers/ImageController.cs | 14 ++--- .../Controllers/ItemUpdateController.cs | 7 +-- .../Controllers/PlaylistsController.cs | 12 ++--- .../Controllers/RemoteImageController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 14 ++--- .../Collections/ICollectionManager.cs | 12 ++--- MediaBrowser.Controller/Entities/BaseItem.cs | 20 ++++---- MediaBrowser.Controller/Entities/Folder.cs | 4 +- MediaBrowser.Controller/Entities/Video.cs | 7 +-- .../Library/ILibraryManager.cs | 41 ++++++++++++--- .../Playlists/IPlaylistManager.cs | 6 +-- .../Encoder/EncoderValidator.cs | 5 +- .../Manager/MetadataService.cs | 4 +- .../TV/DummySeasonProvider.cs | 4 +- 20 files changed, 165 insertions(+), 145 deletions(-) diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index d8ab1f1a14..26fc1bee44 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -746,12 +746,21 @@ namespace Emby.Server.Implementations.Channels // null if came from cache if (itemsResult != null) { - var internalItems = itemsResult.Items - .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken)) - .ToArray(); + var items = itemsResult.Items; + var itemsLen = items.Count; + var internalItems = new Guid[itemsLen]; + for (int i = 0; i < itemsLen; i++) + { + internalItems[i] = (await GetChannelItemEntityAsync( + items[i], + channelProvider, + channel.Id, + parentItem, + cancellationToken).ConfigureAwait(false)).Id; + } var existingIds = _libraryManager.GetItemIds(query); - var deadIds = existingIds.Except(internalItems.Select(i => i.Id)) + var deadIds = existingIds.Except(internalItems) .ToArray(); foreach (var deadId in deadIds) @@ -963,7 +972,7 @@ namespace Emby.Server.Implementations.Channels return item; } - private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken) + private async Task GetChannelItemEntityAsync(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken) { var parentFolderId = parentFolder.Id; @@ -1165,7 +1174,7 @@ namespace Emby.Server.Implementations.Channels } else if (forceUpdate) { - item.UpdateToRepository(ItemUpdateType.None, cancellationToken); + await item.UpdateToRepositoryAsync(ItemUpdateType.None, cancellationToken).ConfigureAwait(false); } if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index ac2edc1e2e..3011a37e31 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Collections } /// - public BoxSet CreateCollection(CollectionCreationOptions options) + public async Task CreateCollectionAsync(CollectionCreationOptions options) { var name = options.Name; @@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Collections // This could cause it to get re-resolved as a plain folder var folderName = _fileSystem.GetValidFilename(name) + " [boxset]"; - var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult(); + var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false); if (parentFolder == null) { @@ -169,12 +169,16 @@ namespace Emby.Server.Implementations.Collections if (options.ItemIdList.Length > 0) { - AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) - { - // The initial adding of items is going to create a local metadata file - // This will cause internet metadata to be skipped as a result - MetadataRefreshMode = MetadataRefreshMode.FullRefresh - }); + await AddToCollectionAsync( + collection.Id, + options.ItemIdList.Select(x => new Guid(x)), + false, + new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + { + // The initial adding of items is going to create a local metadata file + // This will cause internet metadata to be skipped as a result + MetadataRefreshMode = MetadataRefreshMode.FullRefresh + }).ConfigureAwait(false); } else { @@ -197,18 +201,10 @@ namespace Emby.Server.Implementations.Collections } /// - public void AddToCollection(Guid collectionId, IEnumerable ids) - { - AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); - } + public Task AddToCollectionAsync(Guid collectionId, IEnumerable ids) + => AddToCollectionAsync(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); - /// - public void AddToCollection(Guid collectionId, IEnumerable ids) - { - AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); - } - - private void AddToCollection(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions) + private async Task AddToCollectionAsync(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions) { var collection = _libraryManager.GetItemById(collectionId) as BoxSet; if (collection == null) @@ -224,15 +220,14 @@ namespace Emby.Server.Implementations.Collections foreach (var id in ids) { - var guidId = new Guid(id); - var item = _libraryManager.GetItemById(guidId); + var item = _libraryManager.GetItemById(id); if (item == null) { throw new ArgumentException("No item exists with the supplied Id"); } - if (!currentLinkedChildrenIds.Contains(guidId)) + if (!currentLinkedChildrenIds.Contains(id)) { itemList.Add(item); @@ -249,7 +244,7 @@ namespace Emby.Server.Implementations.Collections collection.UpdateRatingToItems(linkedChildrenList); - collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); refreshOptions.ForceSave = true; _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High); @@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Collections } /// - public void RemoveFromCollection(Guid collectionId, IEnumerable itemIds) - { - RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i))); - } - - /// - public void RemoveFromCollection(Guid collectionId, IEnumerable itemIds) + public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable itemIds) { var collection = _libraryManager.GetItemById(collectionId) as BoxSet; @@ -309,7 +298,7 @@ namespace Emby.Server.Implementations.Collections collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray(); } - collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); _providerManager.QueueRefresh( collection.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 7ed8f0bbfb..375f09f5bb 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -771,7 +771,7 @@ namespace Emby.Server.Implementations.Library if (folder.ParentId != rootFolder.Id) { folder.ParentId = rootFolder.Id; - folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); + folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult(); } rootFolder.AddVirtualChild(folder); @@ -1868,7 +1868,8 @@ namespace Emby.Server.Implementations.Library return image.Path != null && !image.IsLocalFile; } - public void UpdateImages(BaseItem item, bool forceUpdate = false) + /// + public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false) { if (item == null) { @@ -1891,7 +1892,7 @@ namespace Emby.Server.Implementations.Library try { var index = item.GetImageIndex(img); - image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult(); + image = await ConvertImageToLocal(item, img, index).ConfigureAwait(false); } catch (ArgumentException) { @@ -1913,7 +1914,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path); + _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path); image.Width = 0; image.Height = 0; continue; @@ -1943,10 +1944,8 @@ namespace Emby.Server.Implementations.Library RegisterItem(item); } - /// - /// Updates the item. - /// - public void UpdateItems(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) + /// + public async Task UpdateItemsAsync(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { foreach (var item in items) { @@ -1957,7 +1956,7 @@ namespace Emby.Server.Implementations.Library item.DateLastSaved = DateTime.UtcNow; - UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate); + await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false); } _itemRepository.SaveItems(items, cancellationToken); @@ -1991,17 +1990,9 @@ namespace Emby.Server.Implementations.Library } } - /// - /// Updates the item. - /// - /// The item. - /// The parent item. - /// The update reason. - /// The cancellation token. - public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) - { - UpdateItems(new[] { item }, parent, updateReason, cancellationToken); - } + /// + public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) + => UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken); /// /// Reports the item removed. @@ -2233,7 +2224,7 @@ namespace Emby.Server.Implementations.Library if (refresh) { - item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); + item.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult(); ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal); } @@ -2420,7 +2411,7 @@ namespace Emby.Server.Implementations.Library if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) { item.ViewType = viewType; - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult(); } var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; @@ -2902,7 +2893,7 @@ namespace Emby.Server.Implementations.Library await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); return item.GetImageInfo(image.Type, imageIndex); } @@ -2920,7 +2911,7 @@ namespace Emby.Server.Implementations.Library // Remove this image to prevent it from retrying over and over item.RemoveImage(image); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); throw new InvalidOperationException(); } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 90cbd85a55..5ed6baeb9b 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv } } - private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken) + private async Task GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken) { var parentFolderId = parentFolder.Id; var isNew = false; @@ -512,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv } else if (forceUpdate) { - _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken); + await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } return item; @@ -1129,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv try { - var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken); + var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false); list.Add(item); } @@ -1146,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv double percent = numComplete; percent /= allChannelsList.Count; - progress.Report(5 * percent + 10); + progress.Report((5 * percent) + 10); } progress.Report(15); @@ -1221,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv if (updatedPrograms.Count > 0) { - _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken); + await _libraryManager.UpdateItemsAsync( + updatedPrograms, + currentChannel, + ItemUpdateType.MetadataImport, + cancellationToken).ConfigureAwait(false); } currentChannel.IsMovie = isMovie; @@ -1234,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv currentChannel.AddTag("Kids"); } - currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); + await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); await currentChannel.RefreshMetadata( new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 38ceadedbb..d35223b0a6 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -184,17 +184,17 @@ namespace Emby.Server.Implementations.Playlists return Playlist.GetPlaylistItems(playlistMediaType, items, user, options); } - public void AddToPlaylist(string playlistId, ICollection itemIds, Guid userId) + public Task AddToPlaylistAsync(string playlistId, ICollection itemIds, Guid userId) { var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId); - AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false) + return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false) { EnableImages = true }); } - private void AddToPlaylistInternal(string playlistId, ICollection newItemIds, User user, DtoOptions options) + private async Task AddToPlaylistInternal(string playlistId, ICollection newItemIds, User user, DtoOptions options) { // Retrieve the existing playlist var playlist = _libraryManager.GetItemById(playlistId) as Playlist @@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.Playlists // Update the playlist in the repository playlist.LinkedChildren = newLinkedChildren; - playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); // Update the playlist on disk if (playlist.IsFile) @@ -256,7 +256,7 @@ namespace Emby.Server.Implementations.Playlists RefreshPriority.High); } - public void RemoveFromPlaylist(string playlistId, IEnumerable entryIds) + public async Task RemoveFromPlaylistAsync(string playlistId, IEnumerable entryIds) { if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist)) { @@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Playlists .Select(i => i.Item1) .ToArray(); - playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); if (playlist.IsFile) { @@ -289,7 +289,7 @@ namespace Emby.Server.Implementations.Playlists RefreshPriority.High); } - public void MoveItem(string playlistId, string entryId, int newIndex) + public async Task MoveItemAsync(string playlistId, string entryId, int newIndex) { if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist)) { @@ -322,7 +322,7 @@ namespace Emby.Server.Implementations.Playlists playlist.LinkedChildren = newList.ToArray(); - playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); if (playlist.IsFile) { diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index 53821a1885..c5910d6e81 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -51,7 +52,7 @@ namespace Jellyfin.Api.Controllers /// A with information about the new collection. [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult CreateCollection( + public async Task> CreateCollection( [FromQuery] string? name, [FromQuery] string? ids, [FromQuery] Guid? parentId, @@ -59,14 +60,14 @@ namespace Jellyfin.Api.Controllers { var userId = _authContext.GetAuthorizationInfo(Request).UserId; - var item = _collectionManager.CreateCollection(new CollectionCreationOptions + var item = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions { IsLocked = isLocked, Name = name, ParentId = parentId, ItemIdList = RequestHelpers.Split(ids, ',', true), UserIds = new[] { userId } - }); + }).ConfigureAwait(false); var dtoOptions = new DtoOptions().AddClientFields(Request); @@ -87,9 +88,9 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) { - _collectionManager.AddToCollection(collectionId, RequestHelpers.Split(itemIds, ',', true)); + await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true); return NoContent(); } @@ -102,9 +103,9 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpDelete("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) { - _collectionManager.RemoveFromCollection(collectionId, RequestHelpers.Split(itemIds, ',', true)); + await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false); return NoContent(); } } diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 75734f0af0..ca9c2fa460 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -174,7 +174,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteItemImage( + public async Task DeleteItemImage( [FromRoute] Guid itemId, [FromRoute] ImageType imageType, [FromRoute] int? imageIndex = null) @@ -185,7 +185,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - item.DeleteImage(imageType, imageIndex ?? 0); + await item.DeleteImageAsync(imageType, imageIndex ?? 0).ConfigureAwait(false); return NoContent(); } @@ -218,7 +218,7 @@ namespace Jellyfin.Api.Controllers // Handle image/png; charset=utf-8 var mimeType = Request.ContentType.Split(';').FirstOrDefault(); await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); return NoContent(); } @@ -237,7 +237,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItemImageIndex( + public async Task UpdateItemImageIndex( [FromRoute] Guid itemId, [FromRoute] ImageType imageType, [FromRoute] int imageIndex, @@ -249,7 +249,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - item.SwapImages(imageType, imageIndex, newIndex); + await item.SwapImagesAsync(imageType, imageIndex, newIndex).ConfigureAwait(false); return NoContent(); } @@ -264,7 +264,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetItemImageInfos([FromRoute] Guid itemId) + public async Task>> GetItemImageInfos([FromRoute] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -281,7 +281,7 @@ namespace Jellyfin.Api.Controllers return list; } - _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct + await _libraryManager.UpdateImagesAsync(item).ConfigureAwait(false); // this makes sure dimensions and hashes are correct foreach (var image in itemImages) { diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 4b40c6ada9..ec52f49964 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Jellyfin.Api.Constants; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request) + public async Task UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers item.OnMetadataChanged(); - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); if (isLockedChanged && item.IsFolder) { @@ -110,7 +111,7 @@ namespace Jellyfin.Api.Controllers foreach (var child in folder.GetRecursiveChildren()) { child.IsLocked = newLockData; - child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await child.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index 12c87d7c36..d69228c33d 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -83,12 +83,12 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult AddToPlaylist( + public async Task AddToPlaylist( [FromRoute] string? playlistId, [FromQuery] string? ids, [FromQuery] Guid? userId) { - _playlistManager.AddToPlaylist(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty); + await _playlistManager.AddToPlaylistAsync(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty).ConfigureAwait(false); return NoContent(); } @@ -102,12 +102,12 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult MoveItem( + public async Task MoveItem( [FromRoute] string? playlistId, [FromRoute] string? itemId, [FromRoute] int newIndex) { - _playlistManager.MoveItem(playlistId, itemId, newIndex); + await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false); return NoContent(); } @@ -120,9 +120,9 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds) + public async Task RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds) { - _playlistManager.RemoveFromPlaylist(playlistId, RequestHelpers.Split(entryIds, ',', true)); + await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index a203c50b96..30a4f73fc0 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -221,7 +221,7 @@ namespace Jellyfin.Api.Controllers await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None) .ConfigureAwait(false); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 14d3f24607..f42810c945 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteAlternateSources([FromRoute] Guid itemId) + public async Task DeleteAlternateSources([FromRoute] Guid itemId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -180,12 +180,12 @@ namespace Jellyfin.Api.Controllers link.SetPrimaryVersionId(null); link.LinkedAlternateVersions = Array.Empty(); - link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await link.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } video.LinkedAlternateVersions = Array.Empty(); video.SetPrimaryVersionId(null); - video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await video.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); return NoContent(); } @@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public ActionResult MergeVersions([FromQuery, Required] string? itemIds) + public async Task MergeVersions([FromQuery, Required] string? itemIds) { var items = RequestHelpers.Split(itemIds, ',', true) .Select(i => _libraryManager.GetItemById(i)) @@ -239,7 +239,7 @@ namespace Jellyfin.Api.Controllers { item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture)); - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); list.Add(new LinkedChild { @@ -258,12 +258,12 @@ namespace Jellyfin.Api.Controllers if (item.LinkedAlternateVersions.Length > 0) { item.LinkedAlternateVersions = Array.Empty(); - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } } primaryVersion.LinkedAlternateVersions = list.ToArray(); - primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await primaryVersion.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); return NoContent(); } diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index 701423c0f3..3861ae634a 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -27,24 +28,23 @@ namespace MediaBrowser.Controller.Collections /// Creates the collection. /// /// The options. - BoxSet CreateCollection(CollectionCreationOptions options); + Task CreateCollectionAsync(CollectionCreationOptions options); /// /// Adds to collection. /// /// The collection identifier. /// The item ids. - void AddToCollection(Guid collectionId, IEnumerable itemIds); + /// representing the asynchronous operation. + Task AddToCollectionAsync(Guid collectionId, IEnumerable itemIds); /// /// Removes from collection. /// /// The collection identifier. /// The item ids. - void RemoveFromCollection(Guid collectionId, IEnumerable itemIds); - - void AddToCollection(Guid collectionId, IEnumerable itemIds); - void RemoveFromCollection(Guid collectionId, IEnumerable itemIds); + /// A representing the asynchronous operation. + Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable itemIds); /// /// Collapses the items within box sets. diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f34309c400..9e595ddc3e 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.Entities new List(); var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false); - LibraryManager.UpdateImages(this); // ensure all image properties in DB are fresh + await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh if (ownedItemsChanged) { @@ -2279,7 +2279,7 @@ namespace MediaBrowser.Controller.Entities /// /// The type. /// The index. - public void DeleteImage(ImageType type, int index) + public async Task DeleteImageAsync(ImageType type, int index) { var info = GetImageInfo(type, index); @@ -2297,7 +2297,7 @@ namespace MediaBrowser.Controller.Entities FileSystem.DeleteFile(info.Path); } - UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); } public void RemoveImage(ItemImageInfo image) @@ -2310,10 +2310,8 @@ namespace MediaBrowser.Controller.Entities ImageInfos = ImageInfos.Except(deletedImages).ToArray(); } - public virtual void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken) - { - LibraryManager.UpdateItem(this, GetParent(), updateReason, cancellationToken); - } + public virtual Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken) + => LibraryManager.UpdateItemAsync(this, GetParent(), updateReason, cancellationToken); /// /// Validates that images within the item are still on the filesystem. @@ -2558,7 +2556,7 @@ namespace MediaBrowser.Controller.Entities return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter; } - public void SwapImages(ImageType type, int index1, int index2) + public Task SwapImagesAsync(ImageType type, int index1, int index2) { if (!AllowsMultipleImages(type)) { @@ -2571,13 +2569,13 @@ namespace MediaBrowser.Controller.Entities if (info1 == null || info2 == null) { // Nothing to do - return; + return Task.CompletedTask; } if (!info1.IsLocalFile || !info2.IsLocalFile) { // TODO: Not supported yet - return; + return Task.CompletedTask; } var path1 = info1.Path; @@ -2594,7 +2592,7 @@ namespace MediaBrowser.Controller.Entities info2.Width = 0; info2.Height = 0; - UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + return UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None); } public virtual bool IsPlayed(User user) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 6441340f97..11542c1cad 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -350,12 +350,12 @@ namespace MediaBrowser.Controller.Entities if (currentChild.UpdateFromResolvedItem(child) > ItemUpdateType.None) { - currentChild.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); + await currentChild.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } else { // metadata is up-to-date; make sure DB has correct images dimensions and hash - LibraryManager.UpdateImages(currentChild); + await LibraryManager.UpdateImagesAsync(currentChild).ConfigureAwait(false); } continue; diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index b7d7e8e1a3..eeff78e103 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -495,9 +495,10 @@ namespace MediaBrowser.Controller.Entities } } - public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken) + /// + public override async Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken) { - base.UpdateToRepository(updateReason, cancellationToken); + await base.UpdateToRepositoryAsync(updateReason, cancellationToken).ConfigureAwait(false); var localAlternates = GetLocalAlternateVersionIds() .Select(i => LibraryManager.GetItemById(i)) @@ -514,7 +515,7 @@ namespace MediaBrowser.Controller.Entities item.Genres = Genres; item.ProviderIds = ProviderIds; - item.UpdateToRepository(ItemUpdateType.MetadataDownload, cancellationToken); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false); } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 9abcf2b626..d53b1fc8da 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -72,6 +72,7 @@ namespace MediaBrowser.Controller.Library /// The name. /// Task{Artist}. MusicArtist GetArtist(string name); + MusicArtist GetArtist(string name, DtoOptions options); /// /// Gets a Studio. @@ -124,7 +125,7 @@ namespace MediaBrowser.Controller.Library /// void QueueLibraryScan(); - void UpdateImages(BaseItem item, bool forceUpdate = false); + Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false); /// /// Gets the default view. @@ -179,6 +180,7 @@ namespace MediaBrowser.Controller.Library /// The sort order. /// IEnumerable{BaseItem}. IEnumerable Sort(IEnumerable items, User user, IEnumerable sortBy, SortOrder sortOrder); + IEnumerable Sort(IEnumerable items, User user, IEnumerable> orderBy); /// @@ -200,9 +202,16 @@ namespace MediaBrowser.Controller.Library /// /// Updates the item. /// - void UpdateItems(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); + Task UpdateItemsAsync(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); - void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); + /// + /// Updates the item. + /// + /// The item. + /// The parent item. + /// The update reason. + /// The cancellation token. + Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); /// /// Retrieves the item. @@ -317,7 +326,8 @@ namespace MediaBrowser.Controller.Library /// The name. /// Type of the view. /// Name of the sort. - UserView GetNamedView(string name, + UserView GetNamedView( + string name, string viewType, string sortName); @@ -329,7 +339,8 @@ namespace MediaBrowser.Controller.Library /// Type of the view. /// Name of the sort. /// The unique identifier. - UserView GetNamedView(string name, + UserView GetNamedView( + string name, Guid parentId, string viewType, string sortName, @@ -341,7 +352,8 @@ namespace MediaBrowser.Controller.Library /// The parent. /// Type of the view. /// Name of the sort. - UserView GetShadowView(BaseItem parent, + UserView GetShadowView( + BaseItem parent, string viewType, string sortName); @@ -393,7 +405,9 @@ namespace MediaBrowser.Controller.Library /// The file system children. /// The directory service. /// IEnumerable<Trailer>. - IEnumerable public class PlaybackProgressEventArgs : EventArgs { + public PlaybackProgressEventArgs() + { + Users = new List(); + } + public List Users { get; set; } public long? PlaybackPositionTicks { get; set; } @@ -35,10 +42,5 @@ namespace MediaBrowser.Controller.Library public string PlaySessionId { get; set; } public SessionInfo Session { get; set; } - - public PlaybackProgressEventArgs() - { - Users = new List(); - } } } diff --git a/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs index 12add25732..f0d77ba2d4 100644 --- a/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs +++ b/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Library { public class PlaybackStopEventArgs : PlaybackProgressEventArgs diff --git a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs index fa01927843..cd91097535 100644 --- a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs +++ b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs index 67d0df4fdc..d7afd21184 100644 --- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Model.LiveTv; namespace MediaBrowser.Controller.LiveTv diff --git a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs index 2ea0a748e1..038ff2eaeb 100644 --- a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs +++ b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index f619b011bf..0794422153 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading; @@ -105,6 +107,7 @@ namespace MediaBrowser.Controller.LiveTv /// /// The identifier. /// The media source identifier. + /// The current live streams. /// The cancellation token. /// Task{StreamResponseInfo}. Task> GetChannelStream(string id, string mediaSourceId, List currentLiveStreams, CancellationToken cancellationToken); diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index b71a766485..3ca1d165ef 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 3679e4f78f..ff92bf856c 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -14,28 +16,37 @@ namespace MediaBrowser.Controller.LiveTv /// /// The name. string Name { get; } + /// /// Gets the type. /// /// The type. string Type { get; } + + bool IsSupported { get; } + /// /// Gets the channels. /// /// Task<IEnumerable<ChannelInfo>>. Task> GetChannels(bool enableCache, CancellationToken cancellationToken); + /// /// Gets the tuner infos. /// /// The cancellation token. /// Task<List<LiveTvTunerInfo>>. Task> GetTunerInfos(CancellationToken cancellationToken); + /// /// Gets the channel stream. /// /// The channel identifier. /// The stream identifier. + /// The current live streams. + /// The cancellation token to cancel operation. Task GetChannelStream(string channelId, string streamId, List currentLiveStreams, CancellationToken cancellationToken); + /// /// Gets the channel stream media sources. /// @@ -45,10 +56,7 @@ namespace MediaBrowser.Controller.LiveTv Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken); - bool IsSupported - { - get; - } + } public interface IConfigurableTunerHost diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index aa7c12dd14..ec933caf34 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs b/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs index 0e09d1aeba..881c42c738 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.LiveTv diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index e1de01ff02..43af495dd6 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs index 67b2f0eb1c..02178297b1 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Model.LiveTv; @@ -5,6 +7,12 @@ namespace MediaBrowser.Controller.LiveTv { public class LiveTvServiceStatusInfo { + public LiveTvServiceStatusInfo() + { + Tuners = new List(); + IsVisible = true; + } + /// /// Gets or sets the status. /// @@ -39,11 +47,5 @@ namespace MediaBrowser.Controller.LiveTv /// /// true if this instance is visible; otherwise, false. public bool IsVisible { get; set; } - - public LiveTvServiceStatusInfo() - { - Tuners = new List(); - IsVisible = true; - } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs index 2857f73f6d..739978e7ce 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Model.LiveTv; @@ -5,6 +7,11 @@ namespace MediaBrowser.Controller.LiveTv { public class LiveTvTunerInfo { + public LiveTvTunerInfo() + { + Clients = new List(); + } + /// /// Gets or sets the type of the source. /// @@ -64,10 +71,5 @@ namespace MediaBrowser.Controller.LiveTv /// /// true if this instance can reset; otherwise, false. public bool CanReset { get; set; } - - public LiveTvTunerInfo() - { - Clients = new List(); - } } } diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs index d06a15323a..bdcffd5cae 100644 --- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using MediaBrowser.Model.LiveTv; diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs index b9e0218ab2..303882b7ef 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using MediaBrowser.Model.LiveTv; diff --git a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs index 99460a6862..847c0ea8c0 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Model.LiveTv; diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs index 6e7acaae39..1343ecd982 100644 --- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using MediaBrowser.Model.LiveTv; diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index df98bb6af8..bcef4666d2 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; diff --git a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs index df3f55c26c..2759b314f5 100644 --- a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs +++ b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.LiveTv { public class TunerChannelMapping diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 67f17f7a52..9692cf921d 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -30,6 +30,7 @@ netstandard2.1 false true + true diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 7b09f489e9..550916f823 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index b971b7c4bf..68bc502a0f 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index 8f6fcb9ab1..4cbb63e46c 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; diff --git a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs index 7c7e84de64..fbc8275341 100644 --- a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs +++ b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index f60e702393..17d6dc5d27 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 174e74f348..6ebf7f159b 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; using System.Threading; using System.Threading.Tasks; @@ -12,7 +14,8 @@ namespace MediaBrowser.Controller.MediaEncoding /// Gets the subtitles. /// /// Task{Stream}. - Task GetSubtitles(BaseItem item, + Task GetSubtitles( + BaseItem item, string mediaSourceId, int subtitleStreamIndex, string outputFormat, @@ -25,6 +28,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// Gets the subtitle language encoding parameter. /// /// The path. + /// The language. /// The protocol. /// The cancellation token. /// System.String. diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs index 361dd79dcc..e7b4c8c15c 100644 --- a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.MediaEncoding { public class ImageEncodingOptions diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index c9f64c7075..ac520c5c44 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Globalization; using System.IO; diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 6c9bbb043e..ce53c23ad2 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs index 39a47792ae..59729de49c 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs index 87a7f7e10b..1366fd42e8 100644 --- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs +++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; @@ -53,7 +55,7 @@ namespace MediaBrowser.Controller.Net } public bool IgnoreLegacyAuth { get; set; } - + public bool AllowLocalOnly { get; set; } } @@ -68,7 +70,7 @@ namespace MediaBrowser.Controller.Net bool AllowLocalOnly { get; } string[] GetRoles(); - + bool IgnoreLegacyAuth { get; } } } diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs index 4361e253b6..735c46ef86 100644 --- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs +++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using Jellyfin.Data.Entities; diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index a54f6d57b5..916dea58be 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs index 609bd5f596..8293a87142 100644 --- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; @@ -21,7 +23,9 @@ namespace MediaBrowser.Controller.Net object GetResult(string content, string contentType, IDictionary responseHeaders = null); object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary responseHeaders = null); + object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary responseHeaders = null); + object GetResult(IRequest requestContext, string content, string contentType, IDictionary responseHeaders = null); object GetRedirectResult(string url); diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs index 421ac3fe24..5da748f41d 100644 --- a/MediaBrowser.Controller/Net/ISessionContext.cs +++ b/MediaBrowser.Controller/Net/ISessionContext.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using Jellyfin.Data.Entities; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Services; @@ -7,9 +9,11 @@ namespace MediaBrowser.Controller.Net public interface ISessionContext { SessionInfo GetSession(object requestContext); + User GetUser(object requestContext); SessionInfo GetSession(IRequest requestContext); + User GetUser(IRequest requestContext); } } diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index 3ef8e5f6d4..e87f3bca68 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + #nullable enable using System; diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs index f0d0b45a0a..c6347133a8 100644 --- a/MediaBrowser.Controller/Net/SecurityException.cs +++ b/MediaBrowser.Controller/Net/SecurityException.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; namespace MediaBrowser.Controller.Net diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs index 85772e0368..c1e9bc8453 100644 --- a/MediaBrowser.Controller/Net/StaticResultOptions.cs +++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; diff --git a/MediaBrowser.Controller/Notifications/INotificationManager.cs b/MediaBrowser.Controller/Notifications/INotificationManager.cs index 44defbe0b2..08d9bc12a2 100644 --- a/MediaBrowser.Controller/Notifications/INotificationManager.cs +++ b/MediaBrowser.Controller/Notifications/INotificationManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs index ab5eb13cd4..fa947220ad 100644 --- a/MediaBrowser.Controller/Notifications/INotificationService.cs +++ b/MediaBrowser.Controller/Notifications/INotificationService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; diff --git a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs index 9f1d2841d7..52a3e120b5 100644 --- a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs +++ b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Model.Notifications; diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs index a1029589b8..d768abfe73 100644 --- a/MediaBrowser.Controller/Notifications/UserNotification.cs +++ b/MediaBrowser.Controller/Notifications/UserNotification.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using Jellyfin.Data.Entities; using MediaBrowser.Model.Notifications; diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 0ae1b8bbfa..ebc37bd1f3 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading; @@ -156,15 +158,23 @@ namespace MediaBrowser.Controller.Persistence int GetCount(InternalItemsQuery query); QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query); List GetMusicGenreNames(); + List GetStudioNames(); + List GetGenreNames(); + List GetAllArtistNames(); } } diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index ba7c9fd509..81ba513cef 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -24,9 +24,15 @@ namespace MediaBrowser.Controller.Persistence /// /// The user id. /// The key. - /// Task{UserItemData}. + /// The user data. UserItemData GetUserData(long userId, string key); + /// + /// Gets the user data. + /// + /// The user id. + /// The keys. + /// The user data. UserItemData GetUserData(long userId, List keys); /// diff --git a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs index e3b2d46650..e07e96f73e 100644 --- a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs +++ b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.Persistence diff --git a/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs b/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs index 7dc563b3ad..f9295c8fd3 100644 --- a/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs +++ b/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs index a3e7d4a677..fbf2c52131 100644 --- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs +++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 0fd63770f4..216dd27098 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs b/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs index 5deb165f62..bf15fe0407 100644 --- a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs +++ b/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; using System.Reflection; diff --git a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs b/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs index 077f5ab63e..93eab42cc7 100644 --- a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs +++ b/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.Controller.Plugins /// The plugin configuration. /// PluginConfiguration, + /// /// The none. /// diff --git a/MediaBrowser.Controller/Providers/AlbumInfo.cs b/MediaBrowser.Controller/Providers/AlbumInfo.cs index dbda4843f9..276bcf1252 100644 --- a/MediaBrowser.Controller/Providers/AlbumInfo.cs +++ b/MediaBrowser.Controller/Providers/AlbumInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Providers/ArtistInfo.cs b/MediaBrowser.Controller/Providers/ArtistInfo.cs index 08bf3982b1..adf885baa6 100644 --- a/MediaBrowser.Controller/Providers/ArtistInfo.cs +++ b/MediaBrowser.Controller/Providers/ArtistInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; namespace MediaBrowser.Controller.Providers diff --git a/MediaBrowser.Controller/Providers/BookInfo.cs b/MediaBrowser.Controller/Providers/BookInfo.cs index 03a6737c5f..cce0a25fcb 100644 --- a/MediaBrowser.Controller/Providers/BookInfo.cs +++ b/MediaBrowser.Controller/Providers/BookInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public class BookInfo : ItemLookupInfo diff --git a/MediaBrowser.Controller/Providers/BoxSetInfo.cs b/MediaBrowser.Controller/Providers/BoxSetInfo.cs index d23f2b9bf3..f43ea67178 100644 --- a/MediaBrowser.Controller/Providers/BoxSetInfo.cs +++ b/MediaBrowser.Controller/Providers/BoxSetInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public class BoxSetInfo : ItemLookupInfo diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index b7640c2050..f77455485a 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; diff --git a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs index 7c1371702e..006174be8c 100644 --- a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs +++ b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.IO; using MediaBrowser.Model.Drawing; diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs index 55c41ff82c..a4c8dab7ea 100644 --- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs +++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs index 6b4c9feb51..32a9cbef2e 100644 --- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs index 949a17740b..f06481c7a9 100644 --- a/MediaBrowser.Controller/Providers/IDirectoryService.cs +++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Model.IO; diff --git a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs b/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs index dec327d665..ab66462fa6 100644 --- a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs +++ b/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs b/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs index 68acb39105..a0e20e312e 100644 --- a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs +++ b/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Providers diff --git a/MediaBrowser.Controller/Providers/IHasLookupInfo.cs b/MediaBrowser.Controller/Providers/IHasLookupInfo.cs index 4c0c384423..42cb523713 100644 --- a/MediaBrowser.Controller/Providers/IHasLookupInfo.cs +++ b/MediaBrowser.Controller/Providers/IHasLookupInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public interface IHasLookupInfo diff --git a/MediaBrowser.Controller/Providers/IHasOrder.cs b/MediaBrowser.Controller/Providers/IHasOrder.cs index a3db612256..9fde0e6958 100644 --- a/MediaBrowser.Controller/Providers/IHasOrder.cs +++ b/MediaBrowser.Controller/Providers/IHasOrder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public interface IHasOrder diff --git a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs index 463c81376d..c129eddb3a 100644 --- a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs +++ b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs index 44fb1b394f..e771c881df 100644 --- a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Providers/IMetadataProvider.cs b/MediaBrowser.Controller/Providers/IMetadataProvider.cs index 62b16dadd7..1a87e0625d 100644 --- a/MediaBrowser.Controller/Providers/IMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IMetadataProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Providers diff --git a/MediaBrowser.Controller/Providers/IMetadataService.cs b/MediaBrowser.Controller/Providers/IMetadataService.cs index 21204e6d31..5f3d4274ef 100644 --- a/MediaBrowser.Controller/Providers/IMetadataService.cs +++ b/MediaBrowser.Controller/Providers/IMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs b/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs index 28da27ae77..6d98af33e4 100644 --- a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs +++ b/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public interface IPreRefreshProvider : ICustomMetadataProvider diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index c77349d010..e470d77b60 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs index c143b15cdb..f146decb60 100644 --- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs index 17ad9e4a3a..9592baa7c1 100644 --- a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs index 3f8c409f5c..9fc379f045 100644 --- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Linq; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs index d61153dfa6..b50def043f 100644 --- a/MediaBrowser.Controller/Providers/ItemInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs index 4707b0c7fc..49974c2a37 100644 --- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Controller/Providers/LocalImageInfo.cs b/MediaBrowser.Controller/Providers/LocalImageInfo.cs index 1842810255..41801862fd 100644 --- a/MediaBrowser.Controller/Providers/LocalImageInfo.cs +++ b/MediaBrowser.Controller/Providers/LocalImageInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs b/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs index 6d49b55104..920e3da5b0 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public enum MetadataRefreshMode diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index 0a473b80ca..b92b837012 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Linq; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index 270ea24449..1c695cafa0 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Controller/Providers/MovieInfo.cs b/MediaBrowser.Controller/Providers/MovieInfo.cs index 5b2c3ed03b..20e6b697ad 100644 --- a/MediaBrowser.Controller/Providers/MovieInfo.cs +++ b/MediaBrowser.Controller/Providers/MovieInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public class MovieInfo : ItemLookupInfo diff --git a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs index 9835351fc8..0b927f6eb0 100644 --- a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs +++ b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; namespace MediaBrowser.Controller.Providers diff --git a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs b/MediaBrowser.Controller/Providers/PersonLookupInfo.cs index a6218c039a..11cb71f902 100644 --- a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs +++ b/MediaBrowser.Controller/Providers/PersonLookupInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public class PersonLookupInfo : ItemLookupInfo diff --git a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs index a2ac6c9ae0..9653bc1c4a 100644 --- a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs +++ b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.Providers diff --git a/MediaBrowser.Controller/Providers/SeasonInfo.cs b/MediaBrowser.Controller/Providers/SeasonInfo.cs index dd2ef9ad73..2a4c1f03c7 100644 --- a/MediaBrowser.Controller/Providers/SeasonInfo.cs +++ b/MediaBrowser.Controller/Providers/SeasonInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Providers/SeriesInfo.cs b/MediaBrowser.Controller/Providers/SeriesInfo.cs index 6c206e0314..976fa175ad 100644 --- a/MediaBrowser.Controller/Providers/SeriesInfo.cs +++ b/MediaBrowser.Controller/Providers/SeriesInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public class SeriesInfo : ItemLookupInfo diff --git a/MediaBrowser.Controller/Providers/SongInfo.cs b/MediaBrowser.Controller/Providers/SongInfo.cs index 50615b0bd3..58f76dca91 100644 --- a/MediaBrowser.Controller/Providers/SongInfo.cs +++ b/MediaBrowser.Controller/Providers/SongInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Providers/TrailerInfo.cs b/MediaBrowser.Controller/Providers/TrailerInfo.cs index 13f07562d4..630850f9db 100644 --- a/MediaBrowser.Controller/Providers/TrailerInfo.cs +++ b/MediaBrowser.Controller/Providers/TrailerInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace MediaBrowser.Controller.Providers { public class TrailerInfo : ItemLookupInfo diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs index a73937b3e3..b99c468435 100644 --- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs index 1911e5c1dd..ac73a5ea89 100644 --- a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs +++ b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs @@ -9,15 +9,22 @@ namespace MediaBrowser.Controller.Resolvers /// The first. /// First = 1, + /// /// The second. /// Second = 2, + /// /// The third. /// Third = 3, + + /// + /// The Fourth. + /// Fourth = 4, + /// /// The last. /// diff --git a/MediaBrowser.Controller/Security/AuthenticationInfo.cs b/MediaBrowser.Controller/Security/AuthenticationInfo.cs index 1d0b959b73..efac9273ec 100644 --- a/MediaBrowser.Controller/Security/AuthenticationInfo.cs +++ b/MediaBrowser.Controller/Security/AuthenticationInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.Security diff --git a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs index 2bd17eb263..c5f3da0b1b 100644 --- a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs +++ b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.Security diff --git a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs index 6a9625613d..883b74165c 100644 --- a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs +++ b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Model.Devices; using MediaBrowser.Model.Querying; @@ -29,6 +31,7 @@ namespace MediaBrowser.Controller.Security void Delete(AuthenticationInfo info); DeviceOptions GetDeviceOptions(string deviceId); + void UpdateDeviceOptions(string deviceId, DeviceOptions options); } } diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs index 685ca3bddc..cc321fd22e 100644 --- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs +++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.Session diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index 04450085bf..22d6e2a04e 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index e54f210506..1a1e200fac 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/Session/SessionEventArgs.cs b/MediaBrowser.Controller/Session/SessionEventArgs.cs index 08baaf6476..097e32eaec 100644 --- a/MediaBrowser.Controller/Session/SessionEventArgs.cs +++ b/MediaBrowser.Controller/Session/SessionEventArgs.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.Session diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 4b088998cd..054fd33d9d 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Linq; using System.Text.Json.Serialization; diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs index de7f72d1c2..70cb9eebe0 100644 --- a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs +++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + #nullable enable using System; @@ -127,7 +129,7 @@ namespace MediaBrowser.Controller.Sorting } /// - public int Compare(string x, string y) + public int Compare(string? x, string? y) { return CompareValues(x, y); } diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs index 2a68f46789..88467814cd 100644 --- a/MediaBrowser.Controller/Sorting/SortExtensions.cs +++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -7,6 +9,7 @@ namespace MediaBrowser.Controller.Sorting public static class SortExtensions { private static readonly AlphanumComparator _comparer = new AlphanumComparator(); + public static IEnumerable OrderByString(this IEnumerable list, Func getName) { return list.OrderBy(getName, _comparer); diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index 39538aacd7..f43d523a63 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs index 8ffd7c5305..a633262de9 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs index 5703aea17c..b1d74517ea 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs index ad6025927c..a86b057783 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; namespace MediaBrowser.Controller.Subtitles diff --git a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs index a202723b99..7d3c20e8f1 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using MediaBrowser.Controller.Providers; diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs index d6bac23bec..e7395b136d 100644 --- a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs +++ b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Sync; diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 8b2d5d7797..c97fd70442 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.IO; using System.Threading; diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs index 56f6f37299..950cc73e85 100644 --- a/MediaBrowser.Controller/Sync/ISyncProvider.cs +++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Model.Sync; diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs index 687a46d78f..a626738fb2 100644 --- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs +++ b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Model.MediaInfo; @@ -5,6 +7,11 @@ namespace MediaBrowser.Controller.Sync { public class SyncedFileInfo { + public SyncedFileInfo() + { + RequiredHttpHeaders = new Dictionary(); + } + /// /// Gets or sets the path. /// @@ -12,25 +19,23 @@ namespace MediaBrowser.Controller.Sync public string Path { get; set; } public string[] PathParts { get; set; } + /// /// Gets or sets the protocol. /// /// The protocol. public MediaProtocol Protocol { get; set; } + /// /// Gets or sets the required HTTP headers. /// /// The required HTTP headers. public Dictionary RequiredHttpHeaders { get; set; } + /// /// Gets or sets the identifier. /// /// The identifier. public string Id { get; set; } - - public SyncedFileInfo() - { - RequiredHttpHeaders = new Dictionary(); - } } } diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs index 45c5438061..60d17fe367 100644 --- a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs @@ -64,4 +64,4 @@ namespace MediaBrowser.Controller.SyncPlay /// The group info for the clients. GroupInfoView GetInfo(); } -} \ No newline at end of file +} diff --git a/MediaBrowser.Controller/TV/ITVSeriesManager.cs b/MediaBrowser.Controller/TV/ITVSeriesManager.cs index 09a0f6fea9..291dea04ef 100644 --- a/MediaBrowser.Controller/TV/ITVSeriesManager.cs +++ b/MediaBrowser.Controller/TV/ITVSeriesManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Querying; From 5ec1a979b2bb501edb8836c0170cd10e5dd2d292 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 22 Aug 2020 21:57:28 +0200 Subject: [PATCH 098/102] Remove extra newline --- MediaBrowser.Controller/Authentication/AuthenticationResult.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs index bdebd76ab3..4249a9a667 100644 --- a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs +++ b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 - using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; From 4243a17c8672d6fb0c541b2b5b2802d3deead68f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 22 Aug 2020 23:29:54 +0200 Subject: [PATCH 099/102] Enable TreatWarningsAsErrors for Emby.Data in Release --- Jellyfin.Data/DayOfWeekHelper.cs | 2 ++ Jellyfin.Data/Entities/ActivityLog.cs | 2 ++ Jellyfin.Data/Entities/Artwork.cs | 2 ++ Jellyfin.Data/Entities/Book.cs | 4 ++++ Jellyfin.Data/Entities/BookMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/Chapter.cs | 2 ++ Jellyfin.Data/Entities/Collection.cs | 2 ++ Jellyfin.Data/Entities/CollectionItem.cs | 2 ++ Jellyfin.Data/Entities/Company.cs | 2 ++ Jellyfin.Data/Entities/CompanyMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/CustomItem.cs | 4 ++++ Jellyfin.Data/Entities/CustomItemMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/Episode.cs | 4 ++++ Jellyfin.Data/Entities/EpisodeMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/Genre.cs | 2 ++ Jellyfin.Data/Entities/Group.cs | 2 ++ Jellyfin.Data/Entities/ImageInfo.cs | 4 +++- Jellyfin.Data/Entities/ItemDisplayPreferences.cs | 4 +++- Jellyfin.Data/Entities/Library.cs | 2 ++ Jellyfin.Data/Entities/LibraryItem.cs | 3 +++ Jellyfin.Data/Entities/LibraryRoot.cs | 2 ++ Jellyfin.Data/Entities/MediaFile.cs | 2 ++ Jellyfin.Data/Entities/MediaFileStream.cs | 2 ++ Jellyfin.Data/Entities/Metadata.cs | 4 ++++ Jellyfin.Data/Entities/MetadataProvider.cs | 2 ++ Jellyfin.Data/Entities/MetadataProviderId.cs | 2 ++ Jellyfin.Data/Entities/Movie.cs | 4 ++++ Jellyfin.Data/Entities/MovieMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/MusicAlbum.cs | 4 ++++ Jellyfin.Data/Entities/MusicAlbumMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/Permission.cs | 2 ++ Jellyfin.Data/Entities/Person.cs | 6 ++++++ Jellyfin.Data/Entities/PersonRole.cs | 2 ++ Jellyfin.Data/Entities/Photo.cs | 4 ++++ Jellyfin.Data/Entities/PhotoMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/ProviderMapping.cs | 2 ++ Jellyfin.Data/Entities/Rating.cs | 2 ++ Jellyfin.Data/Entities/RatingSource.cs | 2 ++ Jellyfin.Data/Entities/Release.cs | 2 ++ Jellyfin.Data/Entities/Season.cs | 4 ++++ Jellyfin.Data/Entities/SeasonMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/Series.cs | 4 ++++ Jellyfin.Data/Entities/SeriesMetadata.cs | 6 ++++++ Jellyfin.Data/Entities/Track.cs | 4 ++++ Jellyfin.Data/Entities/TrackMetadata.cs | 7 ++++++- Jellyfin.Data/Entities/User.cs | 2 ++ Jellyfin.Data/Enums/ArtKind.cs | 2 ++ Jellyfin.Data/Enums/IndexingKind.cs | 4 +++- Jellyfin.Data/Enums/MediaFileKind.cs | 2 ++ Jellyfin.Data/Enums/PersonRoleType.cs | 2 ++ Jellyfin.Data/Jellyfin.Data.csproj | 1 + 51 files changed, 169 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Data/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs index 32a41368db..4e75f4cfd1 100644 --- a/Jellyfin.Data/DayOfWeekHelper.cs +++ b/Jellyfin.Data/DayOfWeekHelper.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using Jellyfin.Data.Enums; diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index 522c206640..ac61b9e3ba 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Artwork.cs b/Jellyfin.Data/Entities/Artwork.cs index 6ed32eac33..4508f54882 100644 --- a/Jellyfin.Data/Entities/Artwork.cs +++ b/Jellyfin.Data/Entities/Artwork.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/Book.cs b/Jellyfin.Data/Entities/Book.cs index c4d12496e6..b6198ee01c 100644 --- a/Jellyfin.Data/Entities/Book.cs +++ b/Jellyfin.Data/Entities/Book.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -31,6 +33,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public Book(Guid urlid, DateTime dateadded) { this.UrlId = urlid; @@ -45,6 +48,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public static Book Create(Guid urlid, DateTime dateadded) { return new Book(urlid, dateadded); diff --git a/Jellyfin.Data/Entities/BookMetadata.cs b/Jellyfin.Data/Entities/BookMetadata.cs index df43090d3d..9734cf20ec 100644 --- a/Jellyfin.Data/Entities/BookMetadata.cs +++ b/Jellyfin.Data/Entities/BookMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -31,6 +33,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public BookMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0) { @@ -65,6 +69,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static BookMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0) { diff --git a/Jellyfin.Data/Entities/Chapter.cs b/Jellyfin.Data/Entities/Chapter.cs index 4575cdb4d7..52cdeef782 100644 --- a/Jellyfin.Data/Entities/Chapter.cs +++ b/Jellyfin.Data/Entities/Chapter.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Collection.cs b/Jellyfin.Data/Entities/Collection.cs index 01836d8937..0c317d71ed 100644 --- a/Jellyfin.Data/Entities/Collection.cs +++ b/Jellyfin.Data/Entities/Collection.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/CollectionItem.cs b/Jellyfin.Data/Entities/CollectionItem.cs index d879806ee6..fb589c2baf 100644 --- a/Jellyfin.Data/Entities/CollectionItem.cs +++ b/Jellyfin.Data/Entities/CollectionItem.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Company.cs b/Jellyfin.Data/Entities/Company.cs index e905a17daf..8bd48045d0 100644 --- a/Jellyfin.Data/Entities/Company.cs +++ b/Jellyfin.Data/Entities/Company.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/CompanyMetadata.cs b/Jellyfin.Data/Entities/CompanyMetadata.cs index e75349cf2a..48ea4bdc50 100644 --- a/Jellyfin.Data/Entities/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/CompanyMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; @@ -28,6 +30,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public CompanyMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0) { @@ -60,6 +64,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static CompanyMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0) { diff --git a/Jellyfin.Data/Entities/CustomItem.cs b/Jellyfin.Data/Entities/CustomItem.cs index 4463915914..8ea08488f4 100644 --- a/Jellyfin.Data/Entities/CustomItem.cs +++ b/Jellyfin.Data/Entities/CustomItem.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -31,6 +33,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public CustomItem(Guid urlid, DateTime dateadded) { this.UrlId = urlid; @@ -45,6 +48,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public static CustomItem Create(Guid urlid, DateTime dateadded) { return new CustomItem(urlid, dateadded); diff --git a/Jellyfin.Data/Entities/CustomItemMetadata.cs b/Jellyfin.Data/Entities/CustomItemMetadata.cs index 965ed731f9..9c89399e69 100644 --- a/Jellyfin.Data/Entities/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/CustomItemMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace Jellyfin.Data.Entities @@ -27,6 +29,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public CustomItemMetadata(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0) { @@ -59,6 +63,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static CustomItemMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0) { diff --git a/Jellyfin.Data/Entities/Episode.cs b/Jellyfin.Data/Entities/Episode.cs index 57fbf894bf..1c18944482 100644 --- a/Jellyfin.Data/Entities/Episode.cs +++ b/Jellyfin.Data/Entities/Episode.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -34,6 +36,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. /// public Episode(Guid urlid, DateTime dateadded, Season _season0) { @@ -59,6 +62,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. /// public static Episode Create(Guid urlid, DateTime dateadded, Season _season0) { diff --git a/Jellyfin.Data/Entities/EpisodeMetadata.cs b/Jellyfin.Data/Entities/EpisodeMetadata.cs index 9a21fd50f0..26ad7200b8 100644 --- a/Jellyfin.Data/Entities/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/EpisodeMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; @@ -28,6 +30,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public EpisodeMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0) { @@ -60,6 +64,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static EpisodeMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0) { diff --git a/Jellyfin.Data/Entities/Genre.cs b/Jellyfin.Data/Entities/Genre.cs index 24e6815d80..43a180f6b6 100644 --- a/Jellyfin.Data/Entities/Genre.cs +++ b/Jellyfin.Data/Entities/Genre.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index 47833378e8..a1ec6b1fa4 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index 64e36a791a..cf0895ad43 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -1,4 +1,6 @@ -using System; +#pragma warning disable CS1591 + +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs index 95c08e6c6c..023cdc7400 100644 --- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs @@ -1,4 +1,6 @@ -using System; +#pragma warning disable CS1591 + +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; diff --git a/Jellyfin.Data/Entities/Library.cs b/Jellyfin.Data/Entities/Library.cs index d935e43b14..23cc9bd7de 100644 --- a/Jellyfin.Data/Entities/Library.cs +++ b/Jellyfin.Data/Entities/Library.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/LibraryItem.cs b/Jellyfin.Data/Entities/LibraryItem.cs index f41753560c..00b2f9497f 100644 --- a/Jellyfin.Data/Entities/LibraryItem.cs +++ b/Jellyfin.Data/Entities/LibraryItem.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -20,6 +22,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. protected LibraryItem(Guid urlid, DateTime dateadded) { this.UrlId = urlid; diff --git a/Jellyfin.Data/Entities/LibraryRoot.cs b/Jellyfin.Data/Entities/LibraryRoot.cs index 9695ed638d..07e16fff45 100644 --- a/Jellyfin.Data/Entities/LibraryRoot.cs +++ b/Jellyfin.Data/Entities/LibraryRoot.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/MediaFile.cs b/Jellyfin.Data/Entities/MediaFile.cs index 7382cda95b..b69dbe2fa7 100644 --- a/Jellyfin.Data/Entities/MediaFile.cs +++ b/Jellyfin.Data/Entities/MediaFile.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/MediaFileStream.cs b/Jellyfin.Data/Entities/MediaFileStream.cs index 977fd54e19..1c59e663d6 100644 --- a/Jellyfin.Data/Entities/MediaFileStream.cs +++ b/Jellyfin.Data/Entities/MediaFileStream.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Metadata.cs b/Jellyfin.Data/Entities/Metadata.cs index a4ac6dc540..42525fa999 100644 --- a/Jellyfin.Data/Entities/Metadata.cs +++ b/Jellyfin.Data/Entities/Metadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -28,6 +30,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. protected Metadata(string title, string language, DateTime dateadded, DateTime datemodified) { if (string.IsNullOrEmpty(title)) diff --git a/Jellyfin.Data/Entities/MetadataProvider.cs b/Jellyfin.Data/Entities/MetadataProvider.cs index e93ea97d62..ebb2c1dbc1 100644 --- a/Jellyfin.Data/Entities/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/MetadataProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/MetadataProviderId.cs b/Jellyfin.Data/Entities/MetadataProviderId.cs index 68f139436a..ca3e16b1aa 100644 --- a/Jellyfin.Data/Entities/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/MetadataProviderId.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Movie.cs b/Jellyfin.Data/Entities/Movie.cs index 64326ca3a4..842d5b2b01 100644 --- a/Jellyfin.Data/Entities/Movie.cs +++ b/Jellyfin.Data/Entities/Movie.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -31,6 +33,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public Movie(Guid urlid, DateTime dateadded) { this.UrlId = urlid; @@ -45,6 +48,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public static Movie Create(Guid urlid, DateTime dateadded) { return new Movie(urlid, dateadded); diff --git a/Jellyfin.Data/Entities/MovieMetadata.cs b/Jellyfin.Data/Entities/MovieMetadata.cs index cbcb78e374..a6c82dda8f 100644 --- a/Jellyfin.Data/Entities/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/MovieMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -32,6 +34,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public MovieMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0) { @@ -66,6 +70,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static MovieMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0) { diff --git a/Jellyfin.Data/Entities/MusicAlbum.cs b/Jellyfin.Data/Entities/MusicAlbum.cs index 9afea1fb69..e03c3bfb0d 100644 --- a/Jellyfin.Data/Entities/MusicAlbum.cs +++ b/Jellyfin.Data/Entities/MusicAlbum.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -31,6 +33,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public MusicAlbum(Guid urlid, DateTime dateadded) { this.UrlId = urlid; @@ -45,6 +48,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public static MusicAlbum Create(Guid urlid, DateTime dateadded) { return new MusicAlbum(urlid, dateadded); diff --git a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs index bfcbebbe8a..01ad736ce2 100644 --- a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -32,6 +34,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public MusicAlbumMetadata(string title, string language, DateTime dateadded, DateTime datemodified, MusicAlbum _musicalbum0) { @@ -66,6 +70,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static MusicAlbumMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, MusicAlbum _musicalbum0) { diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index b675e911d9..af3270a880 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; diff --git a/Jellyfin.Data/Entities/Person.cs b/Jellyfin.Data/Entities/Person.cs index b6d91ea869..f0cfb73221 100644 --- a/Jellyfin.Data/Entities/Person.cs +++ b/Jellyfin.Data/Entities/Person.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -32,6 +34,8 @@ namespace Jellyfin.Data.Entities /// /// /// + /// The date the object was added. + /// The date the object was last modified. public Person(Guid urlid, string name, DateTime dateadded, DateTime datemodified) { this.UrlId = urlid; @@ -53,6 +57,8 @@ namespace Jellyfin.Data.Entities /// /// /// + /// The date the object was added. + /// The date the object was last modified. public static Person Create(Guid urlid, string name, DateTime dateadded, DateTime datemodified) { return new Person(urlid, name, dateadded, datemodified); diff --git a/Jellyfin.Data/Entities/PersonRole.cs b/Jellyfin.Data/Entities/PersonRole.cs index 2dd5f116fc..895a9f47ac 100644 --- a/Jellyfin.Data/Entities/PersonRole.cs +++ b/Jellyfin.Data/Entities/PersonRole.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/Photo.cs b/Jellyfin.Data/Entities/Photo.cs index 9da55fe430..7648bc2120 100644 --- a/Jellyfin.Data/Entities/Photo.cs +++ b/Jellyfin.Data/Entities/Photo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -31,6 +33,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public Photo(Guid urlid, DateTime dateadded) { this.UrlId = urlid; @@ -45,6 +48,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public static Photo Create(Guid urlid, DateTime dateadded) { return new Photo(urlid, dateadded); diff --git a/Jellyfin.Data/Entities/PhotoMetadata.cs b/Jellyfin.Data/Entities/PhotoMetadata.cs index b5aec7229e..3f06d3f2b5 100644 --- a/Jellyfin.Data/Entities/PhotoMetadata.cs +++ b/Jellyfin.Data/Entities/PhotoMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations.Schema; @@ -28,6 +30,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public PhotoMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Photo _photo0) { @@ -60,6 +64,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static PhotoMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Photo _photo0) { diff --git a/Jellyfin.Data/Entities/ProviderMapping.cs b/Jellyfin.Data/Entities/ProviderMapping.cs index c53e3bf408..44ebfba76d 100644 --- a/Jellyfin.Data/Entities/ProviderMapping.cs +++ b/Jellyfin.Data/Entities/ProviderMapping.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Rating.cs b/Jellyfin.Data/Entities/Rating.cs index 49a0d502d2..c57b0a0e84 100644 --- a/Jellyfin.Data/Entities/Rating.cs +++ b/Jellyfin.Data/Entities/Rating.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/RatingSource.cs b/Jellyfin.Data/Entities/RatingSource.cs index b62d8b4443..2ea8e3b311 100644 --- a/Jellyfin.Data/Entities/RatingSource.cs +++ b/Jellyfin.Data/Entities/RatingSource.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Release.cs b/Jellyfin.Data/Entities/Release.cs index 1e9faa5a1d..3e2cf22dba 100644 --- a/Jellyfin.Data/Entities/Release.cs +++ b/Jellyfin.Data/Entities/Release.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/Season.cs b/Jellyfin.Data/Entities/Season.cs index 4b1e785758..e5e7d03ab0 100644 --- a/Jellyfin.Data/Entities/Season.cs +++ b/Jellyfin.Data/Entities/Season.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -34,6 +36,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. /// public Season(Guid urlid, DateTime dateadded, Series _series0) { @@ -59,6 +62,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. /// public static Season Create(Guid urlid, DateTime dateadded, Series _series0) { diff --git a/Jellyfin.Data/Entities/SeasonMetadata.cs b/Jellyfin.Data/Entities/SeasonMetadata.cs index 10d19875e6..cce8cb125d 100644 --- a/Jellyfin.Data/Entities/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/SeasonMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -29,6 +31,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public SeasonMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Season _season0) { @@ -61,6 +65,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static SeasonMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Season _season0) { diff --git a/Jellyfin.Data/Entities/Series.cs b/Jellyfin.Data/Entities/Series.cs index bede14acfb..33c07ca618 100644 --- a/Jellyfin.Data/Entities/Series.cs +++ b/Jellyfin.Data/Entities/Series.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -23,6 +25,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public Series(Guid urlid, DateTime dateadded) { this.UrlId = urlid; @@ -37,6 +40,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. public static Series Create(Guid urlid, DateTime dateadded) { return new Series(urlid, dateadded); diff --git a/Jellyfin.Data/Entities/SeriesMetadata.cs b/Jellyfin.Data/Entities/SeriesMetadata.cs index 16eb59315e..22be2a59b4 100644 --- a/Jellyfin.Data/Entities/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/SeriesMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -32,6 +34,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public SeriesMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Series _series0) { @@ -66,6 +70,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static SeriesMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Series _series0) { diff --git a/Jellyfin.Data/Entities/Track.cs b/Jellyfin.Data/Entities/Track.cs index b7d7b5873b..d52dd725aa 100644 --- a/Jellyfin.Data/Entities/Track.cs +++ b/Jellyfin.Data/Entities/Track.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -34,6 +36,7 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. /// public Track(Guid urlid, DateTime dateadded, MusicAlbum _musicalbum0) { @@ -59,6 +62,7 @@ namespace Jellyfin.Data.Entities /// Static create function (for use in LINQ queries, etc.) /// /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// The date the object was added. /// public static Track Create(Guid urlid, DateTime dateadded, MusicAlbum _musicalbum0) { diff --git a/Jellyfin.Data/Entities/TrackMetadata.cs b/Jellyfin.Data/Entities/TrackMetadata.cs index 23e1219aaf..710908eb8b 100644 --- a/Jellyfin.Data/Entities/TrackMetadata.cs +++ b/Jellyfin.Data/Entities/TrackMetadata.cs @@ -1,5 +1,6 @@ +#pragma warning disable CS1591 + using System; -using System.ComponentModel.DataAnnotations.Schema; namespace Jellyfin.Data.Entities { @@ -28,6 +29,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public TrackMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Track _track0) { @@ -60,6 +63,8 @@ namespace Jellyfin.Data.Entities /// /// The title or name of the object. /// ISO-639-3 3-character language codes. + /// The date the object was added. + /// The date the object was last modified. /// public static TrackMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Track _track0) { diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 50810561f5..8c720d85be 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Enums/ArtKind.cs b/Jellyfin.Data/Enums/ArtKind.cs index 6b69d68b28..71b4db6f26 100644 --- a/Jellyfin.Data/Enums/ArtKind.cs +++ b/Jellyfin.Data/Enums/ArtKind.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace Jellyfin.Data.Enums { public enum ArtKind diff --git a/Jellyfin.Data/Enums/IndexingKind.cs b/Jellyfin.Data/Enums/IndexingKind.cs index 9badc6573b..fafe47e0c1 100644 --- a/Jellyfin.Data/Enums/IndexingKind.cs +++ b/Jellyfin.Data/Enums/IndexingKind.cs @@ -1,4 +1,6 @@ -namespace Jellyfin.Data.Enums +#pragma warning disable CS1591 + +namespace Jellyfin.Data.Enums { public enum IndexingKind { diff --git a/Jellyfin.Data/Enums/MediaFileKind.cs b/Jellyfin.Data/Enums/MediaFileKind.cs index 12f48c5589..b03591fb80 100644 --- a/Jellyfin.Data/Enums/MediaFileKind.cs +++ b/Jellyfin.Data/Enums/MediaFileKind.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace Jellyfin.Data.Enums { public enum MediaFileKind diff --git a/Jellyfin.Data/Enums/PersonRoleType.cs b/Jellyfin.Data/Enums/PersonRoleType.cs index 6e52f2c851..2d80eaa4ca 100644 --- a/Jellyfin.Data/Enums/PersonRoleType.cs +++ b/Jellyfin.Data/Enums/PersonRoleType.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace Jellyfin.Data.Enums { public enum PersonRoleType diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 8ce0f3848c..43b838cc10 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -4,6 +4,7 @@ netstandard2.0;netstandard2.1 false true + true From c4d8275fc1ad29823788043750e2f3f8e8b8244e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 23 Aug 2020 00:26:32 +0200 Subject: [PATCH 100/102] Fix duplicate --- MediaBrowser.Controller/Entities/Folder.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 7687cf12b6..11542c1cad 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1,7 +1,5 @@ #pragma warning disable CS1591 -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; From e88b7b8d0e08694a4cc939b8632cc077dc51c7ca Mon Sep 17 00:00:00 2001 From: Hilman Maulana Date: Sat, 22 Aug 2020 19:07:59 +0000 Subject: [PATCH 101/102] Translated using Weblate (Indonesian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/id/ --- Emby.Server.Implementations/Localization/Core/id.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index ccb72ff937..d2e27e3795 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -1,7 +1,7 @@ { "Albums": "Album", "AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi", - "AppDeviceValues": "Aplikasi: {0}, Alat: {1}", + "AppDeviceValues": "Aplikasi : {0}, Alat : {1}", "LabelRunningTimeValue": "Waktu berjalan: {0}", "MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}", "MessageApplicationUpdated": "Jellyfin Server sudah diperbarui", From 340f83c3f5e0f42e7b02be0e4402e2039f8fe734 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 23 Aug 2020 07:48:12 -0600 Subject: [PATCH 102/102] Ignore null json values --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 3 +++ Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 2 ++ MediaBrowser.Common/Json/JsonDefaults.cs | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 5bf740cfcc..48e2f5d4a1 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -90,6 +90,9 @@ namespace Emby.Server.Implementations.Data _typeMapper = new TypeMapper(); _jsonOptions = JsonDefaults.GetOptions(); + // GetItem throws NotSupportedException with this enabled, so hardcode false. + _jsonOptions.IgnoreNullValues = false; + DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); } diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 2e2bfea684..1f9fc7078b 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -168,6 +168,8 @@ namespace Jellyfin.Server.Extensions // From JsonDefaults options.JsonSerializerOptions.ReadCommentHandling = jsonOptions.ReadCommentHandling; options.JsonSerializerOptions.WriteIndented = jsonOptions.WriteIndented; + options.JsonSerializerOptions.IgnoreNullValues = jsonOptions.IgnoreNullValues; + options.JsonSerializerOptions.Converters.Clear(); foreach (var converter in jsonOptions.Converters) { diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 0a661934e5..891715b3da 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -24,7 +24,8 @@ namespace MediaBrowser.Common.Json var options = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Disallow, - WriteIndented = false + WriteIndented = false, + IgnoreNullValues = true }; options.Converters.Add(new JsonGuidConverter());