Browse Source

Added multi-disc movie support

Luke Pulverenti 12 years ago
parent
commit
c5b00dec8e

+ 4 - 4
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.Entities.Movies
         {
             SpecialFeatureIds = new List<Guid>();
         }
-        
+
         /// <summary>
         /// Should be overridden to return the proper folder where metadata lives
         /// </summary>
@@ -30,7 +30,7 @@ namespace MediaBrowser.Controller.Entities.Movies
         {
             get
             {
-                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso ? System.IO.Path.GetDirectoryName(Path) : Path;
+                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart ? System.IO.Path.GetDirectoryName(Path) : Path;
             }
         }
 
@@ -51,7 +51,7 @@ namespace MediaBrowser.Controller.Entities.Movies
         {
             get
             {
-                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso;
+                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart;
             }
         }
 
@@ -88,7 +88,7 @@ namespace MediaBrowser.Controller.Entities.Movies
 
             return itemsChanged || results.Contains(true);
         }
-        
+
         /// <summary>
         /// Loads the special features.
         /// </summary>

+ 2 - 2
MediaBrowser.Controller/Entities/MusicVideo.cs

@@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso ? System.IO.Path.GetDirectoryName(Path) : Path;
+                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart ? System.IO.Path.GetDirectoryName(Path) : Path;
             }
         }
 
@@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso;
+                return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart;
             }
         }
     }

+ 25 - 19
MediaBrowser.Controller/Entities/Video.cs

@@ -86,7 +86,7 @@ namespace MediaBrowser.Controller.Entities
 
             var allFiles = Directory.EnumerateFiles(rootPath, "*", SearchOption.AllDirectories).ToList();
 
-            return PlayableStreamFileNames.Select(name => allFiles.FirstOrDefault(f => string.Equals(System.IO.Path.GetFileName(f), name, System.StringComparison.OrdinalIgnoreCase)))
+            return PlayableStreamFileNames.Select(name => allFiles.FirstOrDefault(f => string.Equals(System.IO.Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
                 .Where(f => !string.IsNullOrEmpty(f))
                 .ToList();
         }
@@ -176,32 +176,38 @@ namespace MediaBrowser.Controller.Entities
                 return new List<Video>();
             }
 
-            ItemResolveArgs resolveArgs;
+            IEnumerable<FileSystemInfo> files;
 
-            try
+            if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
             {
-                resolveArgs = ResolveArgs;
+                files = new DirectoryInfo(System.IO.Path.GetDirectoryName(Path))
+                    .EnumerateDirectories()
+                    .Where(i => !string.Equals(i.FullName, Path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsMultiPartFile(i.Name));
             }
-            catch (IOException ex)
+            else
             {
-                Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
-                return new List<Video>();
-            }
-
-            if (!resolveArgs.IsDirectory)
-            {
-                return new List<Video>();
-            }
+                ItemResolveArgs resolveArgs;
 
-            var files = resolveArgs.FileSystemChildren.Where(i =>
-            {
-                if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+                try
+                {
+                    resolveArgs = ResolveArgs;
+                }
+                catch (IOException ex)
                 {
-                    return false;
+                    Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
+                    return new List<Video>();
                 }
 
-                return !string.Equals(i.FullName, Path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.FullName);
-            });
+                files = resolveArgs.FileSystemChildren.Where(i =>
+                {
+                    if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+                    {
+                        return false;
+                    }
+
+                    return !string.Equals(i.FullName, Path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.Name);
+                });
+            }
 
             return LibraryManager.ResolvePaths<Video>(files, null).Select(video =>
             {

+ 6 - 2
MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs

@@ -48,7 +48,11 @@ namespace MediaBrowser.Controller.Resolvers
 
         private static readonly Regex MultiFileRegex = new Regex(
             @"(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck]|d)[ _.-]*[0-9]+)(.*?)(\.[^.]+)$",
-            RegexOptions.Compiled);
+            RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+        private static readonly Regex MultiFolderRegex = new Regex(
+            @"(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck]|d)[ _.-]*[0-9]+)$",
+            RegexOptions.Compiled | RegexOptions.IgnoreCase);
 
         /// <summary>
         /// Determines whether [is multi part file] [the specified path].
@@ -57,7 +61,7 @@ namespace MediaBrowser.Controller.Resolvers
         /// <returns><c>true</c> if [is multi part file] [the specified path]; otherwise, <c>false</c>.</returns>
         public static bool IsMultiPartFile(string path)
         {
-            return MultiFileRegex.Match(path).Success;
+            return MultiFileRegex.Match(path).Success || MultiFolderRegex.Match(path).Success;
         }
 
         /// <summary>

+ 88 - 39
MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -50,24 +50,21 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
             if (args.IsDirectory)
             {
                 // Avoid expensive tests against VF's and all their children by not allowing this
-                if (args.Parent == null || args.Parent.IsRoot)
+                if (args.Parent != null)
                 {
-                    return null;
-                }
-
-                // If the parent is not a boxset, the only other allowed parent type is Folder		
-                if (!(args.Parent is BoxSet))
-                {
-                    if (args.Parent.GetType() != typeof(Folder))
+                    if (args.Parent.IsRoot)
                     {
                         return null;
                     }
-                }
 
-                // Optimization to avoid running all these tests against Top folders
-                if (args.Parent != null && args.Parent.IsRoot)
-                {
-                    return null;
+                    // If the parent is not a boxset, the only other allowed parent type is Folder		
+                    if (!(args.Parent is BoxSet))
+                    {
+                        if (args.Parent.GetType() != typeof(Folder))
+                        {
+                            return null;
+                        }
+                    }
                 }
 
                 // Since the looping is expensive, this is an optimization to help us avoid it
@@ -76,16 +73,20 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                     return null;
                 }
 
+                // A shortcut to help us resolve faster in some cases
+                var isKnownMovie = args.ContainsMetaFileByName("movie.xml") || args.ContainsMetaFileByName("tmdb3.json") ||
+                                   args.Path.IndexOf("[tmdbid", StringComparison.OrdinalIgnoreCase) != -1;
+
                 if (args.Path.IndexOf("[trailers]", StringComparison.OrdinalIgnoreCase) != -1)
                 {
-                    return FindMovie<Trailer>(args);
+                    return FindMovie<Trailer>(args.Path, args.FileSystemChildren, isKnownMovie);
                 }
                 if (args.Path.IndexOf("[musicvideos]", StringComparison.OrdinalIgnoreCase) != -1)
                 {
-                    return FindMovie<MusicVideo>(args);
+                    return FindMovie<MusicVideo>(args.Path, args.FileSystemChildren, isKnownMovie);
                 }
 
-                return FindMovie<Movie>(args);
+                return FindMovie<Movie>(args.Path, args.FileSystemChildren, isKnownMovie);
             }
 
             return null;
@@ -123,18 +124,20 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
         /// <summary>
         /// Finds a movie based on a child file system entries
         /// </summary>
-        /// <param name="args">The args.</param>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="path">The path.</param>
+        /// <param name="fileSystemEntries">The file system entries.</param>
+        /// <param name="isKnownMovie">if set to <c>true</c> [is known movie].</param>
         /// <returns>Movie.</returns>
-        private T FindMovie<T>(ItemResolveArgs args)
-            where T : Video, new ()
+        private T FindMovie<T>(string path, IEnumerable<FileSystemInfo> fileSystemEntries, bool isKnownMovie)
+            where T : Video, new()
         {
-            // Optimization to avoid having to resolve every file
-            bool? isKnownMovie = null;
-
             var movies = new List<T>();
 
+            var multiDiscFolders = new List<FileSystemInfo>();
+
             // Loop through each child file/folder and see if we find a video
-            foreach (var child in args.FileSystemChildren)
+            foreach (var child in fileSystemEntries)
             {
                 if ((child.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                 {
@@ -142,7 +145,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                     {
                         return new T
                         {
-                            Path = args.Path,
+                            Path = path,
                             VideoType = VideoType.Dvd
                         };
                     }
@@ -150,17 +153,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                     {
                         return new T
                         {
-                            Path = args.Path,
+                            Path = path,
                             VideoType = VideoType.BluRay
                         };
                     }
-                    if (IsHdDvdDirectory(child.Name))
+
+                    if (EntityResolutionHelper.IsMultiPartFile(child.Name))
                     {
-                        return new T
-                        {
-                            Path = args.Path,
-                            VideoType = VideoType.HdDvd
-                        };
+                        multiDiscFolders.Add(child);
                     }
 
                     continue;
@@ -183,12 +183,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                 if (item != null)
                 {
                     // If we already know it's a movie, we can stop looping
-                    if (!isKnownMovie.HasValue)
-                    {
-                        isKnownMovie = args.ContainsMetaFileByName("movie.xml") || args.ContainsMetaFileByName("tmdb3.json") || args.Path.IndexOf("[tmdbid", StringComparison.OrdinalIgnoreCase) != -1;
-                    }
-
-                    if (isKnownMovie.Value)
+                    if (isKnownMovie)
                     {
                         return item;
                     }
@@ -202,9 +197,63 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                 return GetMultiFileMovie(movies);
             }
 
-            return movies.Count == 1 ? movies[0] : null;
+            if (movies.Count == 1)
+            {
+                return movies[0];
+            }
+
+            if (multiDiscFolders.Count > 0)
+            {
+                return GetMultiDiscMovie<T>(multiDiscFolders);
+            }
+
+            return null;
         }
 
+        /// <summary>
+        /// Gets the multi disc movie.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="folders">The folders.</param>
+        /// <returns>``0.</returns>
+        private T GetMultiDiscMovie<T>(List<FileSystemInfo> folders)
+               where T : Video, new()
+        {
+            var videoType = VideoType.BluRay;
+
+            folders = folders.Where(i =>
+            {
+                var subfolders = Directory.GetDirectories(i.FullName).Select(Path.GetFileName).ToList();
+
+                if (subfolders.Any(IsDvdDirectory))
+                {
+                    videoType = VideoType.Dvd;
+                    return true;
+                }
+                if (subfolders.Any(IsBluRayDirectory))
+                {
+                    videoType = VideoType.BluRay;
+                    return true;
+                }
+
+                return false;
+
+            }).OrderBy(i => i.FullName).ToList();
+
+            if (folders.Count == 0)
+            {
+                return null;
+            }
+
+            return new T
+            {
+                Path = folders[0].FullName,
+
+                IsMultiPart = true,
+
+                VideoType = videoType
+            };
+        }
 
         /// <summary>
         /// Gets the multi file movie.
@@ -216,7 +265,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                where T : Video, new()
         {
             var multiPartMovies = movies.OrderBy(i => i.Path)
-                .Where(i => EntityResolutionHelper.IsMultiPartFile(i.Path))
+                .Where(i => EntityResolutionHelper.IsMultiPartFile(i.Name))
                 .ToList();
 
             // They must all be part of the sequence

+ 27 - 0
MediaBrowser.Tests/Resolvers/MovieResolverTests.cs

@@ -25,6 +25,33 @@ namespace MediaBrowser.Tests.Resolvers
             Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - pt 1.mkv"));
             Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - part 1.mkv"));
             Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - dvd 1.mkv"));
+
+            // Not case sensitive
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - Disc1.mkv"));
+        }
+
+        [TestMethod]
+        public void TestMultiPartFolders()
+        {
+            Assert.IsFalse(EntityResolutionHelper.IsMultiPartFile(@"blah blah"));
+
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - cd1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disc1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disk1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - pt1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - part1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - dvd1"));
+
+            // Add a space
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - cd 1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disc 1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disk 1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - pt 1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - part 1"));
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - dvd 1"));
+
+            // Not case sensitive
+            Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - Disc1"));
         }
     }
 }