From 8da012c8c507b6a177cfff233cfdac3490f79847 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Thu, 10 Oct 2019 12:09:16 +0900 Subject: add tests for Emby.Naming/TV/EpisodePathParser.cs This should in the future help to detect working and non working name matchings --- .../Jellyfin.Naming.Tests/EpisodePathParserTest.cs | 25 ++++++++++++++++++++++ .../Jellyfin.Naming.Tests.csproj | 20 +++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs create mode 100644 tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj (limited to 'tests') diff --git a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs new file mode 100644 index 000000000..596bb3253 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs @@ -0,0 +1,25 @@ +namespace Emby.Naming.TV +{ + using Emby.Naming.Common; + using Xunit; + + public class EpisodePathParserTest + { + [Theory] + [InlineData("/media/Foo/Foo-S01E01", "Foo", 1, 1)] + [InlineData("/media/Foo - S04E011", "Foo", 4, 11)] + [InlineData("/media/Foo/Foo s01x01", "Foo", 1, 1)] + [InlineData("/media/Foo/Foo s01x03 - the bar of foo", "Foo", 1, 3)] + public void ParseEpisodesCorrectly(string path, string name, int season, int episode) + { + NamingOptions o = new NamingOptions(); + EpisodePathParser p = new EpisodePathParser(o); + var res = p.Parse(path, false); + + Assert.True(res.Success); + Assert.Equal(name, res.SeriesName); + Assert.Equal(season, res.SeasonNumber); + Assert.Equal(episode, res.EpisodeNumber); + } + } +} \ No newline at end of file diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj new file mode 100644 index 000000000..f5e151348 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.0 + + false + + + + + + + + + + + + + + -- cgit v1.2.3 From 45f906c5564d8ecbc3d6d1cabcd78e929a9fb7e1 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Fri, 11 Oct 2019 11:46:51 +0900 Subject: added a couple more tests --- .../Jellyfin.Naming.Tests/EpisodePathParserTest.cs | 30 +++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs index 596bb3253..c4e1ff354 100644 --- a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs @@ -9,7 +9,7 @@ namespace Emby.Naming.TV [InlineData("/media/Foo/Foo-S01E01", "Foo", 1, 1)] [InlineData("/media/Foo - S04E011", "Foo", 4, 11)] [InlineData("/media/Foo/Foo s01x01", "Foo", 1, 1)] - [InlineData("/media/Foo/Foo s01x03 - the bar of foo", "Foo", 1, 3)] + [InlineData("/media/Foo (2019)/Season 4/Foo (2019).S04E03", "Foo (2019)", 4, 3)] public void ParseEpisodesCorrectly(string path, string name, int season, int episode) { NamingOptions o = new NamingOptions(); @@ -20,6 +20,34 @@ namespace Emby.Naming.TV Assert.Equal(name, res.SeriesName); Assert.Equal(season, res.SeasonNumber); Assert.Equal(episode, res.EpisodeNumber); + + //testing other paths delimeter + var res2 = p.Parse(path.Replace("/", "\\"), false); + Assert.True(res2.Success); + Assert.Equal(name, res2.SeriesName); + Assert.Equal(season, res2.SeasonNumber); + Assert.Equal(episode, res2.EpisodeNumber); + } + + [Theory] + [InlineData("/media/Foo/Foo 889.avi", "Foo", 889)] + public void ParseEpisodeWithoutSeason(string path, string name, int episode) + { + NamingOptions o = new NamingOptions(); + EpisodePathParser p = new EpisodePathParser(o); + var res = p.Parse(path, false, null, null, true); + + Assert.True(res.Success); + Assert.Equal(name, res.SeriesName); + Assert.True(res.SeasonNumber == null); + Assert.Equal(episode, res.EpisodeNumber); + + //testing other paths delimeter + var res2 = p.Parse(path.Replace("/", "\\"), false, null, null, true); + Assert.True(res2.Success); + Assert.Equal(name, res2.SeriesName); + Assert.True(res2.SeasonNumber == null); + Assert.Equal(episode, res2.EpisodeNumber); } } } \ No newline at end of file -- cgit v1.2.3 From 4a20260a27e8ede4188609d8206a7313f7243e97 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Fri, 11 Oct 2019 19:24:55 +0900 Subject: add another parser case and allow parsing of seasonless Add another parser case and we now allow parsing of seasonless series which hopefully should cover more cases of directory structure --- Emby.Naming/Common/NamingOptions.cs | 10 +++++++--- Emby.Server.Implementations/Library/LibraryManager.cs | 6 +++++- tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs | 5 +++-- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 88a9b46e6..e1f25fd2b 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -328,6 +328,10 @@ namespace Emby.Naming.Common // *** End Kodi Standard Naming +                // [bar] Foo - 1 [baz] +                new EpisodeExpression(@".*?(\[.*?\])+.*?(?(\w+\s)+?)[-\s_]+(?\d{1,3}).*$"){ + IsNamed=false, + }, new EpisodeExpression(@".*(\\|\/)[sS]?(?\d{1,4})[xX](?\d{1,3})[^\\\/]*$") { IsNamed = true @@ -654,9 +658,9 @@ namespace Emby.Naming.Common @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})((-| - )?[xXeE](?\d{1,3}))+[^\\\/]*$", @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})(-[xX]?[eE]?(?\d{1,3}))+[^\\\/]*$" }.Select(i => new EpisodeExpression(i) - { - IsNamed = true - }).ToArray(); + { + IsNamed = true + }).ToArray(); VideoFileExtensions = extensions .Distinct(StringComparer.OrdinalIgnoreCase) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 87e951f25..90f373cc6 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1899,7 +1899,7 @@ namespace Emby.Server.Implementations.Library /// The cancellation token. public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { - UpdateItems(new [] { item }, parent, updateReason, cancellationToken); + UpdateItems(new[] { item }, parent, updateReason, cancellationToken); } /// @@ -2487,6 +2487,10 @@ namespace Emby.Server.Implementations.Library { episode.ParentIndexNumber = season.IndexNumber; } + else + { + episode.ParentIndexNumber = 1; + } if (episode.ParentIndexNumber.HasValue) { diff --git a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs index c4e1ff354..28ccd6df1 100644 --- a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs @@ -30,12 +30,13 @@ namespace Emby.Naming.TV } [Theory] - [InlineData("/media/Foo/Foo 889.avi", "Foo", 889)] + [InlineData("/media/Foo/Foo 889", "Foo", 889)] + [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] public void ParseEpisodeWithoutSeason(string path, string name, int episode) { NamingOptions o = new NamingOptions(); EpisodePathParser p = new EpisodePathParser(o); - var res = p.Parse(path, false, null, null, true); + var res = p.Parse(path, true, null, null, true); Assert.True(res.Success); Assert.Equal(name, res.SeriesName); -- cgit v1.2.3 From a245f5a0d463e132bcbb3c5871465bdb8bbec0b7 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 19 Oct 2019 00:22:08 +0200 Subject: Rewrite hex encoder/decoder --- .gitignore | 3 ++ Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Drawing/Emby.Drawing.csproj | 7 +-- Emby.Notifications/Emby.Notifications.csproj | 2 +- Emby.Photos/Emby.Photos.csproj | 2 +- .../Library/DefaultAuthenticationProvider.cs | 4 +- Emby.Server.Implementations/Library/UserManager.cs | 4 +- .../LiveTv/LiveTvManager.cs | 6 ++- .../Updates/InstallationManager.cs | 4 +- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 2 +- MediaBrowser.Api/LiveTv/LiveTvService.cs | 7 +-- MediaBrowser.Api/MediaBrowser.Api.csproj | 2 +- MediaBrowser.Common/Cryptography/PasswordHash.cs | 11 ++--- .../Extensions/CollectionExtensions.cs | 48 ------------------ MediaBrowser.Common/Extensions/CopyToExtensions.cs | 26 ++++++++++ MediaBrowser.Common/Hex.cs | 57 ++++++++++++++++++++++ MediaBrowser.Common/HexHelper.cs | 24 --------- MediaBrowser.Common/MediaBrowser.Common.csproj | 2 +- .../MediaBrowser.Controller.csproj | 2 +- .../MediaBrowser.LocalMetadata.csproj | 2 +- .../MediaBrowser.MediaEncoding.csproj | 2 +- .../MediaBrowser.Providers.csproj | 4 +- .../MediaBrowser.WebDashboard.csproj | 2 +- .../MediaBrowser.XbmcMetadata.csproj | 2 +- Mono.Nat/Mono.Nat.csproj | 2 +- RSSDP/RSSDP.csproj | 2 +- .../Jellyfin.Common.Benches/HexDecodeBenches.cs | 42 ++++++++++++++++ .../Jellyfin.Common.Benches/HexEncodeBenches.cs | 29 +++++++++++ .../Jellyfin.Common.Benches.csproj | 16 ++++++ benches/Jellyfin.Common.Benches/Program.cs | 14 ++++++ tests/Jellyfin.Common.Tests/HexTests.cs | 19 ++++++++ tests/Jellyfin.Common.Tests/PasswordHashTests.cs | 6 +-- 32 files changed, 243 insertions(+), 114 deletions(-) delete mode 100644 MediaBrowser.Common/Extensions/CollectionExtensions.cs create mode 100644 MediaBrowser.Common/Extensions/CopyToExtensions.cs create mode 100644 MediaBrowser.Common/Hex.cs delete mode 100644 MediaBrowser.Common/HexHelper.cs create mode 100644 benches/Jellyfin.Common.Benches/HexDecodeBenches.cs create mode 100644 benches/Jellyfin.Common.Benches/HexEncodeBenches.cs create mode 100644 benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj create mode 100644 benches/Jellyfin.Common.Benches/Program.cs create mode 100644 tests/Jellyfin.Common.Tests/HexTests.cs (limited to 'tests') diff --git a/.gitignore b/.gitignore index 34cf1a84c..42243f01a 100644 --- a/.gitignore +++ b/.gitignore @@ -268,3 +268,6 @@ doc/ # Deployment artifacts dist *.exe + +# BenchmarkDotNet artifacts +BenchmarkDotNet.Artifacts diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 34b49120b..8d6fabdb4 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -12,7 +12,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 2e539f2c7..85cecdc44 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.1 false true true @@ -17,9 +17,4 @@ - - - latest - - diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index cbd3bde4f..004ded77b 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index b57b93a8c..a71c75127 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -14,7 +14,7 @@ - netstandard2.0 + netstandard2.1 false true true diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index c95b00ede..85110c21c 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -2,11 +2,11 @@ using System; using System.Linq; using System.Text; using System.Threading.Tasks; +using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Cryptography; -using static MediaBrowser.Common.HexHelper; namespace Emby.Server.Implementations.Library { @@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Library { return string.IsNullOrEmpty(user.EasyPassword) ? null - : ToHexString(PasswordHash.Parse(user.EasyPassword).Hash); + : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash); } /// diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 2b6ae1297..60d16c8a0 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Events; using MediaBrowser.Common.Net; @@ -31,7 +32,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Users; using Microsoft.Extensions.Logging; -using static MediaBrowser.Common.HexHelper; namespace Emby.Server.Implementations.Library { @@ -490,7 +490,7 @@ namespace Emby.Server.Implementations.Library { return string.IsNullOrEmpty(user.EasyPassword) ? null - : ToHexString(PasswordHash.Parse(user.EasyPassword).Hash); + : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash); } private void ResetInvalidLoginAttemptCount(User user) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 49308b2b1..d4bd598e3 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2304,8 +2304,10 @@ namespace Emby.Server.Implementations.LiveTv if (provider == null) { throw new ResourceNotFoundException( - string.Format("Couldn't find provider of type: '{0}'", info.Type) - ); + string.Format( + CultureInfo.InvariantCulture, + "Couldn't find provider of type: '{0}'", + info.Type)); } await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 0c0c77cda..024bc9a47 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; @@ -19,7 +18,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Updates; using Microsoft.Extensions.Logging; -using static MediaBrowser.Common.HexHelper; namespace Emby.Server.Implementations.Updates { @@ -455,7 +453,7 @@ namespace Emby.Server.Implementations.Updates { cancellationToken.ThrowIfCancellationRequested(); - var hash = ToHexString(md5.ComputeHash(stream)); + var hash = Hex.Encode(md5.ComputeHash(stream)); if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase)) { _logger.LogError( diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 396bdd4b7..988ac364a 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index b05e8c949..2b9a64e97 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Api.UserLibrary; +using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -25,7 +26,6 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Net.Http.Headers; -using static MediaBrowser.Common.HexHelper; namespace MediaBrowser.Api.LiveTv { @@ -887,8 +887,9 @@ namespace MediaBrowser.Api.LiveTv { // SchedulesDirect requires a SHA1 hash of the user's password // https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token - using (SHA1 sha = SHA1.Create()) { - return ToHexString( + using (SHA1 sha = SHA1.Create()) + { + return Hex.Encode( sha.ComputeHash(Encoding.UTF8.GetBytes(str))); } } diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index f653270a6..0d62cf8c5 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -10,7 +10,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs index dca31cd92..4c6804097 100644 --- a/MediaBrowser.Common/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; -using static MediaBrowser.Common.HexHelper; namespace MediaBrowser.Common.Cryptography { @@ -102,13 +101,13 @@ namespace MediaBrowser.Common.Cryptography // Check if the string also contains a salt if (splitted.Length - index == 2) { - salt = FromHexString(splitted[index++]); - hash = FromHexString(splitted[index++]); + salt = Hex.Decode(splitted[index++]); + hash = Hex.Decode(splitted[index++]); } else { salt = Array.Empty(); - hash = FromHexString(splitted[index++]); + hash = Hex.Decode(splitted[index++]); } return new PasswordHash(id, hash, salt, parameters); @@ -145,11 +144,11 @@ namespace MediaBrowser.Common.Cryptography if (Salt.Length != 0) { str.Append('$') - .Append(ToHexString(Salt)); + .Append(Hex.Encode(Salt, false)); } return str.Append('$') - .Append(ToHexString(Hash)).ToString(); + .Append(Hex.Encode(Hash, false)).ToString(); } } } diff --git a/MediaBrowser.Common/Extensions/CollectionExtensions.cs b/MediaBrowser.Common/Extensions/CollectionExtensions.cs deleted file mode 100644 index 215224398..000000000 --- a/MediaBrowser.Common/Extensions/CollectionExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; - -namespace MediaBrowser.Common.Extensions -{ - // The MS CollectionExtensions are only available in netcoreapp - public static class CollectionExtensions - { - public static TValue GetValueOrDefault(this IReadOnlyDictionary dictionary, TKey key) - { - dictionary.TryGetValue(key, out var ret); - return ret; - } - - /// - /// Copies all the elements of the current collection to the specified list - /// starting at the specified destination array index. The index is specified as a 32-bit integer. - /// - /// The current collection that is the source of the elements. - /// The list that is the destination of the elements copied from the current collection. - /// A 32-bit integer that represents the index in destination at which copying begins. - /// - public static void CopyTo(this IReadOnlyList source, IList destination, int index = 0) - { - for (int i = 0; i < source.Count; i++) - { - destination[index + i] = source[i]; - } - } - - /// - /// Copies all the elements of the current collection to the specified list - /// starting at the specified destination array index. The index is specified as a 32-bit integer. - /// - /// The current collection that is the source of the elements. - /// The list that is the destination of the elements copied from the current collection. - /// A 32-bit integer that represents the index in destination at which copying begins. - /// - public static void CopyTo(this IReadOnlyCollection source, IList destination, int index = 0) - { - foreach (T item in source) - { - destination[index++] = item; - } - } - } -} diff --git a/MediaBrowser.Common/Extensions/CopyToExtensions.cs b/MediaBrowser.Common/Extensions/CopyToExtensions.cs new file mode 100644 index 000000000..78a73f07e --- /dev/null +++ b/MediaBrowser.Common/Extensions/CopyToExtensions.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Common.Extensions +{ + /// + /// Provides CopyTo extensions methods for . + /// + public static class CollectionExtensions + { + /// + /// Copies all the elements of the current collection to the specified list + /// starting at the specified destination array index. The index is specified as a 32-bit integer. + /// + /// The current collection that is the source of the elements. + /// The list that is the destination of the elements copied from the current collection. + /// A 32-bit integer that represents the index in destination at which copying begins. + /// + public static void CopyTo(this IReadOnlyList source, IList destination, int index = 0) + { + for (int i = 0; i < source.Count; i++) + { + destination[index + i] = source[i]; + } + } + } +} diff --git a/MediaBrowser.Common/Hex.cs b/MediaBrowser.Common/Hex.cs new file mode 100644 index 000000000..e19a9a1f4 --- /dev/null +++ b/MediaBrowser.Common/Hex.cs @@ -0,0 +1,57 @@ +using System; +using System.Globalization; + +namespace MediaBrowser.Common +{ + /// + /// Encoding and decoding hex strings. + /// + public static class Hex + { + internal const string HexCharsLower = "0123456789abcdef"; + internal const string HexCharsUpper = "0123456789ABCDEF"; + + /// + /// Encodes bytes as a hex string. + /// + /// + /// + /// bytes as a hex string. + public static string Encode(ReadOnlySpan bytes, bool lowercase = true) + { + var hexChars = lowercase ? HexCharsLower : HexCharsUpper; + + // TODO: use string.Create when it's supports spans + // Ref: https://github.com/dotnet/corefx/issues/29120 + char[] s = new char[bytes.Length * 2]; + int j = 0; + for (int i = 0; i < bytes.Length; i++) + { + s[j++] = hexChars[bytes[i] >> 4]; + s[j++] = hexChars[bytes[i] & 0x0f]; + } + + return new string(s); + } + + /// + /// Decodes a hex string into bytes. + /// + /// The . + /// The decoded bytes. + public static byte[] Decode(ReadOnlySpan str) + { + byte[] bytes = new byte[str.Length / 2]; + int j = 0; + for (int i = 0; i < str.Length; i += 2) + { + bytes[j++] = byte.Parse( + str.Slice(i, 2), + NumberStyles.HexNumber, + CultureInfo.InvariantCulture); + } + + return bytes; + } + } +} diff --git a/MediaBrowser.Common/HexHelper.cs b/MediaBrowser.Common/HexHelper.cs deleted file mode 100644 index 61007b5b2..000000000 --- a/MediaBrowser.Common/HexHelper.cs +++ /dev/null @@ -1,24 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Globalization; - -namespace MediaBrowser.Common -{ - public static class HexHelper - { - public static byte[] FromHexString(string str) - { - byte[] bytes = new byte[str.Length / 2]; - for (int i = 0; i < str.Length; i += 2) - { - bytes[i / 2] = byte.Parse(str.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); - } - - return bytes; - } - - public static string ToHexString(byte[] bytes) - => BitConverter.ToString(bytes).Replace("-", ""); - } -} diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index cf3f6c2a4..922a62a5f 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -21,7 +21,7 @@ - netstandard2.0 + netstandard2.1 false true true diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index c6bca2518..276eb71bc 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -17,7 +17,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index a8f8da9b8..71eb62693 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -10,7 +10,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 264f31f3c..083c361d0 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.1 false true diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index c7ecc59c9..9e108a18b 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -19,7 +19,7 @@ - netstandard2.0 + netstandard2.1 false true @@ -28,5 +28,5 @@ latest - + diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index a43949367..1d256d689 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -16,7 +16,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 1ca9e43bb..ecc61a8d8 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -10,7 +10,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/Mono.Nat/Mono.Nat.csproj b/Mono.Nat/Mono.Nat.csproj index edfd5c9bb..c143000b3 100644 --- a/Mono.Nat/Mono.Nat.csproj +++ b/Mono.Nat/Mono.Nat.csproj @@ -10,7 +10,7 @@ - netstandard2.0 + netstandard2.1 false diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj index 456a93aa8..9753ae9b1 100644 --- a/RSSDP/RSSDP.csproj +++ b/RSSDP/RSSDP.csproj @@ -7,7 +7,7 @@ - netstandard2.0 + netstandard2.1 false diff --git a/benches/Jellyfin.Common.Benches/HexDecodeBenches.cs b/benches/Jellyfin.Common.Benches/HexDecodeBenches.cs new file mode 100644 index 000000000..281275597 --- /dev/null +++ b/benches/Jellyfin.Common.Benches/HexDecodeBenches.cs @@ -0,0 +1,42 @@ +using System; +using System.Globalization; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using MediaBrowser.Common; + +namespace Jellyfin.Common.Benches +{ + [MemoryDiagnoser] + public class HexDecodeBenches + { + private const int N = 1000000; + private readonly string data; + + public HexDecodeBenches() + { + var tmp = new byte[N]; + new Random(42).NextBytes(tmp); + data = Hex.Encode(tmp); + } + + public static byte[] DecodeSubString(string str) + { + byte[] bytes = new byte[str.Length / 2]; + for (int i = 0; i < str.Length; i += 2) + { + bytes[i / 2] = byte.Parse( + str.Substring(i, 2), + NumberStyles.HexNumber, + CultureInfo.InvariantCulture); + } + + return bytes; + } + + [Benchmark] + public byte[] Decode() => Hex.Decode(data); + + [Benchmark] + public byte[] DecodeSubString() => DecodeSubString(data); + } +} diff --git a/benches/Jellyfin.Common.Benches/HexEncodeBenches.cs b/benches/Jellyfin.Common.Benches/HexEncodeBenches.cs new file mode 100644 index 000000000..e7b446cc2 --- /dev/null +++ b/benches/Jellyfin.Common.Benches/HexEncodeBenches.cs @@ -0,0 +1,29 @@ +using System; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using MediaBrowser.Common; + +namespace Jellyfin.Common.Benches +{ + [MemoryDiagnoser] + public class HexEncodeBenches + { + private const int N = 1000; + private readonly byte[] data; + + public HexEncodeBenches() + { + data = new byte[N]; + new Random(42).NextBytes(data); + } + + [Benchmark] + public string HexEncode() => Hex.Encode(data); + + [Benchmark] + public string BitConverterToString() => BitConverter.ToString(data); + + [Benchmark] + public string BitConverterToStringWithReplace() => BitConverter.ToString(data).Replace("-", ""); + } +} diff --git a/benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj b/benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj new file mode 100644 index 000000000..4d5046bf9 --- /dev/null +++ b/benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + diff --git a/benches/Jellyfin.Common.Benches/Program.cs b/benches/Jellyfin.Common.Benches/Program.cs new file mode 100644 index 000000000..b218b0dc1 --- /dev/null +++ b/benches/Jellyfin.Common.Benches/Program.cs @@ -0,0 +1,14 @@ +using System; +using BenchmarkDotNet.Running; + +namespace Jellyfin.Common.Benches +{ + public static class Program + { + public static void Main(string[] args) + { + _ = BenchmarkRunner.Run(); + _ = BenchmarkRunner.Run(); + } + } +} diff --git a/tests/Jellyfin.Common.Tests/HexTests.cs b/tests/Jellyfin.Common.Tests/HexTests.cs new file mode 100644 index 000000000..5b578d38c --- /dev/null +++ b/tests/Jellyfin.Common.Tests/HexTests.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Common; +using Xunit; + +namespace Jellyfin.Common.Tests +{ + public class HexTests + { + [Theory] + [InlineData("")] + [InlineData("00")] + [InlineData("01")] + [InlineData("000102030405060708090a0b0c0d0e0f")] + [InlineData("0123456789abcdef")] + public void RoundTripTest(string data) + { + Assert.Equal(data, Hex.Encode(Hex.Decode(data))); + } + } +} diff --git a/tests/Jellyfin.Common.Tests/PasswordHashTests.cs b/tests/Jellyfin.Common.Tests/PasswordHashTests.cs index 5fa86f3bd..03523dbc4 100644 --- a/tests/Jellyfin.Common.Tests/PasswordHashTests.cs +++ b/tests/Jellyfin.Common.Tests/PasswordHashTests.cs @@ -1,6 +1,6 @@ +using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using Xunit; -using static MediaBrowser.Common.HexHelper; namespace Jellyfin.Common.Tests { @@ -15,8 +15,8 @@ namespace Jellyfin.Common.Tests { var pass = PasswordHash.Parse(passwordHash); Assert.Equal(id, pass.Id); - Assert.Equal(salt, ToHexString(pass.Salt)); - Assert.Equal(hash, ToHexString(pass.Hash)); + Assert.Equal(salt, Hex.Encode(pass.Salt, false)); + Assert.Equal(hash, Hex.Encode(pass.Hash, false)); } [Theory] -- cgit v1.2.3 From a2df6e0e36a05e50f09a3b2e2df9408661b1e6ea Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 8 Nov 2019 19:59:48 +0100 Subject: Fix naming tests --- Emby.Naming/Common/NamingOptions.cs | 13 ++++++++--- Emby.Naming/Emby.Naming.csproj | 8 +++---- .../Jellyfin.Common.Tests.csproj | 2 +- .../Jellyfin.Naming.Tests/EpisodePathParserTest.cs | 27 +++++++++++----------- .../Jellyfin.Naming.Tests.csproj | 9 ++++---- 5 files changed, 33 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 9cf430daf..bb214e9f2 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -311,6 +311,12 @@ namespace Emby.Naming.Common } }, + // "Foo Bar 889" + new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?(\w+\s*?)*)\s(?\d{1,3})(-(?\d{2,3}))*[^\\\/]*$") + { + IsNamed = true + }, + new EpisodeExpression("[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$") { SupportsAbsoluteEpisodeNumbers = true @@ -328,9 +334,10 @@ namespace Emby.Naming.Common // *** End Kodi Standard Naming -                // [bar] Foo - 1 [baz] -                new EpisodeExpression(@".*?(\[.*?\])+.*?(?(\w+\s)+?)[-\s_]+(?\d+).*$"){ - IsNamed=false, +                // [bar] Foo - 1 [baz] + new EpisodeExpression(@".*?(\[.*?\])+.*?(?(\w+\s*?)+?)[-\s_]+(?\d+).*$") + { + IsNamed = true }, new EpisodeExpression(@".*(\\|\/)[sS]?(?\d+)[xX](?\d+)[^\\\/]*$") { diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index a23fa3df7..fd0773df5 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -23,10 +23,10 @@ - - - - + + + + diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index bb40985a4..aa005b31d 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs index 28ccd6df1..dd1e04215 100644 --- a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs @@ -1,8 +1,9 @@ -namespace Emby.Naming.TV -{ - using Emby.Naming.Common; - using Xunit; +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; +namespace Jellyfin.Naming.Tests +{ public class EpisodePathParserTest { [Theory] @@ -21,8 +22,8 @@ namespace Emby.Naming.TV Assert.Equal(season, res.SeasonNumber); Assert.Equal(episode, res.EpisodeNumber); - //testing other paths delimeter - var res2 = p.Parse(path.Replace("/", "\\"), false); + // testing other paths delimeter + var res2 = p.Parse(path.Replace('/', '\\'), false); Assert.True(res2.Success); Assert.Equal(name, res2.SeriesName); Assert.Equal(season, res2.SeasonNumber); @@ -31,24 +32,24 @@ namespace Emby.Naming.TV [Theory] [InlineData("/media/Foo/Foo 889", "Foo", 889)] - [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] + [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] public void ParseEpisodeWithoutSeason(string path, string name, int episode) { NamingOptions o = new NamingOptions(); EpisodePathParser p = new EpisodePathParser(o); - var res = p.Parse(path, true, null, null, true); + var res = p.Parse(path, true, fillExtendedInfo: true); Assert.True(res.Success); Assert.Equal(name, res.SeriesName); - Assert.True(res.SeasonNumber == null); + Assert.Null(res.SeasonNumber); Assert.Equal(episode, res.EpisodeNumber); - //testing other paths delimeter - var res2 = p.Parse(path.Replace("/", "\\"), false, null, null, true); + // testing other paths delimeter + var res2 = p.Parse(path.Replace('/', '\\'), false, fillExtendedInfo: false); Assert.True(res2.Success); Assert.Equal(name, res2.SeriesName); - Assert.True(res2.SeasonNumber == null); + Assert.Null(res2.SeasonNumber); Assert.Equal(episode, res2.EpisodeNumber); } } -} \ No newline at end of file +} diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index f5e151348..fe1518131 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -2,15 +2,14 @@ netcoreapp3.0 - false - - - - + + + + -- cgit v1.2.3