From 357429b8ea232619cdbdeda44e5f4b94c7a40f26 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 8 Jan 2021 14:48:08 +0100 Subject: [PATCH 001/671] Add .nfo ratings tag --- .../Parsers/BaseNfoParser.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index c287113c5..2a64bb243 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -624,6 +624,21 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } + case "ratings": + { + if (!reader.IsEmptyElement) + { + using var subtree = reader.ReadSubtree(); + FetchFromRatingsNode(subtree, item); + } + else + { + reader.Read(); + } + + break; + } + case "aired": case "formed": case "premiered": @@ -866,6 +881,90 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } + private void FetchFromRatingsNode(XmlReader reader, T item) + { + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "rating": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + + var ratingName = reader.GetAttribute("name"); + + using var subtree = reader.ReadSubtree(); + FetchFromRatingNode(subtree, item, ratingName); + + break; + } + + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); + } + } + } + + private void FetchFromRatingNode(XmlReader reader, T item, string? ratingName) + { + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "value": + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (float.TryParse(val.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) + { + // if ratingName contains tomato --> assume critic rating + if (ratingName != null && ratingName.Contains("tomato", StringComparison.InvariantCultureIgnoreCase)) + { + item.CriticRating = ratingValue; + } + else + { + item.CommunityRating = ratingValue; + } + } + } + + break; + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); + } + } + } + /// /// Gets the persons from XML node. /// From 620fbf0f89d33ab5d4d66373c7f9fe4580691f22 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Jan 2021 10:51:59 +0100 Subject: [PATCH 002/671] Remove CropWhitespace function --- Emby.Drawing/ImageProcessor.cs | 5 -- Jellyfin.Api/Controllers/ImageController.cs | 5 +- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 85 ++----------------- .../Drawing/ImageProcessingOptions.cs | 3 - 4 files changed, 6 insertions(+), 92 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 8a2301d2d..af6e5b339 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -181,11 +181,6 @@ namespace Emby.Drawing { if (!File.Exists(cacheFilePath)) { - if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath)) - { - options.CropWhiteSpace = false; - } - string resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat); if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase)) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index c606d327c..cd1296310 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1681,7 +1681,7 @@ namespace Jellyfin.Api.Controllers int? width, int? height, int? quality, - bool? cropWhitespace, + bool? cropWhitespace, // TODO: Remove bool? addPlayedIndicator, int? blur, string? backgroundColor, @@ -1756,7 +1756,6 @@ namespace Jellyfin.Api.Controllers backgroundColor, foregroundLayer, imageInfo, - cropWhitespace.Value, outputFormats, cacheDuration, responseHeaders, @@ -1855,7 +1854,6 @@ namespace Jellyfin.Api.Controllers string? backgroundColor, string? foregroundLayer, ItemImageInfo imageInfo, - bool cropWhitespace, IReadOnlyCollection supportedFormats, TimeSpan? cacheDuration, IDictionary headers, @@ -1868,7 +1866,6 @@ namespace Jellyfin.Api.Controllers var options = new ImageProcessingOptions { - CropWhiteSpace = cropWhitespace, Height = height, ImageIndex = index ?? 0, Image = imageInfo, diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index eab5777d5..a786c01a6 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -91,9 +91,6 @@ namespace Jellyfin.Drawing.Skia } } - private static bool IsTransparent(SKColor color) - => (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0; - /// /// Convert a to a . /// @@ -111,65 +108,6 @@ namespace Jellyfin.Drawing.Skia }; } - private static bool IsTransparentRow(SKBitmap bmp, int row) - { - for (var i = 0; i < bmp.Width; ++i) - { - if (!IsTransparent(bmp.GetPixel(i, row))) - { - return false; - } - } - - return true; - } - - private static bool IsTransparentColumn(SKBitmap bmp, int col) - { - for (var i = 0; i < bmp.Height; ++i) - { - if (!IsTransparent(bmp.GetPixel(col, i))) - { - return false; - } - } - - return true; - } - - private SKBitmap CropWhiteSpace(SKBitmap bitmap) - { - var topmost = 0; - while (topmost < bitmap.Height && IsTransparentRow(bitmap, topmost)) - { - topmost++; - } - - int bottommost = bitmap.Height; - while (bottommost >= 0 && IsTransparentRow(bitmap, bottommost - 1)) - { - bottommost--; - } - - var leftmost = 0; - while (leftmost < bitmap.Width && IsTransparentColumn(bitmap, leftmost)) - { - leftmost++; - } - - var rightmost = bitmap.Width; - while (rightmost >= 0 && IsTransparentColumn(bitmap, rightmost - 1)) - { - rightmost--; - } - - var newRect = SKRectI.Create(leftmost, topmost, rightmost - leftmost, bottommost - topmost); - - using var image = SKImage.FromBitmap(bitmap); - using var subset = image.Subset(newRect); - return SKBitmap.FromImage(subset); - } - /// /// The path is null. /// The path is not valid. @@ -312,22 +250,11 @@ namespace Jellyfin.Drawing.Skia return resultBitmap; } - private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) - { - if (cropWhitespace) - { - using var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin); - return bitmap == null ? null : CropWhiteSpace(bitmap); - } - - return Decode(path, forceAnalyzeBitmap, orientation, out origin); - } - - private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation) + private SKBitmap? GetBitmap(string path, bool autoOrient, ImageOrientation? orientation) { if (autoOrient) { - var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out var origin); + var bitmap = Decode(path, true, orientation, out var origin); if (bitmap != null && origin != SKEncodedOrigin.TopLeft) { @@ -340,7 +267,7 @@ namespace Jellyfin.Drawing.Skia return bitmap; } - return GetBitmap(path, cropWhitespace, false, orientation, out _); + return Decode(path, false, orientation, out _); } private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) @@ -466,7 +393,7 @@ namespace Jellyfin.Drawing.Skia var blur = options.Blur ?? 0; var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0); - using var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation); + using var bitmap = GetBitmap(inputPath, autoOrient, orientation); if (bitmap == null) { throw new InvalidDataException($"Skia unable to read image {inputPath}"); @@ -474,9 +401,7 @@ namespace Jellyfin.Drawing.Skia var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height); - if (!options.CropWhiteSpace - && options.HasDefaultOptions(inputPath, originalImageSize) - && !autoOrient) + if (options.HasDefaultOptions(inputPath, originalImageSize) && !autoOrient) { // Just spit out the original file if all the options are default return inputPath; diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 22105b7d7..22de9a43e 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -24,8 +24,6 @@ namespace MediaBrowser.Controller.Drawing public int ImageIndex { get; set; } - public bool CropWhiteSpace { get; set; } - public int? Width { get; set; } public int? Height { get; set; } @@ -106,7 +104,6 @@ namespace MediaBrowser.Controller.Drawing PercentPlayed.Equals(0) && !UnplayedCount.HasValue && !Blur.HasValue && - !CropWhiteSpace && string.IsNullOrEmpty(BackgroundColor) && string.IsNullOrEmpty(ForegroundLayer); } From 262c6ae249b91d3fb986994a369cb83aafeb1ce8 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 9 Jan 2021 15:33:39 +0100 Subject: [PATCH 003/671] Remove ',' hack --- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 2a64bb243..2954868fc 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -938,10 +938,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - if (float.TryParse(val.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) + if (float.TryParse(val, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) { // if ratingName contains tomato --> assume critic rating - if (ratingName != null && ratingName.Contains("tomato", StringComparison.InvariantCultureIgnoreCase)) + if (ratingName != null && ratingName.Contains("tomato", StringComparison.OrdinalIgnoreCase)) { item.CriticRating = ratingValue; } From cf9a03790b4b5c77411b03a73df112f060c0ed02 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 20 Jan 2021 20:32:45 +0100 Subject: [PATCH 004/671] Check rating name for "audience" --- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 2954868fc..2e1fa4375 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -941,7 +941,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (float.TryParse(val, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) { // if ratingName contains tomato --> assume critic rating - if (ratingName != null && ratingName.Contains("tomato", StringComparison.OrdinalIgnoreCase)) + if (ratingName != null && + ratingName.Contains("tomato", StringComparison.OrdinalIgnoreCase) && + !ratingName.Contains("audience", StringComparison.OrdinalIgnoreCase)) { item.CriticRating = ratingValue; } From df402df908e3c0aae6ac276dbcef6438a00d148c Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 21 Jan 2021 12:43:54 +0000 Subject: [PATCH 005/671] fix return --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index fbd08a97c..92483ae9a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -209,6 +209,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _ffmpegPath = path; EncoderLocation = location; + return true; } else { From b646787ab6cae179e2e31f05bbdfd9201c7d1a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20V=C3=A1radi?= Date: Fri, 5 Feb 2021 20:02:47 +0100 Subject: [PATCH 006/671] Get IndexNumberEnd from nfo --- .../Parsers/EpisodeNfoParser.cs | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index 81774b873..5922b4f44 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -35,14 +35,17 @@ namespace MediaBrowser.XbmcMetadata.Parsers { item.ResetPeople(); - var xml = streamReader.ReadToEnd(); + var xmlFile = streamReader.ReadToEnd(); var srch = ""; - var index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase); + var index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase); + + var xml = xmlFile; if (index != -1) { - xml = xml.Substring(0, index + srch.Length); + xml = xmlFile.Substring(0, index + srch.Length); + xmlFile = xmlFile.Substring(index + srch.Length); } // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions @@ -73,6 +76,38 @@ namespace MediaBrowser.XbmcMetadata.Parsers catch (XmlException) { } + + while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1) + { + xml = xmlFile.Substring(0, index + srch.Length); + xmlFile = xmlFile.Substring(index + srch.Length); + + // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions + try + { + using (var stringReader = new StringReader(xml)) + using (var reader = XmlReader.Create(stringReader, settings)) + { + reader.MoveToContent(); + + if (reader.ReadToDescendant("episode")) + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + if (int.TryParse(number, out var num)) + { + item.Item.IndexNumberEnd = Math.Max(num, item.Item.IndexNumberEnd ?? num); + } + } + } + } + } + catch (XmlException) + { + } + } } } From a5e55ba85956fe54539cb22dfd95c9499aa3de6c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Feb 2021 15:59:27 -0500 Subject: [PATCH 007/671] Clean up UserManager.AuthenticateUser --- .../Users/UserManager.cs | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d1de5408c..38a074e98 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -402,27 +402,18 @@ namespace Jellyfin.Server.Implementations.Users } var user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); - bool success; - IAuthenticationProvider? authenticationProvider; + var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint) + .ConfigureAwait(false); + var authenticationProvider = authResult.authenticationProvider; + var success = authResult.success; - if (user != null) + if (user is null) { - var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint) - .ConfigureAwait(false); - authenticationProvider = authResult.authenticationProvider; - success = authResult.success; - } - else - { - var authResult = await AuthenticateLocalUser(username, password, null, remoteEndPoint) - .ConfigureAwait(false); - authenticationProvider = authResult.authenticationProvider; string updatedUsername = authResult.username; - success = authResult.success; if (success - && authenticationProvider != null - && !(authenticationProvider is DefaultAuthenticationProvider)) + && authenticationProvider is not null + && authenticationProvider is not DefaultAuthenticationProvider) { // Trust the username returned by the authentication provider username = updatedUsername; From 053063fd479a19efed31e38464d0a5d7ac2aeaca Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 7 Feb 2021 17:42:23 +0000 Subject: [PATCH 008/671] Fixed IP6 host parsing --- MediaBrowser.Common/Net/IPHost.cs | 104 +++++++++--------- .../NetworkTesting/NetworkParseTests.cs | 65 +++++------ 2 files changed, 84 insertions(+), 85 deletions(-) diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index 4cede9ab1..84aebb6e7 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -128,62 +128,62 @@ namespace MediaBrowser.Common.Net /// true if the parsing is successful, false if not. public static bool TryParse(string host, out IPHost hostObj) { - if (!string.IsNullOrEmpty(host)) + if (string.IsNullOrWhiteSpace(host)) { - // See if it's an IPv6 with port address e.g. [::1]:120. - int i = host.IndexOf("]:", StringComparison.OrdinalIgnoreCase); - if (i != -1) + hostObj = IPHost.None; + return false; + } + + // See if it's an IPv6 with port address e.g. [::1] or [::1]:120. + int i = host.IndexOf("]", StringComparison.OrdinalIgnoreCase); + if (i != -1) + { + return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj); + } + + if (IPNetAddress.TryParse(host, out var netAddress)) + { + // Host name is an ip address, so fake resolve. + hostObj = new IPHost(host, netAddress.Address); + return true; + } + + // Is it a host, IPv4/6 with/out port? + string[] hosts = host.Split(':'); + + if (hosts.Length <= 2) + { + // This is either a hostname: port, or an IP4:port. + host = hosts[0]; + + if (string.Equals("localhost", host, StringComparison.OrdinalIgnoreCase)) { - return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj); - } - else - { - // See if it's an IPv6 in [] with no port. - i = host.IndexOf(']', StringComparison.OrdinalIgnoreCase); - if (i != -1) - { - return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj); - } - - // Is it a host or IPv4 with port? - string[] hosts = host.Split(':'); - - if (hosts.Length > 2) - { - hostObj = new IPHost(string.Empty, IPAddress.None); - return false; - } - - // Remove port from IPv4 if it exists. - host = hosts[0]; - - if (string.Equals("localhost", host, StringComparison.OrdinalIgnoreCase)) - { - hostObj = new IPHost(host, new IPAddress(Ipv4Loopback)); - return true; - } - - if (IPNetAddress.TryParse(host, out IPNetAddress netIP)) - { - // Host name is an ip address, so fake resolve. - hostObj = new IPHost(host, netIP.Address); - return true; - } + hostObj = new IPHost(host); + return true; } - // Only thing left is to see if it's a host string. - if (!string.IsNullOrEmpty(host)) + if (IPAddress.TryParse(host, out var netIP)) { - // Use regular expression as CheckHostName isn't RFC5892 compliant. - // Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation - Regex re = new Regex(@"^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)$", RegexOptions.IgnoreCase | RegexOptions.Multiline); - if (re.Match(host).Success) - { - hostObj = new IPHost(host); - return true; - } + // Host name is an ip address, so fake resolve. + hostObj = new IPHost(host, netIP); + return true; } } + else + { + // Invalid host name, as it cannot contain : + hostObj = new IPHost(string.Empty, IPAddress.None); + return false; + } + + // Use regular expression as CheckHostName isn't RFC5892 compliant. + // Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation + Regex re = new Regex(@"^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)$", RegexOptions.IgnoreCase | RegexOptions.Multiline); + if (re.Match(host).Success) + { + hostObj = new IPHost(host); + return true; + } hostObj = IPHost.None; return false; @@ -344,10 +344,14 @@ namespace MediaBrowser.Common.Net { output += "Any Address,"; } - else + else if (i.AddressFamily == AddressFamily.InterNetwork) { output += $"{i}/32,"; } + else + { + output += $"{i}/128,"; + } } output = output[0..^1]; diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs index b7c1510d2..7086162a7 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs @@ -13,34 +13,6 @@ namespace Jellyfin.Networking.Tests { public class NetworkParseTests { - /// - /// Tries to identify the string and return an object of that class. - /// - /// String to parse. - /// IPObject to return. - /// True if the value parsed successfully. - private static bool TryParse(string addr, out IPObject result) - { - if (!string.IsNullOrEmpty(addr)) - { - // Is it an IP address - if (IPNetAddress.TryParse(addr, out IPNetAddress nw)) - { - result = nw; - return true; - } - - if (IPHost.TryParse(addr, out IPHost h)) - { - result = h; - return true; - } - } - - result = IPNetAddress.None; - return false; - } - private static IConfigurationManager GetMockConfig(NetworkConfiguration conf) { var configManager = new Mock @@ -118,11 +90,33 @@ namespace Jellyfin.Networking.Tests [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")] [InlineData("fe80::7add:12ff:febb:c67b%16")] [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] + [InlineData("fe80::7add:12ff:febb:c67b%16:123")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]")] + [InlineData("192.168.1.2/255.255.255.0")] + [InlineData("192.168.1.2/24")] + public void ValidHostStrings(string address) + { + Assert.True(IPHost.TryParse(address, out _)); + } + + /// + /// Checks IP address formats. + /// + /// + [Theory] + [InlineData("127.0.0.1")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")] + [InlineData("fe80::7add:12ff:febb:c67b%16")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] + [InlineData("fe80::7add:12ff:febb:c67b%16:123")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]")] [InlineData("192.168.1.2/255.255.255.0")] [InlineData("192.168.1.2/24")] public void ValidIPStrings(string address) { - Assert.True(TryParse(address, out _)); + Assert.True(IPNetAddress.TryParse(address, out _)); } @@ -138,7 +132,8 @@ namespace Jellyfin.Networking.Tests [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] public void InvalidAddressString(string address) { - Assert.False(TryParse(address, out _)); + Assert.False(IPNetAddress.TryParse(address, out _)); + Assert.False(IPHost.TryParse(address, out _)); } @@ -172,11 +167,11 @@ namespace Jellyfin.Networking.Tests "[]")] [InlineData( "192.158.1.2/16, localhost, fd23:184f:2029:0:3139:7386:67d7:d517, !10.10.10.10", - "[192.158.1.2/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]", + "[192.158.1.2/16,[127.0.0.1/32,::1/128],fd23:184f:2029:0:3139:7386:67d7:d517/128]", "[192.158.1.2/16,127.0.0.1/32]", "[10.10.10.10/32]", "[10.10.10.10/32]", - "[192.158.0.0/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]")] + "[192.158.0.0/16,127.0.0.1/32,::1/128,fd23:184f:2029:0:3139:7386:67d7:d517/128]")] [InlineData("192.158.1.2/255.255.0.0,192.169.1.2/8", "[192.158.1.2/16,192.169.1.2/8]", "[192.158.1.2/16,192.169.1.2/8]", @@ -333,8 +328,8 @@ namespace Jellyfin.Networking.Tests public void TestSubnetContains(string network, string ip) { - Assert.True(TryParse(network, out IPObject? networkObj)); - Assert.True(TryParse(ip, out IPObject? ipObj)); + Assert.True(IPNetAddress.TryParse(network, out var networkObj)); + Assert.True(IPNetAddress.TryParse(ip, out var ipObj)); Assert.True(networkObj.Contains(ipObj)); } @@ -468,7 +463,7 @@ namespace Jellyfin.Networking.Tests // User on internal network, no binding specified - so result is the 1st internal. [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")] - // User on external network, internal binding only - so asumption is a proxy forward, return external override. + // User on external network, internal binding only - so assumption is a proxy forward, return external override. [InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] // User on external network, no binding - so result is the 1st external which is overriden. From 094ffafb249e454f8e71279f8aa0693a17990e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20V=C3=A1radi?= Date: Mon, 8 Feb 2021 18:56:06 +0100 Subject: [PATCH 009/671] Comment and simplify code --- .../Parsers/EpisodeNfoParser.cs | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index 5922b4f44..3a852384a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -51,6 +51,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions try { + // Extract episode details from the firs episodedetails block using (var stringReader = new StringReader(xml)) using (var reader = XmlReader.Create(stringReader, settings)) { @@ -72,19 +73,14 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } } - } - catch (XmlException) - { - } - while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1) - { - xml = xmlFile.Substring(0, index + srch.Length); - xmlFile = xmlFile.Substring(index + srch.Length); - - // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions - try + // Extract the last episode number from nfo + // This is needed because XBMC metadata uses multiple episodedetails blocks instead of episodenumberend tag + while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1) { + xml = xmlFile.Substring(0, index + srch.Length); + xmlFile = xmlFile.Substring(index + srch.Length); + using (var stringReader = new StringReader(xml)) using (var reader = XmlReader.Create(stringReader, settings)) { @@ -104,9 +100,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } } - catch (XmlException) - { - } + } + catch (XmlException) + { } } } From 351d61a3181c6caf217ec74eed37f972190b624d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20V=C3=A1radi?= Date: Mon, 8 Feb 2021 19:40:17 +0100 Subject: [PATCH 010/671] Add test for multiepisode nfo parsing --- .../Parsers/EpisodeNfoProviderTests.cs | 20 +++++++++++++++++++ .../Test Data/Rising.nfo | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/Jellyfin.XbmcMetadata.Tests/Test Data/Rising.nfo diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs index 67b4b969a..7f87163fd 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs @@ -81,6 +81,26 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal(new DateTime(2017, 10, 7, 14, 25, 47), item.DateCreated); } + [Fact] + public void Fetch_Valid_MultiEpisode_Succes() + { + var result = new MetadataResult() + { + Item = new Episode() + }; + + _parser.Fetch(result, "Test Data/Rising.nfo", CancellationToken.None); + + var item = result.Item; + Assert.Equal("Rising (1)", item.Name); + Assert.Equal(1, item.IndexNumber); + Assert.Equal(2, item.IndexNumberEnd); + Assert.Equal(1, item.ParentIndexNumber); + Assert.Equal("A new Stargate team embarks on a dangerous mission to a distant galaxy, where they discover a mythical lost city -- and a deadly new enemy.", item.Overview); + Assert.Equal(new DateTime(2004, 7, 16), item.PremiereDate); + Assert.Equal(2004, item.ProductionYear); + } + [Fact] public void Fetch_WithNullItem_ThrowsArgumentException() { diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Rising.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Rising.nfo new file mode 100644 index 000000000..56250c09a --- /dev/null +++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Rising.nfo @@ -0,0 +1,20 @@ + + Rising (1) + 1 + 1 + 2004-07-16 + A new Stargate team embarks on a dangerous mission to a distant galaxy, where they discover a mythical lost city -- and a deadly new enemy. + https://artworks.thetvdb.com/banners/episodes/70851/25333.jpg + false + 8.0 + + + Rising (2) + 1 + 2 + 2004-07-16 + Sheppard tries to convince Weir to mount a rescue mission to free Colonel Sumner, Teyla, and the others captured by the Wraith. + https://artworks.thetvdb.com/banners/episodes/70851/25334.jpg + false + 7.9 + From c97edc96eb432940fb7352165c644bab4d6d0dda Mon Sep 17 00:00:00 2001 From: netpok Date: Mon, 8 Feb 2021 21:02:54 +0100 Subject: [PATCH 011/671] Fix typo Co-authored-by: Cody Robibero --- MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index 3a852384a..aa3be587b 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions try { - // Extract episode details from the firs episodedetails block + // Extract episode details from the first episodedetails block using (var stringReader = new StringReader(xml)) using (var reader = XmlReader.Create(stringReader, settings)) { From 7ba53548a2ed07cf3b6b4692ddd79df9d85edf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20V=C3=A1radi?= Date: Tue, 9 Feb 2021 07:21:08 +0100 Subject: [PATCH 012/671] Fix typos in nfo tests --- .../Parsers/EpisodeNfoProviderTests.cs | 4 ++-- .../Parsers/MovieNfoParserTests.cs | 2 +- .../Parsers/MusicAlbumNfoProviderTests.cs | 2 +- .../Parsers/MusicArtistNfoParserTests.cs | 2 +- .../Parsers/SeasonNfoProviderTests.cs | 2 +- .../Parsers/SeriesNfoParserTests.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs index 7f87163fd..3710d52c3 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs @@ -32,7 +32,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers } [Fact] - public void Fetch_Valid_Succes() + public void Fetch_Valid_Success() { var result = new MetadataResult() { @@ -82,7 +82,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers } [Fact] - public void Fetch_Valid_MultiEpisode_Succes() + public void Fetch_Valid_MultiEpisode_Success() { var result = new MetadataResult() { diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 765464ece..007a41a24 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -30,7 +30,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers } [Fact] - public void Fetch_Valid_Succes() + public void Fetch_Valid_Success() { var result = new MetadataResult