diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 272dff3ecc..98c259825e 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -76,11 +76,13 @@ namespace MediaBrowser.Api { if (!(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName)) { - var collectionType = _libraryManager.GetInheritedContentType(item); - if (string.IsNullOrWhiteSpace(collectionType)) + var inheritedContentType = _libraryManager.GetInheritedContentType(item); + var configuredContentType = _libraryManager.GetConfiguredContentType(item); + + if (string.IsNullOrWhiteSpace(inheritedContentType) || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || !string.IsNullOrWhiteSpace(configuredContentType)) { info.ContentTypeOptions = GetContentTypeOptions(true); - info.ContentType = _libraryManager.GetContentType(item); + info.ContentType = configuredContentType; } } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 8573f32e0b..2ebd1cab93 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -265,6 +265,20 @@ namespace MediaBrowser.Controller.Library /// The item. /// System.String. string GetInheritedContentType(BaseItem item); + + /// + /// Gets the type of the configured content. + /// + /// The item. + /// System.String. + string GetConfiguredContentType(BaseItem item); + + /// + /// Gets the type of the configured content. + /// + /// The path. + /// System.String. + string GetConfiguredContentType(string path); /// /// Normalizes the root path list. diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 37a68b52b8..48bddd6a81 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -13,6 +13,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Naming.Audio; @@ -200,7 +201,7 @@ namespace MediaBrowser.Server.Implementations.Library MultiItemResolvers = EntityResolvers.OfType().ToArray(); IntroProviders = introProviders.ToArray(); Comparers = itemComparers.ToArray(); - + PostscanTasks = postscanTasks.OrderBy(i => { var hasOrder = i as IHasOrder; @@ -578,7 +579,7 @@ namespace MediaBrowser.Server.Implementations.Library if (string.IsNullOrWhiteSpace(collectionType)) { - collectionType = GetConfiguredContentType(fullPath); + collectionType = GetContentTypeOverride(fullPath, true); } var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, this, directoryService) @@ -1554,16 +1555,17 @@ namespace MediaBrowser.Server.Implementations.Library public string GetContentType(BaseItem item) { - // Types cannot be overridden, so go from the top down until we find a configured content type - - var type = GetInheritedContentType(item); - - if (!string.IsNullOrWhiteSpace(type)) + string configuredContentType = GetConfiguredContentType(item, false); + if (!string.IsNullOrWhiteSpace(configuredContentType)) { - return type; + return configuredContentType; } - - return GetConfiguredContentType(item); + configuredContentType = GetConfiguredContentType(item, true); + if (!string.IsNullOrWhiteSpace(configuredContentType)) + { + return configuredContentType; + } + return GetInheritedContentType(item); } public string GetInheritedContentType(BaseItem item) @@ -1580,19 +1582,36 @@ namespace MediaBrowser.Server.Implementations.Library .LastOrDefault(i => !string.IsNullOrWhiteSpace(i)); } - private string GetConfiguredContentType(BaseItem item) + public string GetConfiguredContentType(BaseItem item) { - return GetConfiguredContentType(item.ContainingFolderPath); + return GetConfiguredContentType(item, false); } - private string GetConfiguredContentType(string path) + public string GetConfiguredContentType(string path) { - var type = ConfigurationManager.Configuration.ContentTypes - .FirstOrDefault(i => string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase) || _fileSystem.ContainsSubPath(i.Name, path)); - - return type == null ? null : type.Value; + return GetContentTypeOverride(path, false); } + public string GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath) + { + ICollectionFolder collectionFolder = item as ICollectionFolder; + if (collectionFolder != null) + { + return collectionFolder.CollectionType; + } + return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath); + } + + private string GetContentTypeOverride(string path, bool inherit) + { + var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase) || (inherit && _fileSystem.ContainsSubPath(i.Name, path))); + if (nameValuePair != null) + { + return nameValuePair.Value; + } + return null; + } + private string GetTopFolderContentType(BaseItem item) { while (!(item.Parent is AggregateFolder) && item.Parent != null) @@ -1747,7 +1766,7 @@ namespace MediaBrowser.Server.Implementations.Library public int? GetSeasonNumberFromPath(string path) { - return new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, true).SeasonNumber; + return new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber; } public bool FillMissingEpisodeNumbersFromPath(Episode episode) @@ -1755,8 +1774,8 @@ namespace MediaBrowser.Server.Implementations.Library var resolver = new EpisodeResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger()); - var fileType = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || episode.VideoType == VideoType.HdDvd ? - FileInfoType.Directory : + var fileType = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || episode.VideoType == VideoType.HdDvd ? + FileInfoType.Directory : FileInfoType.File; var locationType = episode.LocationType; diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index a1c762283b..d1a6cbbf84 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -129,17 +129,26 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio { var path = fileSystemInfo.FullName; var isMultiDisc = IsMultiDiscFolder(path); - var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); - if (isMultiDisc && hasMusic) + if (isMultiDisc) { - logger.Debug("Found multi-disc folder: " + path); - discSubfolderCount++; + var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); + + if (hasMusic) + { + logger.Debug("Found multi-disc folder: " + path); + discSubfolderCount++; + } } - else if (hasMusic) + else { - // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album - notMultiDisc = true; + var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); + + if (hasMusic) + { + // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album + notMultiDisc = true; + } } } } diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index 80477e5671..fe4832d48b 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { var season = new Season { - IndexNumber = new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(args.Path, true).SeasonNumber + IndexNumber = new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(args.Path, true, true).SeasonNumber }; if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0) diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 7f1416ad65..ee0fe6b948 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -1,9 +1,17 @@ -using MediaBrowser.Controller.Entities.TV; +using System.Collections.Generic; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using System; using System.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Naming.Common; +using MediaBrowser.Naming.IO; +using MediaBrowser.Naming.TV; +using EpisodeInfo = MediaBrowser.Controller.Providers.EpisodeInfo; namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { @@ -12,6 +20,17 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV /// public class SeriesResolver : FolderResolver { + private readonly IFileSystem _fileSystem; + private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; + + public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager) + { + _fileSystem = fileSystem; + _logger = logger; + _libraryManager = libraryManager; + } + /// /// Gets the priority. /// @@ -34,26 +53,139 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV if (args.IsDirectory) { var collectionType = args.GetCollectionType(); - - // If there's a collection type and it's not tv, it can't be a series if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { if (args.HasParent()) { return null; - } - - return new Series + } + + var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path); + if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { - Path = args.Path, - Name = Path.GetFileName(args.Path) - }; + return new Series + { + Path = args.Path, + Name = Path.GetFileName(args.Path) + }; + } + } + else + { + if (args.HasParent()) + { + return null; + } + + if (string.IsNullOrWhiteSpace(collectionType)) + { + if (args.Parent.IsRoot) + { + return null; + } + if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, false)) + { + return new Series + { + Path = args.Path, + Name = Path.GetFileName(args.Path) + }; + } + } } } return null; } + public static bool IsSeriesFolder(string path, + IEnumerable fileSystemChildren, + IDirectoryService directoryService, + IFileSystem fileSystem, + ILogger logger, + ILibraryManager libraryManager, + bool isTvContentType) + { + foreach (var child in fileSystemChildren) + { + var attributes = child.Attributes; + + if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) + { + //logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName); + continue; + } + + // Can't enforce this because files saved by Bitcasa are always marked System + //if ((attributes & FileAttributes.System) == FileAttributes.System) + //{ + // logger.Debug("Igoring series subfolder marked system: {0}", child.FullName); + // continue; + //} + + if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) + { + if (IsSeasonFolder(child.FullName, isTvContentType)) + { + //logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName); + return true; + } + } + else + { + string fullName = child.FullName; + if (libraryManager.IsVideoFile(fullName)) + { + if (isTvContentType) + { + return true; + } + + var episodeResolver = new Naming.TV.EpisodeResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger()); + var episodeInfo = episodeResolver.Resolve(fullName, FileInfoType.File, false); + if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue) + { + return true; + } + } + } + } + + logger.Debug("{0} is not a series folder.", path); + return false; + } + + /// + /// Determines whether [is place holder] [the specified path]. + /// + /// The path. + /// true if [is place holder] [the specified path]; otherwise, false. + /// path + private static bool IsVideoPlaceHolder(string path) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentNullException("path"); + } + + var extension = Path.GetExtension(path); + + return string.Equals(extension, ".disc", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Determines whether [is season folder] [the specified path]. + /// + /// The path. + /// if set to true [is tv content type]. + /// true if [is season folder] [the specified path]; otherwise, false. + private static bool IsSeasonFolder(string path, bool isTvContentType) + { + var seasonNumber = new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, isTvContentType, isTvContentType).SeasonNumber; + + return seasonNumber.HasValue; + } + /// /// Sets the initial item values. /// diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 81054b3c5a..f32e33be0a 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -51,7 +51,7 @@ False - ..\packages\MediaBrowser.Naming.1.0.0.25\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll + ..\packages\MediaBrowser.Naming.1.0.0.27\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll False diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index aba7d0e330..e838757dc6 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/SharedVersion.cs b/SharedVersion.cs index 70c7bbf8c4..65f786cebb 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; [assembly: AssemblyVersion("3.0.*")] -//[assembly: AssemblyVersion("3.0.5482.1")] +//[assembly: AssemblyVersion("3.0.5482.3")]