diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-06-16 15:02:57 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-06-16 15:02:57 -0400 |
| commit | c5b00dec8ec326bbb17cf122263b78851ce398dd (patch) | |
| tree | 1a82f76db1064a71d7bfa13d3882405427456673 | |
| parent | e231bd4d32ce69a459f6c2ae1a995c14c2edb356 (diff) | |
Added multi-disc movie support
6 files changed, 152 insertions, 66 deletions
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 2e9cc3bea..307fe1954 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/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> diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index 983ce1567..035709bb4 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/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; } } } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index a15362037..d8d700d64 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/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 => { diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index a1b7be906..78c9b133b 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/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> diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 136e83aa0..dbcafcbff 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/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 diff --git a/MediaBrowser.Tests/Resolvers/MovieResolverTests.cs b/MediaBrowser.Tests/Resolvers/MovieResolverTests.cs index 19a6f0861..1cd481faa 100644 --- a/MediaBrowser.Tests/Resolvers/MovieResolverTests.cs +++ b/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")); } } } |
