From 3205e97e1e7ad6e3e84797f9575b6958d5dfc8b0 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Fri, 4 Mar 2022 10:52:15 +0100 Subject: [PATCH 1/2] Strip out external file fuzzy matching Convert MediaFlagDelimiter back to char --- Emby.Naming/Common/NamingOptions.cs | 4 ++-- .../ExternalFiles/ExternalPathParser.cs | 8 ++++---- .../MediaInfo/MediaInfoResolver.cs | 17 ++++------------- .../MediaInfo/MediaInfoResolverTests.cs | 18 +++++++++--------- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index a79153e86e..de9c75da21 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -266,7 +266,7 @@ namespace Emby.Naming.Common MediaFlagDelimiters = new[] { - "." + '.' }; MediaForcedFlags = new[] @@ -715,7 +715,7 @@ namespace Emby.Naming.Common /// /// Gets or sets list of external media flag delimiters. /// - public string[] MediaFlagDelimiters { get; set; } + public char[] MediaFlagDelimiters { get; set; } /// /// Gets or sets list of external media forced flags. diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs index 9d07dc2f9c..3bde3a1cf9 100644 --- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs +++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs @@ -61,11 +61,11 @@ namespace Emby.Naming.ExternalFiles { var languageString = extraString; var titleString = string.Empty; - int separatorLength = separator.Length; + const int SeparatorLength = 1; while (languageString.Length > 0) { - int lastSeparator = languageString.LastIndexOf(separator, StringComparison.OrdinalIgnoreCase); + int lastSeparator = languageString.LastIndexOf(separator); if (lastSeparator == -1) { @@ -73,7 +73,7 @@ namespace Emby.Naming.ExternalFiles } string currentSlice = languageString[lastSeparator..]; - string currentSliceWithoutSeparator = currentSlice[separatorLength..]; + string currentSliceWithoutSeparator = currentSlice[SeparatorLength..]; if (_namingOptions.MediaDefaultFlags.Any(s => currentSliceWithoutSeparator.Contains(s, StringComparison.OrdinalIgnoreCase))) { @@ -107,7 +107,7 @@ namespace Emby.Naming.ExternalFiles languageString = languageString[..lastSeparator]; } - pathInfo.Title = separatorLength <= titleString.Length ? titleString[separatorLength..] : null; + pathInfo.Title = titleString.Length >= SeparatorLength ? titleString[SeparatorLength..] : null; } return pathInfo; diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index 359eb88020..d1594a9679 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -24,16 +24,6 @@ namespace MediaBrowser.Providers.MediaInfo /// public abstract class MediaInfoResolver { - /// - /// The instance. - /// - private const CompareOptions CompareOptions = System.Globalization.CompareOptions.IgnoreCase | System.Globalization.CompareOptions.IgnoreNonSpace | System.Globalization.CompareOptions.IgnoreSymbols; - - /// - /// The instance. - /// - private readonly CompareInfo _compareInfo = CultureInfo.InvariantCulture.CompareInfo; - /// /// The instance. /// @@ -175,11 +165,12 @@ namespace MediaBrowser.Providers.MediaInfo foreach (var file in files) { + var prefixLength = video.FileNameWithoutExtension.Length; var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - if (_compareInfo.IsPrefix(fileNameWithoutExtension, video.FileNameWithoutExtension, CompareOptions, out int matchLength) - && (fileNameWithoutExtension.Length == matchLength || _namingOptions.MediaFlagDelimiters.Contains(fileNameWithoutExtension[matchLength].ToString()))) + if (video.FileNameWithoutExtension.Equals(fileNameWithoutExtension[..prefixLength], StringComparison.OrdinalIgnoreCase) + && (fileNameWithoutExtension.Length == prefixLength || _namingOptions.MediaFlagDelimiters.Contains(fileNameWithoutExtension[prefixLength]))) { - var externalPathInfo = _externalPathParser.ParseFile(file, fileNameWithoutExtension[matchLength..]); + var externalPathInfo = _externalPathParser.ParseFile(file, fileNameWithoutExtension[prefixLength..]); if (externalPathInfo != null) { diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs index 89bc416def..9bd02d956c 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs @@ -133,18 +133,18 @@ public class MediaInfoResolverTests } [Theory] - [InlineData("My.Video.srt", null)] // exact - [InlineData("My.Video.en.srt", "eng")] - [InlineData("MyVideo.en.srt", "eng")] // shorter title - [InlineData("My _ Video.en.srt", "eng")] // longer title - [InlineData("My.Video.en.srt", "eng", true)] - public void GetExternalFiles_FuzzyMatching_MatchesAndParsesToken(string file, string? language, bool metadataDirectory = false) + [InlineData("My.Video.mkv", "My.Video.srt", null)] + [InlineData("My.Video.mkv", "My.Video.en.srt", "eng")] + [InlineData("My.Video.mkv", "My.Video.en.srt", "eng", true)] + [InlineData("Example Movie (2021).mp4", "Example Movie (2021).English.Srt", "eng")] + [InlineData("[LTDB] Who Framed Roger Rabbit (1998) - [Bluray-1080p].mkv", "[LTDB] Who Framed Roger Rabbit (1998) - [Bluray-1080p].en.srt", "eng")] + public void GetExternalFiles_NameMatching_MatchesAndParsesToken(string movie, string file, string? language, bool metadataDirectory = false) { BaseItem.MediaSourceManager = Mock.Of(); var video = new Movie { - Path = VideoDirectoryPath + "/My.Video.mkv" + Path = VideoDirectoryPath + "/" + movie }; var directoryService = GetDirectoryServiceForExternalFile(file, metadataDirectory); @@ -162,7 +162,7 @@ public class MediaInfoResolverTests [InlineData("My.Video.txt")] [InlineData("My.Video Sequel.srt")] [InlineData("Some.Other.Video.srt")] - public void GetExternalFiles_FuzzyMatching_RejectsNonMatches(string file) + public void GetExternalFiles_NameMatching_RejectsNonMatches(string file) { BaseItem.MediaSourceManager = Mock.Of(); @@ -344,7 +344,7 @@ public class MediaInfoResolverTests var files = new string[fileCount]; for (int i = 0; i < fileCount; i++) { - files[i] = $"{VideoDirectoryPath}/MyVideo.{i}.srt"; + files[i] = $"{VideoDirectoryPath}/My.Video.{i}.srt"; } var directoryService = new Mock(MockBehavior.Strict); From dad7a6fdf6f345969f10942257af6b3d0d61fd9a Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Fri, 4 Mar 2022 15:58:01 +0100 Subject: [PATCH 2/2] Switch to using spans for string comparison --- .../MediaInfo/MediaInfoResolver.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index d1594a9679..9338272079 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -149,8 +149,6 @@ namespace MediaBrowser.Providers.MediaInfo return Array.Empty(); } - var externalPathInfos = new List(); - var files = directoryService.GetFilePaths(folder, clearCache).ToList(); var internalMetadataPath = video.GetInternalMetadataPath(); if (_fileSystem.DirectoryExists(internalMetadataPath)) @@ -163,14 +161,15 @@ namespace MediaBrowser.Providers.MediaInfo return Array.Empty(); } + var externalPathInfos = new List(); + ReadOnlySpan prefix = video.FileNameWithoutExtension; foreach (var file in files) { - var prefixLength = video.FileNameWithoutExtension.Length; - var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - if (video.FileNameWithoutExtension.Equals(fileNameWithoutExtension[..prefixLength], StringComparison.OrdinalIgnoreCase) - && (fileNameWithoutExtension.Length == prefixLength || _namingOptions.MediaFlagDelimiters.Contains(fileNameWithoutExtension[prefixLength]))) + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file.AsSpan()); + if (prefix.Equals(fileNameWithoutExtension[..prefix.Length], StringComparison.OrdinalIgnoreCase) + && (fileNameWithoutExtension.Length == prefix.Length || _namingOptions.MediaFlagDelimiters.Contains(fileNameWithoutExtension[prefix.Length]))) { - var externalPathInfo = _externalPathParser.ParseFile(file, fileNameWithoutExtension[prefixLength..]); + var externalPathInfo = _externalPathParser.ParseFile(file, fileNameWithoutExtension[prefix.Length..].ToString()); if (externalPathInfo != null) {