Browse Source

Clean up Emby.Naming

Bond_009 5 years ago
parent
commit
dc62e436c4

+ 4 - 7
Emby.Naming/Audio/AlbumParser.cs

@@ -19,15 +19,13 @@ namespace Emby.Naming.Audio
             _options = options;
         }
 
-        public MultiPartResult ParseMultiPart(string path)
+        public bool IsMultiPart(string path)
         {
-            var result = new MultiPartResult();
-
             var filename = Path.GetFileName(path);
 
             if (string.IsNullOrEmpty(filename))
             {
-                return result;
+                return false;
             }
 
             // TODO: Move this logic into options object
@@ -57,12 +55,11 @@ namespace Emby.Naming.Audio
 
                 if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
                 {
-                    result.IsMultiPart = true;
-                    break;
+                    return true;
                 }
             }
 
-            return result;
+            return false;
         }
     }
 }

+ 0 - 26
Emby.Naming/Audio/MultiPartResult.cs

@@ -1,26 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
-namespace Emby.Naming.Audio
-{
-    public class MultiPartResult
-    {
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the part.
-        /// </summary>
-        /// <value>The part.</value>
-        public string Part { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is multi part.
-        /// </summary>
-        /// <value><c>true</c> if this instance is multi part; otherwise, <c>false</c>.</value>
-        public bool IsMultiPart { get; set; }
-    }
-}

+ 1 - 1
Emby.Naming/AudioBook/AudioBookFileInfo.cs

@@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook
         public int? ChapterNumber { get; set; }
 
         /// <summary>
-        /// Gets or sets the type.
+        /// Gets or sets a value indicating whether this instance is a directory.
         /// </summary>
         /// <value>The type.</value>
         public bool IsDirectory { get; set; }

+ 3 - 16
Emby.Naming/AudioBook/AudioBookListResolver.cs

@@ -39,9 +39,7 @@ namespace Emby.Naming.AudioBook
             var stackResult = new StackResolver(_options)
                 .ResolveAudioBooks(metadata);
 
-            var list = new List<AudioBookInfo>();
-
-            foreach (var stack in stackResult.Stacks)
+            foreach (var stack in stackResult)
             {
                 var stackFiles = stack.Files.Select(i => audioBookResolver.Resolve(i, stack.IsDirectoryStack)).ToList();
                 stackFiles.Sort();
@@ -50,20 +48,9 @@ namespace Emby.Naming.AudioBook
                     Files = stackFiles,
                     Name = stack.Name
                 };
-                list.Add(info);
-            }
-
-            // Whatever files are left, just add them
-            /*list.AddRange(remainingFiles.Select(i => new AudioBookInfo
-            {
-                Files = new List<AudioBookFileInfo> { i },
-                Name = i.,
-                Year = i.Year
-            }));*/
-
-            var orderedList = list.OrderBy(i => i.Name);
 
-            return orderedList;
+                yield return info;
+            }
         }
     }
 }

+ 18 - 18
Emby.Naming/Common/EpisodeExpression.cs

@@ -11,6 +11,24 @@ namespace Emby.Naming.Common
         private string _expression;
         private Regex _regex;
 
+        public EpisodeExpression(string expression, bool byDate)
+        {
+            Expression = expression;
+            IsByDate = byDate;
+            DateTimeFormats = Array.Empty<string>();
+            SupportsAbsoluteEpisodeNumbers = true;
+        }
+
+        public EpisodeExpression(string expression)
+            : this(expression, false)
+        {
+        }
+
+        public EpisodeExpression()
+            : this(null)
+        {
+        }
+
         public string Expression
         {
             get => _expression;
@@ -32,23 +50,5 @@ namespace Emby.Naming.Common
         public string[] DateTimeFormats { get; set; }
 
         public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled));
-
-        public EpisodeExpression(string expression, bool byDate)
-        {
-            Expression = expression;
-            IsByDate = byDate;
-            DateTimeFormats = Array.Empty<string>();
-            SupportsAbsoluteEpisodeNumbers = true;
-        }
-
-        public EpisodeExpression(string expression)
-            : this(expression, false)
-        {
-        }
-
-        public EpisodeExpression()
-            : this(null)
-        {
-        }
     }
 }

+ 43 - 40
Emby.Naming/Common/NamingOptions.cs

@@ -11,46 +11,6 @@ namespace Emby.Naming.Common
 {
     public class NamingOptions
     {
-        public string[] AudioFileExtensions { get; set; }
-
-        public string[] AlbumStackingPrefixes { get; set; }
-
-        public string[] SubtitleFileExtensions { get; set; }
-
-        public char[] SubtitleFlagDelimiters { get; set; }
-
-        public string[] SubtitleForcedFlags { get; set; }
-
-        public string[] SubtitleDefaultFlags { get; set; }
-
-        public EpisodeExpression[] EpisodeExpressions { get; set; }
-
-        public string[] EpisodeWithoutSeasonExpressions { get; set; }
-
-        public string[] EpisodeMultiPartExpressions { get; set; }
-
-        public string[] VideoFileExtensions { get; set; }
-
-        public string[] StubFileExtensions { get; set; }
-
-        public string[] AudioBookPartsExpressions { get; set; }
-
-        public StubTypeRule[] StubTypes { get; set; }
-
-        public char[] VideoFlagDelimiters { get; set; }
-
-        public Format3DRule[] Format3DRules { get; set; }
-
-        public string[] VideoFileStackingExpressions { get; set; }
-
-        public string[] CleanDateTimes { get; set; }
-
-        public string[] CleanStrings { get; set; }
-
-        public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
-
-        public ExtraRule[] VideoExtraRules { get; set; }
-
         public NamingOptions()
         {
             VideoFileExtensions = new[]
@@ -681,11 +641,54 @@ namespace Emby.Naming.Common
             Compile();
         }
 
+        public string[] AudioFileExtensions { get; set; }
+
+        public string[] AlbumStackingPrefixes { get; set; }
+
+        public string[] SubtitleFileExtensions { get; set; }
+
+        public char[] SubtitleFlagDelimiters { get; set; }
+
+        public string[] SubtitleForcedFlags { get; set; }
+
+        public string[] SubtitleDefaultFlags { get; set; }
+
+        public EpisodeExpression[] EpisodeExpressions { get; set; }
+
+        public string[] EpisodeWithoutSeasonExpressions { get; set; }
+
+        public string[] EpisodeMultiPartExpressions { get; set; }
+
+        public string[] VideoFileExtensions { get; set; }
+
+        public string[] StubFileExtensions { get; set; }
+
+        public string[] AudioBookPartsExpressions { get; set; }
+
+        public StubTypeRule[] StubTypes { get; set; }
+
+        public char[] VideoFlagDelimiters { get; set; }
+
+        public Format3DRule[] Format3DRules { get; set; }
+
+        public string[] VideoFileStackingExpressions { get; set; }
+
+        public string[] CleanDateTimes { get; set; }
+
+        public string[] CleanStrings { get; set; }
+
+        public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
+
+        public ExtraRule[] VideoExtraRules { get; set; }
+
         public Regex[] VideoFileStackingRegexes { get; private set; }
+
         public Regex[] CleanDateTimeRegexes { get; private set; }
+
         public Regex[] CleanStringRegexes { get; private set; }
 
         public Regex[] EpisodeWithoutSeasonRegexes { get; private set; }
+
         public Regex[] EpisodeMultiPartRegexes { get; private set; }
 
         public void Compile()

+ 1 - 4
Emby.Naming/Emby.Naming.csproj

@@ -4,9 +4,6 @@
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
 
@@ -27,7 +24,7 @@
 
   <!-- Code Analyzers-->
   <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+    <!-- TODO: <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
     <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
     <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
     <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />

+ 3 - 2
Emby.Naming/TV/EpisodePathParser.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 #pragma warning disable SA1600
+#nullable enable
 
 using System;
 using System.Collections.Generic;
@@ -28,7 +29,7 @@ namespace Emby.Naming.TV
                 path += ".mp4";
             }
 
-            EpisodePathParserResult result = null;
+            EpisodePathParserResult? result = null;
 
             foreach (var expression in _options.EpisodeExpressions)
             {
@@ -136,7 +137,7 @@ namespace Emby.Naming.TV
                         // It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
                         int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
                         if (nextIndex >= name.Length
-                            || "0123456789iIpP".IndexOf(name[nextIndex]) == -1)
+                            || !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal))
                         {
                             if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
                             {

+ 6 - 14
Emby.Naming/TV/EpisodeResolver.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 #pragma warning disable SA1600
+#nullable enable
 
 using System;
 using System.IO;
@@ -18,7 +19,7 @@ namespace Emby.Naming.TV
             _options = options;
         }
 
-        public EpisodeInfo Resolve(
+        public EpisodeInfo? Resolve(
             string path,
             bool isDirectory,
             bool? isNamed = null,
@@ -26,14 +27,9 @@ namespace Emby.Naming.TV
             bool? supportsAbsoluteNumbers = null,
             bool fillExtendedInfo = true)
         {
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
-
             bool isStub = false;
-            string container = null;
-            string stubType = null;
+            string? container = null;
+            string? stubType = null;
 
             if (!isDirectory)
             {
@@ -41,17 +37,13 @@ namespace Emby.Naming.TV
                 // Check supported extensions
                 if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
                 {
-                    var stubResult = StubResolver.ResolveFile(path, _options);
-
-                    isStub = stubResult.IsStub;
-
                     // It's not supported. Check stub extensions
-                    if (!isStub)
+                    if (!StubResolver.TryResolveFile(path, _options, out stubType))
                     {
                         return null;
                     }
 
-                    stubType = stubResult.StubType;
+                    isStub = true;
                 }
 
                 container = extension.TrimStart('.');

+ 20 - 19
Emby.Naming/TV/SeasonPathParser.cs

@@ -8,9 +8,24 @@ using System.Linq;
 
 namespace Emby.Naming.TV
 {
-    public class SeasonPathParser
+    public static class SeasonPathParser
     {
-        public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
+        /// <summary>
+        /// A season folder must contain one of these somewhere in the name.
+        /// </summary>
+        private static readonly string[] _seasonFolderNames =
+        {
+            "season",
+            "sæson",
+            "temporada",
+            "saison",
+            "staffel",
+            "series",
+            "сезон",
+            "stagione"
+        };
+
+        public static SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
         {
             var result = new SeasonPathParserResult();
 
@@ -27,21 +42,6 @@ namespace Emby.Naming.TV
             return result;
         }
 
-        /// <summary>
-        /// A season folder must contain one of these somewhere in the name.
-        /// </summary>
-        private static readonly string[] _seasonFolderNames =
-        {
-            "season",
-            "sæson",
-            "temporada",
-            "saison",
-            "staffel",
-            "series",
-            "сезон",
-            "stagione"
-        };
-
         /// <summary>
         /// Gets the season number from path.
         /// </summary>
@@ -150,6 +150,7 @@ namespace Emby.Naming.TV
                         {
                             numericStart = i;
                         }
+
                         length++;
                     }
                 }
@@ -161,11 +162,11 @@ namespace Emby.Naming.TV
                 }
 
                 var currentChar = path[i];
-                if (currentChar.Equals('('))
+                if (currentChar == '(')
                 {
                     hasOpenParenth = true;
                 }
-                else if (currentChar.Equals(')'))
+                else if (currentChar == ')')
                 {
                     hasOpenParenth = false;
                 }

+ 7 - 14
Emby.Naming/Video/StackResolver.cs

@@ -20,7 +20,7 @@ namespace Emby.Naming.Video
             _options = options;
         }
 
-        public StackResult ResolveDirectories(IEnumerable<string> files)
+        public IEnumerable<FileStack> ResolveDirectories(IEnumerable<string> files)
         {
             return Resolve(files.Select(i => new FileSystemMetadata
             {
@@ -29,7 +29,7 @@ namespace Emby.Naming.Video
             }));
         }
 
-        public StackResult ResolveFiles(IEnumerable<string> files)
+        public IEnumerable<FileStack> ResolveFiles(IEnumerable<string> files)
         {
             return Resolve(files.Select(i => new FileSystemMetadata
             {
@@ -38,9 +38,8 @@ namespace Emby.Naming.Video
             }));
         }
 
-        public StackResult ResolveAudioBooks(IEnumerable<FileSystemMetadata> files)
+        public IEnumerable<FileStack> ResolveAudioBooks(IEnumerable<FileSystemMetadata> files)
         {
-            var result = new StackResult();
             foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName)))
             {
                 var stack = new FileStack()
@@ -58,20 +57,16 @@ namespace Emby.Naming.Video
                     stack.Files.Add(file.FullName);
                 }
 
-                result.Stacks.Add(stack);
+                yield return stack;
             }
-
-            return result;
         }
 
-        public StackResult Resolve(IEnumerable<FileSystemMetadata> files)
+        public IEnumerable<FileStack> Resolve(IEnumerable<FileSystemMetadata> files)
         {
-            var result = new StackResult();
-
             var resolver = new VideoResolver(_options);
 
             var list = files
-                .Where(i => i.IsDirectory || (resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName)))
+                .Where(i => i.IsDirectory || resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName))
                 .OrderBy(i => i.FullName)
                 .ToList();
 
@@ -191,14 +186,12 @@ namespace Emby.Naming.Video
 
                     if (stack.Files.Count > 1)
                     {
-                        result.Stacks.Add(stack);
+                        yield return stack;
                         i += stack.Files.Count - 1;
                         break;
                     }
                 }
             }
-
-            return result;
         }
 
         private string GetRegexInput(FileSystemMetadata file)

+ 0 - 17
Emby.Naming/Video/StackResult.cs

@@ -1,17 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
-using System.Collections.Generic;
-
-namespace Emby.Naming.Video
-{
-    public class StackResult
-    {
-        public List<FileStack> Stacks { get; set; }
-
-        public StackResult()
-        {
-            Stacks = new List<FileStack>();
-        }
-    }
-}

+ 9 - 11
Emby.Naming/Video/StubResolver.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 #pragma warning disable SA1600
+#nullable enable
 
 using System;
 using System.IO;
@@ -10,25 +11,22 @@ namespace Emby.Naming.Video
 {
     public static class StubResolver
     {
-        public static StubResult ResolveFile(string path, NamingOptions options)
+        public static bool TryResolveFile(string path, NamingOptions options, out string? stubType)
         {
+            stubType = default;
+
             if (path == null)
             {
-                return default;
+                return false;
             }
 
             var extension = Path.GetExtension(path);
 
             if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
             {
-                return default;
+                return false;
             }
 
-            var result = new StubResult()
-            {
-                IsStub = true
-            };
-
             path = Path.GetFileNameWithoutExtension(path);
             var token = Path.GetExtension(path).TrimStart('.');
 
@@ -36,12 +34,12 @@ namespace Emby.Naming.Video
             {
                 if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase))
                 {
-                    result.StubType = rule.StubType;
-                    break;
+                    stubType = rule.StubType;
+                    return true;
                 }
             }
 
-            return result;
+            return true;
         }
     }
 }

+ 1 - 1
Emby.Naming/Video/VideoFileInfo.cs

@@ -68,7 +68,7 @@ namespace Emby.Naming.Video
         public string StubType { get; set; }
 
         /// <summary>
-        /// Gets or sets the type.
+        /// Gets or sets a value indicating whether this instance is a directory.
         /// </summary>
         /// <value>The type.</value>
         public bool IsDirectory { get; set; }

+ 11 - 7
Emby.Naming/Video/VideoInfo.cs

@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 
 namespace Emby.Naming.Video
@@ -10,11 +11,14 @@ namespace Emby.Naming.Video
         /// <summary>
         /// Initializes a new instance of the <see cref="VideoInfo" /> class.
         /// </summary>
-        public VideoInfo()
+        /// <param name="name">The name.</param>
+        public VideoInfo(string name)
         {
-            Files = new List<VideoFileInfo>();
-            Extras = new List<VideoFileInfo>();
-            AlternateVersions = new List<VideoFileInfo>();
+            Name = name;
+
+            Files = Array.Empty<VideoFileInfo>();
+            Extras = Array.Empty<VideoFileInfo>();
+            AlternateVersions = Array.Empty<VideoFileInfo>();
         }
 
         /// <summary>
@@ -33,18 +37,18 @@ namespace Emby.Naming.Video
         /// Gets or sets the files.
         /// </summary>
         /// <value>The files.</value>
-        public List<VideoFileInfo> Files { get; set; }
+        public IReadOnlyList<VideoFileInfo> Files { get; set; }
 
         /// <summary>
         /// Gets or sets the extras.
         /// </summary>
         /// <value>The extras.</value>
-        public List<VideoFileInfo> Extras { get; set; }
+        public IReadOnlyList<VideoFileInfo> Extras { get; set; }
 
         /// <summary>
         /// Gets or sets the alternate versions.
         /// </summary>
         /// <value>The alternate versions.</value>
-        public List<VideoFileInfo> AlternateVersions { get; set; }
+        public IReadOnlyList<VideoFileInfo> AlternateVersions { get; set; }
     }
 }

+ 26 - 17
Emby.Naming/Video/VideoListResolver.cs

@@ -41,20 +41,19 @@ namespace Emby.Naming.Video
                 });
 
             var stackResult = new StackResolver(_options)
-                .Resolve(nonExtras);
+                .Resolve(nonExtras).ToList();
 
             var remainingFiles = videoInfos
-                .Where(i => !stackResult.Stacks.Any(s => s.ContainsFile(i.Path, i.IsDirectory)))
+                .Where(i => !stackResult.Any(s => s.ContainsFile(i.Path, i.IsDirectory)))
                 .ToList();
 
             var list = new List<VideoInfo>();
 
-            foreach (var stack in stackResult.Stacks)
+            foreach (var stack in stackResult)
             {
-                var info = new VideoInfo
+                var info = new VideoInfo(stack.Name)
                 {
-                    Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList(),
-                    Name = stack.Name
+                    Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList()
                 };
 
                 info.Year = info.Files[0].Year;
@@ -85,10 +84,9 @@ namespace Emby.Naming.Video
 
             foreach (var media in standaloneMedia)
             {
-                var info = new VideoInfo
+                var info = new VideoInfo(media.Name)
                 {
-                    Files = new List<VideoFileInfo> { media },
-                    Name = media.Name
+                    Files = new List<VideoFileInfo> { media }
                 };
 
                 info.Year = info.Files[0].Year;
@@ -128,7 +126,8 @@ namespace Emby.Naming.Video
                             .Except(extras)
                             .ToList();
 
-                        info.Extras.AddRange(extras);
+                        extras.AddRange(info.Extras);
+                        info.Extras = extras;
                     }
                 }
 
@@ -141,7 +140,8 @@ namespace Emby.Naming.Video
                     .Except(extrasByFileName)
                     .ToList();
 
-                info.Extras.AddRange(extrasByFileName);
+                extrasByFileName.AddRange(info.Extras);
+                info.Extras = extrasByFileName;
             }
 
             // If there's only one video, accept all trailers
@@ -152,7 +152,8 @@ namespace Emby.Naming.Video
                     .Where(i => i.ExtraType == ExtraType.Trailer)
                     .ToList();
 
-                list[0].Extras.AddRange(trailers);
+                trailers.AddRange(list[0].Extras);
+                list[0].Extras = trailers;
 
                 remainingFiles = remainingFiles
                     .Except(trailers)
@@ -160,14 +161,13 @@ namespace Emby.Naming.Video
             }
 
             // Whatever files are left, just add them
-            list.AddRange(remainingFiles.Select(i => new VideoInfo
+            list.AddRange(remainingFiles.Select(i => new VideoInfo(i.Name)
             {
                 Files = new List<VideoFileInfo> { i },
-                Name = i.Name,
                 Year = i.Year
             }));
 
-            return list.OrderBy(i => i.Name);
+            return list;
         }
 
         private IEnumerable<VideoInfo> GetVideosGroupedByVersion(List<VideoInfo> videos)
@@ -191,9 +191,18 @@ namespace Emby.Naming.Video
 
                 list.Add(ordered[0]);
 
-                list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList();
+                var alternateVersionsLen = ordered.Count - 1;
+                var alternateVersions = new VideoFileInfo[alternateVersionsLen];
+                for (int i = 0; i < alternateVersionsLen; i++)
+                {
+                    alternateVersions[i] = ordered[i + 1].Files[0];
+                }
+
+                list[0].AlternateVersions = alternateVersions;
                 list[0].Name = folderName;
-                list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras));
+                var extras = ordered.Skip(1).SelectMany(i => i.Extras).ToList();
+                extras.AddRange(list[0].Extras);
+                list[0].Extras = extras;
 
                 return list;
             }

+ 9 - 12
Emby.Naming/Video/VideoResolver.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 #pragma warning disable SA1600
+#nullable enable
 
 using System;
 using System.IO;
@@ -22,7 +23,7 @@ namespace Emby.Naming.Video
         /// </summary>
         /// <param name="path">The path.</param>
         /// <returns>VideoFileInfo.</returns>
-        public VideoFileInfo ResolveDirectory(string path)
+        public VideoFileInfo? ResolveDirectory(string path)
         {
             return Resolve(path, true);
         }
@@ -32,7 +33,7 @@ namespace Emby.Naming.Video
         /// </summary>
         /// <param name="path">The path.</param>
         /// <returns>VideoFileInfo.</returns>
-        public VideoFileInfo ResolveFile(string path)
+        public VideoFileInfo? ResolveFile(string path)
         {
             return Resolve(path, false);
         }
@@ -42,10 +43,10 @@ namespace Emby.Naming.Video
         /// </summary>
         /// <param name="path">The path.</param>
         /// <param name="isDirectory">if set to <c>true</c> [is folder].</param>
-        /// <param name="parseName">Whether or not the name should be parsed for info</param>
+        /// <param name="parseName">Whether or not the name should be parsed for info.</param>
         /// <returns>VideoFileInfo.</returns>
         /// <exception cref="ArgumentNullException"><c>path</c> is <c>null</c>.</exception>
-        public VideoFileInfo Resolve(string path, bool isDirectory, bool parseName = true)
+        public VideoFileInfo? Resolve(string path, bool isDirectory, bool parseName = true)
         {
             if (string.IsNullOrEmpty(path))
             {
@@ -53,8 +54,8 @@ namespace Emby.Naming.Video
             }
 
             bool isStub = false;
-            string container = null;
-            string stubType = null;
+            string? container = null;
+            string? stubType = null;
 
             if (!isDirectory)
             {
@@ -63,17 +64,13 @@ namespace Emby.Naming.Video
                 // Check supported extensions
                 if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
                 {
-                    var stubResult = StubResolver.ResolveFile(path, _options);
-
-                    isStub = stubResult.IsStub;
-
                     // It's not supported. Check stub extensions
-                    if (!isStub)
+                    if (!StubResolver.TryResolveFile(path, _options, out stubType))
                     {
                         return null;
                     }
 
-                    stubType = stubResult.StubType;
+                    isStub = true;
                 }
 
                 container = extension.TrimStart('.');

+ 1 - 1
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -2384,7 +2384,7 @@ namespace Emby.Server.Implementations.Library
 
         public int? GetSeasonNumberFromPath(string path)
         {
-            return new SeasonPathParser().Parse(path, true, true).SeasonNumber;
+            return SeasonPathParser.Parse(path, true, true).SeasonNumber;
         }
 
         public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)

+ 6 - 14
Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs

@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
         }
 
         /// <summary>
-        /// Determine if the supplied file data points to a music album
+        /// Determine if the supplied file data points to a music album.
         /// </summary>
         public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions)
         {
@@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
         }
 
         /// <summary>
-        /// Determine if the supplied resolve args should be considered a music album
+        /// Determine if the supplied resolve args should be considered a music album.
         /// </summary>
         /// <param name="args">The args.</param>
         /// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
@@ -104,7 +104,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
         }
 
         /// <summary>
-        /// Determine if the supplied list contains what we should consider music
+        /// Determine if the supplied list contains what we should consider music.
         /// </summary>
         private bool ContainsMusic(
             IEnumerable<FileSystemMetadata> list,
@@ -118,6 +118,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
             var discSubfolderCount = 0;
             var notMultiDisc = false;
 
+            var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
+            var parser = new AlbumParser(namingOptions);
             foreach (var fileSystemInfo in list)
             {
                 if (fileSystemInfo.IsDirectory)
@@ -134,7 +136,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
 
                         if (hasMusic)
                         {
-                            if (IsMultiDiscFolder(path, libraryOptions))
+                            if (parser.IsMultiPart(path))
                             {
                                 logger.LogDebug("Found multi-disc folder: " + path);
                                 discSubfolderCount++;
@@ -165,15 +167,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
 
             return discSubfolderCount > 0;
         }
-
-        private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions)
-        {
-            var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
-
-            var parser = new AlbumParser(namingOptions);
-            var result = parser.ParseMultiPart(path);
-
-            return result.IsMultiPart;
-        }
     }
 }

+ 35 - 38
Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -21,6 +21,28 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
     /// </summary>
     public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
     {
+        private string[] _validCollectionTypes = new[]
+        {
+                CollectionType.Movies,
+                CollectionType.HomeVideos,
+                CollectionType.MusicVideos,
+                CollectionType.Movies,
+                CollectionType.Photos
+        };
+
+        private readonly IImageProcessor _imageProcessor;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MovieResolver"/> class.
+        /// </summary>
+        /// <param name="libraryManager">The library manager.</param>
+        /// <param name="imageProcessor">The image processor.</param>
+        public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
+            : base(libraryManager)
+        {
+            _imageProcessor = imageProcessor;
+        }
+
         /// <summary>
         /// Gets the priority.
         /// </summary>
@@ -144,7 +166,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
 
             foreach (var video in resolverResult)
             {
-                var firstVideo = video.Files.First();
+                var firstVideo = video.Files[0];
 
                 var videoItem = new T
                 {
@@ -230,7 +252,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                     // Owned items will be caught by the plain video resolver
                     if (args.Parent == null)
                     {
-                        //return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
+                        // return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
                         return null;
                     }
 
@@ -275,7 +297,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             {
                 item = ResolveVideo<Movie>(args, true);
             }
-
             else if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
                 string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
             {
@@ -319,7 +340,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         {
             if (item is Movie || item is MusicVideo)
             {
-                //we need to only look at the name of this actual item (not parents)
+                // We need to only look at the name of this actual item (not parents)
                 var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.ContainingFolderPath);
 
                 if (!string.IsNullOrEmpty(justName))
@@ -347,9 +368,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         }
 
         /// <summary>
-        /// Finds a movie based on a child file system entries
+        /// Finds a movie based on a child file system entries.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
         /// <returns>Movie.</returns>
         private T FindMovie<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName)
             where T : Video, new()
@@ -377,6 +397,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                         Set3DFormat(movie);
                         return movie;
                     }
+
                     if (IsBluRayDirectory(child.FullName, filename, directoryService))
                     {
                         var movie = new T
@@ -407,9 +428,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             }
 
             // TODO: Allow GetMultiDiscMovie in here
-            const bool supportsMultiVersion = true;
+            const bool SupportsMultiVersion = true;
 
-            var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
+            var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, SupportsMultiVersion, collectionType, parseName) ??
                 new MultiItemResolverResult();
 
             if (result.Items.Count == 1)
@@ -437,7 +458,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         /// <summary>
         /// Gets the multi disc movie.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
         /// <param name="multiDiscFolders">The folders.</param>
         /// <param name="directoryService">The directory service.</param>
         /// <returns>``0.</returns>
@@ -451,7 +471,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 var subFileEntries = directoryService.GetFileSystemEntries(i);
 
                 var subfolders = subFileEntries
-                 .Where(e => e.IsDirectory)
+                    .Where(e => e.IsDirectory)
                     .ToList();
 
                 if (subfolders.Any(s => IsDvdDirectory(s.FullName, s.Name, directoryService)))
@@ -459,6 +479,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                     videoTypes.Add(VideoType.Dvd);
                     return true;
                 }
+
                 if (subfolders.Any(s => IsBluRayDirectory(s.FullName, s.Name, directoryService)))
                 {
                     videoTypes.Add(VideoType.BluRay);
@@ -476,7 +497,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 }
 
                 return false;
-
             }).OrderBy(i => i).ToList();
 
             // If different video types were found, don't allow this
@@ -491,11 +511,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             }
 
             var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
-            var resolver = new StackResolver(namingOptions);
 
-            var result = resolver.ResolveDirectories(folderPaths);
+            var result = new StackResolver(namingOptions).ResolveDirectories(folderPaths).ToList();
 
-            if (result.Stacks.Count != 1)
+            if (result.Count != 1)
             {
                 return null;
             }
@@ -508,7 +527,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
 
                 VideoType = videoTypes[0],
 
-                Name = result.Stacks[0].Name
+                Name = result[0].Name
             };
 
             SetIsoType(returnVideo);
@@ -516,15 +535,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             return returnVideo;
         }
 
-        private string[] ValidCollectionTypes = new[]
-        {
-                CollectionType.Movies,
-                CollectionType.HomeVideos,
-                CollectionType.MusicVideos,
-                CollectionType.Movies,
-                CollectionType.Photos
-        };
-
         private bool IsInvalid(Folder parent, string collectionType)
         {
             if (parent != null)
@@ -540,20 +550,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 return false;
             }
 
-            return !ValidCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase);
-        }
-
-        private IImageProcessor _imageProcessor;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="MovieResolver"/> class.
-        /// </summary>
-        /// <param name="libraryManager">The library manager.</param>
-        /// <param name="imageProcessor">The image processor.</param>
-        public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
-            : base(libraryManager)
-        {
-            _imageProcessor = imageProcessor;
+            return !_validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase);
         }
     }
 }

+ 10 - 11
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -9,17 +9,12 @@ using Microsoft.Extensions.Logging;
 namespace Emby.Server.Implementations.Library.Resolvers.TV
 {
     /// <summary>
-    /// Class SeasonResolver
+    /// Class SeasonResolver.
     /// </summary>
     public class SeasonResolver : FolderResolver<Season>
     {
-        /// <summary>
-        /// The _config
-        /// </summary>
         private readonly IServerConfigurationManager _config;
-
         private readonly ILibraryManager _libraryManager;
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         private readonly ILocalizationManager _localization;
         private readonly ILogger _logger;
 
@@ -45,14 +40,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
         /// <returns>Season.</returns>
         protected override Season Resolve(ItemResolveArgs args)
         {
-            if (args.Parent is Series && args.IsDirectory)
+            if (args.Parent is Series series && args.IsDirectory)
             {
                 var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
-                var series = ((Series)args.Parent);
 
                 var path = args.Path;
 
-                var seasonParserResult = new SeasonPathParser().Parse(path, true, true);
+                var seasonParserResult = SeasonPathParser.Parse(path, true, true);
 
                 var season = new Season
                 {
@@ -74,7 +68,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                     {
                         if (episodeInfo.EpisodeNumber.HasValue && episodeInfo.SeasonNumber.HasValue)
                         {
-                            _logger.LogDebug("Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
+                            _logger.LogDebug(
+                                "Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
                                 path,
                                 episodeInfo.SeasonNumber.Value,
                                 episodeInfo.EpisodeNumber.Value);
@@ -90,7 +85,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
 
                     season.Name = seasonNumber == 0 ?
                         args.LibraryOptions.SeasonZeroDisplayName :
-                        string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture), args.GetLibraryOptions().PreferredMetadataLanguage);
+                        string.Format(
+                            CultureInfo.InvariantCulture,
+                            _localization.GetLocalizedString("NameSeasonNumber"),
+                            seasonNumber,
+                            args.GetLibraryOptions().PreferredMetadataLanguage);
 
                 }
 

+ 1 - 1
Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -203,7 +203,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
         /// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
         private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager)
         {
-            var seasonNumber = new SeasonPathParser().Parse(path, isTvContentType, isTvContentType).SeasonNumber;
+            var seasonNumber = SeasonPathParser.Parse(path, isTvContentType, isTvContentType).SeasonNumber;
 
             return seasonNumber.HasValue;
         }

+ 0 - 3
Jellyfin.Server/Jellyfin.Server.csproj

@@ -6,9 +6,6 @@
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-  </PropertyGroup>
-
-  <PropertyGroup>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <Nullable>enable</Nullable>
   </PropertyGroup>

+ 1 - 1
tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs

@@ -60,7 +60,7 @@ namespace Jellyfin.Naming.Tests.Music
         {
             var parser = new AlbumParser(new NamingOptions());
 
-            return parser.ParseMultiPart(path).IsMultiPart;
+            return parser.IsMultiPart(path);
         }
     }
 }

+ 1 - 2
tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs

@@ -103,8 +103,7 @@ namespace Jellyfin.Naming.Tests.TV
 
         private int? GetSeasonNumberFromPath(string path)
         {
-            var result = new SeasonPathParser()
-                .Parse(path, true, true);
+            var result = SeasonPathParser.Parse(path, true, true);
 
             return result.SeasonNumber;
         }

+ 65 - 65
tests/Jellyfin.Naming.Tests/Video/StackTests.cs

@@ -1,4 +1,5 @@
-using Emby.Naming.Common;
+using System.Linq;
+using Emby.Naming.Common;
 using Emby.Naming.Video;
 using MediaBrowser.Model.IO;
 using Xunit;
@@ -21,10 +22,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
-            TestStackInfo(result.Stacks[0], "Bad Boys (2006)", 4);
+            Assert.Single(result);
+            TestStackInfo(result[0], "Bad Boys (2006)", 4);
         }
 
         [Fact]
@@ -38,9 +39,9 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Empty(result.Stacks);
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -54,9 +55,9 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Empty(result.Stacks);
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -70,9 +71,9 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Empty(result.Stacks);
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -86,9 +87,9 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Empty(result.Stacks);
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -102,9 +103,8 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
-
-            Assert.Empty(result.Stacks);
+            var result = resolver.ResolveFiles(files).ToList();
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -119,9 +119,9 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Empty(result.Stacks);
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -135,10 +135,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
-            TestStackInfo(result.Stacks[0], "300 2006", 2);
+            Assert.Single(result);
+            TestStackInfo(result[0], "300 2006", 2);
         }
 
         [Fact]
@@ -155,10 +155,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
-            TestStackInfo(result.Stacks[0], "Bad Boys (2006).stv.unrated.multi.1080p.bluray.x264-rough", 4);
+            Assert.Single(result);
+            TestStackInfo(result[0], "Bad Boys (2006).stv.unrated.multi.1080p.bluray.x264-rough", 4);
         }
 
         [Fact]
@@ -175,9 +175,9 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Empty(result.Stacks);
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -194,10 +194,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
-            TestStackInfo(result.Stacks[0], "300 (2006)", 4);
+            Assert.Single(result);
+            TestStackInfo(result[0], "300 (2006)", 4);
         }
 
         [Fact]
@@ -214,10 +214,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
-            TestStackInfo(result.Stacks[0], "Bad Boys (2006)", 3);
+            Assert.Single(result);
+            TestStackInfo(result[0], "Bad Boys (2006)", 3);
         }
 
         [Fact]
@@ -238,11 +238,11 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Equal(2, result.Stacks.Count);
-            TestStackInfo(result.Stacks[1], "Bad Boys (2006)", 4);
-            TestStackInfo(result.Stacks[0], "300 (2006)", 3);
+            Assert.Equal(2, result.Count);
+            TestStackInfo(result[1], "Bad Boys (2006)", 4);
+            TestStackInfo(result[0], "300 (2006)", 3);
         }
 
         [Fact]
@@ -256,10 +256,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveDirectories(files);
+            var result = resolver.ResolveDirectories(files).ToList();
 
-            Assert.Single(result.Stacks);
-            TestStackInfo(result.Stacks[0], "blah blah", 2);
+            Assert.Single(result);
+            TestStackInfo(result[0], "blah blah", 2);
         }
 
         [Fact]
@@ -275,11 +275,11 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
+            Assert.Single(result);
 
-            TestStackInfo(result.Stacks[0], "300", 3);
+            TestStackInfo(result[0], "300", 3);
         }
 
         [Fact]
@@ -297,12 +297,12 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Equal(2, result.Stacks.Count);
+            Assert.Equal(2, result.Count);
 
-            TestStackInfo(result.Stacks[0], "300", 2);
-            TestStackInfo(result.Stacks[1], "Avengers", 3);
+            TestStackInfo(result[0], "300", 2);
+            TestStackInfo(result[1], "Avengers", 3);
         }
 
         [Fact]
@@ -328,13 +328,13 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Equal(3, result.Stacks.Count);
+            Assert.Equal(3, result.Count);
 
-            TestStackInfo(result.Stacks[0], "300 (2006)", 4);
-            TestStackInfo(result.Stacks[1], "300", 3);
-            TestStackInfo(result.Stacks[2], "Bad Boys (2006)", 4);
+            TestStackInfo(result[0], "300 (2006)", 4);
+            TestStackInfo(result[1], "300", 3);
+            TestStackInfo(result[2], "Bad Boys (2006)", 4);
         }
 
         [Fact]
@@ -354,11 +354,11 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
+            Assert.Single(result);
 
-            TestStackInfo(result.Stacks[0], "300 (2006)", 4);
+            TestStackInfo(result[0], "300 (2006)", 4);
         }
 
         [Fact]
@@ -375,11 +375,11 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.Resolve(files);
+            var result = resolver.Resolve(files).ToList();
 
-            Assert.Equal(2, result.Stacks.Count);
-            TestStackInfo(result.Stacks[0], "300 (2006)", 3);
-            TestStackInfo(result.Stacks[1], "Bad Boys (2006)", 2);
+            Assert.Equal(2, result.Count);
+            TestStackInfo(result[0], "300 (2006)", 3);
+            TestStackInfo(result[1], "Bad Boys (2006)", 2);
         }
 
         [Fact]
@@ -397,9 +397,9 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Empty(result.Stacks);
+            Assert.Empty(result);
         }
 
         [Fact]
@@ -414,10 +414,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveFiles(files);
+            var result = resolver.ResolveFiles(files).ToList();
 
-            Assert.Single(result.Stacks);
-            Assert.Equal(2, result.Stacks[0].Files.Count);
+            Assert.Single(result);
+            Assert.Equal(2, result[0].Files.Count);
         }
 
         [Fact]
@@ -432,10 +432,10 @@ namespace Jellyfin.Naming.Tests.Video
 
             var resolver = GetResolver();
 
-            var result = resolver.ResolveDirectories(files);
+            var result = resolver.ResolveDirectories(files).ToList();
 
-            Assert.Single(result.Stacks);
-            Assert.Equal(2, result.Stacks[0].Files.Count);
+            Assert.Single(result);
+            Assert.Equal(2, result[0].Files.Count);
         }
 
         private void TestStackInfo(FileStack stack, string name, int fileCount)

+ 5 - 5
tests/Jellyfin.Naming.Tests/Video/StubTests.cs

@@ -38,17 +38,17 @@ namespace Jellyfin.Naming.Tests.Video
         {
             var options = new NamingOptions();
 
-            var resultStubType = StubResolver.ResolveFile(path, options);
+            var isStubResult = StubResolver.TryResolveFile(path, options, out var stubTypeResult);
 
-            Assert.Equal(isStub, resultStubType.IsStub);
+            Assert.Equal(isStub, isStubResult);
 
-            if (stubType == null)
+            if (isStub)
             {
-                Assert.Null(resultStubType.StubType);
+                Assert.Equal(stubType, stubTypeResult);
             }
             else
             {
-                Assert.Equal(stubType, resultStubType.StubType, true);
+                Assert.Null(stubTypeResult);
             }
         }
     }