From df402df908e3c0aae6ac276dbcef6438a00d148c Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 21 Jan 2021 12:43:54 +0000 Subject: [PATCH 01/61] 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 fbd08a97c7..92483ae9a5 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 02/61] 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 81774b8735..5922b4f44f 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 053063fd479a19efed31e38464d0a5d7ac2aeaca Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 7 Feb 2021 17:42:23 +0000 Subject: [PATCH 03/61] 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 4cede9ab16..84aebb6e78 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 b7c1510d27..7086162a7f 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 04/61] 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 5922b4f44f..3a852384af 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 05/61] 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 67b4b969a3..7f87163fdf 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 0000000000..56250c09a8 --- /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 06/61] 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 3a852384af..aa3be587b4 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 07/61] 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 7f87163fdf..3710d52c31 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 765464eced..007a41a24a 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