diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/Library/LibraryManager.cs')
| -rw-r--r-- | MediaBrowser.Server.Implementations/Library/LibraryManager.cs | 708 |
1 files changed, 605 insertions, 103 deletions
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index ad5eac033..37a68b52b 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -15,9 +15,13 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Naming.Audio; +using MediaBrowser.Naming.Common; +using MediaBrowser.Naming.IO; +using MediaBrowser.Naming.TV; +using MediaBrowser.Naming.Video; using MediaBrowser.Server.Implementations.Library.Validators; using MediaBrowser.Server.Implementations.ScheduledTasks; -using MoreLinq; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -64,6 +68,7 @@ namespace MediaBrowser.Server.Implementations.Library /// </summary> /// <value>The entity resolvers enumerable.</value> private IItemResolver[] EntityResolvers { get; set; } + private IMultiItemResolver[] MultiItemResolvers { get; set; } /// <summary> /// Gets or sets the comparers. @@ -192,9 +197,10 @@ namespace MediaBrowser.Server.Implementations.Library EntityResolutionIgnoreRules = rules.ToArray(); PluginFolderCreators = pluginFolders.ToArray(); EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray(); + MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray(); IntroProviders = introProviders.ToArray(); Comparers = itemComparers.ToArray(); - + PostscanTasks = postscanTasks.OrderBy(i => { var hasOrder = i as IHasOrder; @@ -268,7 +274,7 @@ namespace MediaBrowser.Server.Implementations.Library { var config = ConfigurationManager.Configuration; - var ibnPathChanged = !string.Equals(_itemsByNamePath, ConfigurationManager.ApplicationPaths.ItemsByNamePath, StringComparison.CurrentCulture); + var ibnPathChanged = !string.Equals(_itemsByNamePath, ConfigurationManager.ApplicationPaths.ItemsByNamePath, StringComparison.Ordinal); if (ibnPathChanged) { @@ -276,7 +282,7 @@ namespace MediaBrowser.Server.Implementations.Library } var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName; - var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.CurrentCulture); + var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal); var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted; RecordConfigurationValues(config); @@ -331,7 +337,7 @@ namespace MediaBrowser.Server.Implementations.Library { var seasons = RootFolder.RecursiveChildren .OfType<Season>() - .Where(i => i.IndexNumber.HasValue && i.IndexNumber.Value == 0 && !string.Equals(i.Name, newName, StringComparison.CurrentCulture)) + .Where(i => i.IndexNumber.HasValue && i.IndexNumber.Value == 0 && !string.Equals(i.Name, newName, StringComparison.Ordinal)) .ToList(); foreach (var season in seasons) @@ -340,7 +346,7 @@ namespace MediaBrowser.Server.Implementations.Library try { - await UpdateItem(season, ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false); + await UpdateItem(season, ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -449,10 +455,10 @@ namespace MediaBrowser.Server.Implementations.Library { var list = new List<string> { - ConfigurationManager.ApplicationPaths.GetInternalMetadataPath(item.Id) + item.GetInternalMetadataPath() }; - list.AddRange(children.Select(i => ConfigurationManager.ApplicationPaths.GetInternalMetadataPath(i.Id))); + list.AddRange(children.Select(i => i.GetInternalMetadataPath())); return list; } @@ -462,36 +468,63 @@ namespace MediaBrowser.Server.Implementations.Library /// </summary> /// <param name="args">The args.</param> /// <returns>BaseItem.</returns> - public BaseItem ResolveItem(ItemResolveArgs args) + private BaseItem ResolveItem(ItemResolveArgs args) { - var item = EntityResolvers.Select(r => + var item = EntityResolvers.Select(r => Resolve(args, r)) + .FirstOrDefault(i => i != null); + + if (item != null) { - try - { - return r.ResolvePath(args); - } - catch (Exception ex) - { - _logger.ErrorException("Error in {0} resolving {1}", ex, r.GetType().Name, args.Path); + ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this); + } - return null; - } + return item; + } - }).FirstOrDefault(i => i != null); + private BaseItem Resolve(ItemResolveArgs args, IItemResolver resolver) + { + try + { + return resolver.ResolvePath(args); + } + catch (Exception ex) + { + _logger.ErrorException("Error in {0} resolving {1}", ex, resolver.GetType().Name, args.Path); + return null; + } + } - if (item != null) + public Guid GetNewItemId(string key, Type type) + { + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentNullException("key"); + } + if (type == null) { - ResolverHelper.SetInitialItemValues(item, args, _fileSystem); + throw new ArgumentNullException("type"); } - return item; + if (ConfigurationManager.Configuration.EnableLocalizedGuids && key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath)) + { + // Try to normalize paths located underneath program-data in an attempt to make them more portable + key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length) + .TrimStart(new[] { '/', '\\' }) + .Replace("/", "\\"); + } + + key = type.FullName + key.ToLower(); + + return key.GetMD5(); } public IEnumerable<BaseItem> ReplaceVideosWithPrimaryVersions(IEnumerable<BaseItem> items) { - return items.Select(i => + var dict = new Dictionary<Guid, BaseItem>(); + + foreach (var item in items) { - var video = i as Video; + var video = item as Video; if (video != null) { @@ -501,14 +534,15 @@ namespace MediaBrowser.Server.Implementations.Library if (primary != null) { - return primary; + dict[primary.Id] = primary; + continue; } } } + dict[item.Id] = item; + } - return i; - - }).DistinctBy(i => i.Id); + return dict.Values; } /// <summary> @@ -527,31 +561,32 @@ namespace MediaBrowser.Server.Implementations.Library return item; } - public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null) + public BaseItem ResolvePath(FileSystemInfo fileInfo, + Folder parent = null) { return ResolvePath(fileInfo, new DirectoryService(_logger), parent); } - /// <summary> - /// Resolves a path into a BaseItem - /// </summary> - /// <param name="fileInfo">The file info.</param> - /// <param name="directoryService">The directory service.</param> - /// <param name="parent">The parent.</param> - /// <returns>BaseItem.</returns> - /// <exception cref="System.ArgumentNullException">fileInfo</exception> - public BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null) + private BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null, string collectionType = null) { if (fileInfo == null) { throw new ArgumentNullException("fileInfo"); } + var fullPath = fileInfo.FullName; + + if (string.IsNullOrWhiteSpace(collectionType)) + { + collectionType = GetConfiguredContentType(fullPath); + } + var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, this, directoryService) { Parent = parent, - Path = fileInfo.FullName, - FileInfo = fileInfo + Path = fullPath, + FileInfo = fileInfo, + CollectionType = collectionType }; // Return null if ignore rules deem that we should do so @@ -619,40 +654,48 @@ namespace MediaBrowser.Server.Implementations.Library return !args.ContainsFileSystemEntryByName(".ignore"); } - /// <summary> - /// Resolves a set of files into a list of BaseItem - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="files">The files.</param> - /// <param name="directoryService">The directory service.</param> - /// <param name="parent">The parent.</param> - /// <returns>List{``0}.</returns> - public List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent) - where T : BaseItem + public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent, string collectionType) { - var list = new List<T>(); + var fileList = files.ToList(); - Parallel.ForEach(files, f => + if (parent != null) { - try + foreach (var resolver in MultiItemResolvers) { - var item = ResolvePath(f, directoryService, parent) as T; + var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService); - if (item != null) + if (result != null && result.Items.Count > 0) { - lock (list) + var items = new List<BaseItem>(); + items.AddRange(result.Items); + + foreach (var item in items) { - list.Add(item); + ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService); } + items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType)); + return items; } } + } + + return ResolveFileList(fileList, directoryService, parent, collectionType); + } + + private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemInfo> fileList, IDirectoryService directoryService, Folder parent, string collectionType) + { + return fileList.Select(f => + { + try + { + return ResolvePath(f, directoryService, parent, collectionType); + } catch (Exception ex) { _logger.ErrorException("Error resolving path {0}", ex, f.FullName); + return null; } - }); - - return list; + }).Where(i => i != null); } /// <summary> @@ -666,7 +709,7 @@ namespace MediaBrowser.Server.Implementations.Library Directory.CreateDirectory(rootFolderPath); - var rootFolder = GetItemById(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath)); + var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath)); // Add in the plug-in folders foreach (var child in PluginFolderCreators) @@ -677,7 +720,14 @@ namespace MediaBrowser.Server.Implementations.Library { if (folder.Id == Guid.Empty) { - folder.Id = (folder.Path ?? folder.GetType().Name).GetMBId(folder.GetType()); + if (string.IsNullOrWhiteSpace(folder.Path)) + { + folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType()); + } + else + { + folder.Id = GetNewItemId(folder.Path, folder.GetType()); + } } folder = GetItemById(folder.Id) as BasePluginFolder ?? folder; @@ -692,16 +742,27 @@ namespace MediaBrowser.Server.Implementations.Library } private UserRootFolder _userRootFolder; + private readonly object _syncLock = new object(); public Folder GetUserRootFolder() { if (_userRootFolder == null) { - var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + lock (_syncLock) + { + if (_userRootFolder == null) + { + var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - Directory.CreateDirectory(userRootPath); + Directory.CreateDirectory(userRootPath); - _userRootFolder = GetItemById(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ?? - (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)); + _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder; + + if (_userRootFolder == null) + { + _userRootFolder = (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)); + } + } + } } return _userRootFolder; @@ -807,7 +868,7 @@ namespace MediaBrowser.Server.Implementations.Library var type = typeof(T); - if (type == typeof(Person) && ConfigurationManager.Configuration.EnablePeoplePrefixSubFolders) + if (type == typeof(Person)) { subFolderPrefix = validFilename.Substring(0, 1); } @@ -816,7 +877,7 @@ namespace MediaBrowser.Server.Implementations.Library Path.Combine(path, validFilename) : Path.Combine(path, subFolderPrefix, validFilename); - var id = fullPath.GetMBId(type); + var id = GetNewItemId(fullPath, type); BaseItem obj; @@ -1187,26 +1248,81 @@ namespace MediaBrowser.Server.Implementations.Library return item; } + public BaseItem GetMemoryItemById(Guid id) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + BaseItem item; + + LibraryItemsCache.TryGetValue(id, out item); + + return item; + } + /// <summary> /// Gets the intros. /// </summary> /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <returns>IEnumerable{System.String}.</returns> - public IEnumerable<Video> GetIntros(BaseItem item, User user) + public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user) { - return IntroProviders.SelectMany(i => i.GetIntros(item, user)) + var tasks = IntroProviders + .OrderBy(i => (i.GetType().Name.IndexOf("Default", StringComparison.OrdinalIgnoreCase) == -1 ? 0 : 1)) + .Take(1) + .Select(i => GetIntros(i, item, user)); + + var items = await Task.WhenAll(tasks).ConfigureAwait(false); + + return items + .SelectMany(i => i.ToArray()) .Select(ResolveIntro) .Where(i => i != null); } /// <summary> + /// Gets the intros. + /// </summary> + /// <param name="provider">The provider.</param> + /// <param name="item">The item.</param> + /// <param name="user">The user.</param> + /// <returns>Task<IEnumerable<IntroInfo>>.</returns> + private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, User user) + { + try + { + return await provider.GetIntros(item, user).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting intros", ex); + + return new List<IntroInfo>(); + } + } + + /// <summary> /// Gets all intro files. /// </summary> /// <returns>IEnumerable{System.String}.</returns> public IEnumerable<string> GetAllIntroFiles() { - return IntroProviders.SelectMany(i => i.GetAllIntroFiles()); + return IntroProviders.SelectMany(i => + { + try + { + return i.GetAllIntroFiles().ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting intro files", ex); + + return new List<string>(); + } + }); } /// <summary> @@ -1383,7 +1499,8 @@ namespace MediaBrowser.Server.Implementations.Library item.DateLastSaved = DateTime.UtcNow; - _logger.Debug("Saving {0} to database.", item.Path ?? item.Name); + var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name; + _logger.Debug("Saving {0} to database.", logName); await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false); @@ -1435,12 +1552,48 @@ namespace MediaBrowser.Server.Implementations.Library return ItemRepository.RetrieveItem(id); } - /// <summary> - /// Finds the type of the collection. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>System.String.</returns> - public string FindCollectionType(BaseItem item) + 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)) + { + return type; + } + + return GetConfiguredContentType(item); + } + + public string GetInheritedContentType(BaseItem item) + { + var type = GetTopFolderContentType(item); + + if (!string.IsNullOrWhiteSpace(type)) + { + return type; + } + + return item.Parents + .Select(GetConfiguredContentType) + .LastOrDefault(i => !string.IsNullOrWhiteSpace(i)); + } + + private string GetConfiguredContentType(BaseItem item) + { + return GetConfiguredContentType(item.ContainingFolderPath); + } + + private 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; + } + + private string GetTopFolderContentType(BaseItem item) { while (!(item.Parent is AggregateFolder) && item.Parent != null) { @@ -1452,42 +1605,30 @@ namespace MediaBrowser.Server.Implementations.Library return null; } - var collectionTypes = _userManager.Users - .Select(i => i.RootFolder) - .Distinct() - .SelectMany(i => i.Children) - .OfType<CollectionFolder>() + return GetUserRootFolder().Children + .OfType<ICollectionFolder>() .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path)) .Select(i => i.CollectionType) - .Where(i => !string.IsNullOrEmpty(i)) - .Distinct() - .ToList(); - - return collectionTypes.Count == 1 ? collectionTypes[0] : null; + .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); } - public Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken) - { - return GetNamedView(name, null, type, sortName, cancellationToken); - } - - public async Task<UserView> GetNamedView(string name, string category, string type, string sortName, CancellationToken cancellationToken) + public async Task<UserView> GetNamedView(string name, + string type, + string sortName, + CancellationToken cancellationToken) { var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "views"); - if (!string.IsNullOrWhiteSpace(category)) - { - path = Path.Combine(path, _fileSystem.GetValidFilename(category)); - } - path = Path.Combine(path, _fileSystem.GetValidFilename(type)); - var id = (path + "_namedview_" + name).GetMBId(typeof(UserView)); + var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView)); var item = GetItemById(id) as UserView; - if (item == null || + var refresh = false; + + if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) { Directory.CreateDirectory(path); @@ -1504,10 +1645,371 @@ namespace MediaBrowser.Server.Implementations.Library await CreateItem(item, cancellationToken).ConfigureAwait(false); - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); + refresh = true; + } + + if (!refresh && item != null) + { + refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24; + } + + if (refresh) + { + await item.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = true + + }, cancellationToken).ConfigureAwait(false); } return item; } + + public async Task<UserView> GetSpecialFolder(User user, + string name, + string parentId, + string viewType, + string sortName, + CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + if (string.IsNullOrWhiteSpace(parentId)) + { + throw new ArgumentNullException("parentId"); + } + + if (string.IsNullOrWhiteSpace(viewType)) + { + throw new ArgumentNullException("viewType"); + } + + var id = GetNewItemId("7_namedview_" + name + user.Id.ToString("N") + parentId, typeof(UserView)); + + var path = BaseItem.GetInternalMetadataPathForId(id); + + var item = GetItemById(id) as UserView; + + var refresh = false; + + if (item == null) + { + Directory.CreateDirectory(path); + + item = new UserView + { + Path = path, + Id = id, + DateCreated = DateTime.UtcNow, + Name = name, + ViewType = viewType, + ForcedSortName = sortName, + UserId = user.Id, + ParentId = new Guid(parentId) + }; + + await CreateItem(item, cancellationToken).ConfigureAwait(false); + + refresh = true; + } + + if (!refresh && item != null) + { + refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24; + } + + if (refresh) + { + await item.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = true + + }, cancellationToken).ConfigureAwait(false); + } + + return item; + } + + public bool IsVideoFile(string path) + { + var resolver = new VideoResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger()); + return resolver.IsVideoFile(path); + } + + public bool IsAudioFile(string path) + { + var parser = new AudioFileParser(new ExtendedNamingOptions()); + return parser.IsAudioFile(path); + } + + public int? GetSeasonNumberFromPath(string path) + { + return new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, true).SeasonNumber; + } + + public bool FillMissingEpisodeNumbersFromPath(Episode episode) + { + 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 : + FileInfoType.File; + + var locationType = episode.LocationType; + + var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ? + resolver.Resolve(episode.Path, fileType) : + new Naming.TV.EpisodeInfo(); + + if (episodeInfo == null) + { + episodeInfo = new Naming.TV.EpisodeInfo(); + } + + var changed = false; + + if (episodeInfo.IsByDate) + { + if (episode.IndexNumber.HasValue) + { + episode.IndexNumber = null; + changed = true; + } + + if (episode.IndexNumberEnd.HasValue) + { + episode.IndexNumberEnd = null; + changed = true; + } + + if (!episode.PremiereDate.HasValue) + { + if (episodeInfo.Year.HasValue && episodeInfo.Month.HasValue && episodeInfo.Day.HasValue) + { + episode.PremiereDate = new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value).ToUniversalTime(); + } + + if (episode.PremiereDate.HasValue) + { + changed = true; + } + } + + if (!episode.ProductionYear.HasValue) + { + episode.ProductionYear = episodeInfo.Year; + + if (episode.ProductionYear.HasValue) + { + changed = true; + } + } + + if (!episode.ParentIndexNumber.HasValue) + { + var season = episode.Season; + + if (season != null) + { + episode.ParentIndexNumber = season.IndexNumber; + } + + if (episode.ParentIndexNumber.HasValue) + { + changed = true; + } + } + } + else + { + if (!episode.IndexNumber.HasValue) + { + episode.IndexNumber = episodeInfo.EpisodeNumber; + + if (episode.IndexNumber.HasValue) + { + changed = true; + } + } + + if (!episode.IndexNumberEnd.HasValue) + { + episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber; + + if (episode.IndexNumberEnd.HasValue) + { + changed = true; + } + } + + if (!episode.ParentIndexNumber.HasValue) + { + episode.ParentIndexNumber = episodeInfo.SeasonNumber; + + if (!episode.ParentIndexNumber.HasValue) + { + var season = episode.Season; + + if (season != null) + { + episode.ParentIndexNumber = season.IndexNumber; + } + } + + if (episode.ParentIndexNumber.HasValue) + { + changed = true; + } + } + } + + return changed; + } + + public ItemLookupInfo ParseName(string name) + { + var resolver = new VideoResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger()); + + var result = resolver.CleanDateTime(name); + var cleanName = resolver.CleanString(result.Name); + + return new ItemLookupInfo + { + Name = cleanName.Name, + Year = result.Year + }; + } + + public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) + { + var files = fileSystemChildren.OfType<DirectoryInfo>() + .Where(i => string.Equals(i.Name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly)) + .ToList(); + + var videoListResolver = new VideoListResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger()); + + var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo + { + FullName = i.FullName, + Type = GetFileType(i) + + }).ToList()); + + var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase)); + + if (currentVideo != null) + { + files.AddRange(currentVideo.Extras.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => new FileInfo(i.Path))); + } + + return ResolvePaths(files, directoryService, null, null) + .OfType<Video>() + .Select(video => + { + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = GetItemById(video.Id) as Video; + + if (dbItem != null) + { + video = dbItem; + } + + video.ExtraType = ExtraType.Trailer; + + return video; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); + } + + private FileInfoType GetFileType(FileSystemInfo info) + { + if ((info.Attributes & FileAttributes.Directory) == FileAttributes.Directory) + { + return FileInfoType.Directory; + } + + return FileInfoType.File; + } + + public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) + { + var files = fileSystemChildren.OfType<DirectoryInfo>() + .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly)) + .ToList(); + + var videoListResolver = new VideoListResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger()); + + var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo + { + FullName = i.FullName, + Type = GetFileType(i) + + }).ToList()); + + var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase)); + + if (currentVideo != null) + { + files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => new FileInfo(i.Path))); + } + + return ResolvePaths(files, directoryService, null, null) + .OfType<Video>() + .Select(video => + { + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = GetItemById(video.Id) as Video; + + if (dbItem != null) + { + video = dbItem; + } + + SetExtraTypeFromFilename(video); + + return video; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); + } + + private void SetExtraTypeFromFilename(Video item) + { + var resolver = new ExtraResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger(), new RegexProvider()); + + var result = resolver.GetExtraInfo(item.Path); + + if (string.Equals(result.ExtraType, "deletedscene", StringComparison.OrdinalIgnoreCase)) + { + item.ExtraType = ExtraType.DeletedScene; + } + else if (string.Equals(result.ExtraType, "behindthescenes", StringComparison.OrdinalIgnoreCase)) + { + item.ExtraType = ExtraType.BehindTheScenes; + } + else if (string.Equals(result.ExtraType, "interview", StringComparison.OrdinalIgnoreCase)) + { + item.ExtraType = ExtraType.Interview; + } + else if (string.Equals(result.ExtraType, "scene", StringComparison.OrdinalIgnoreCase)) + { + item.ExtraType = ExtraType.Scene; + } + else if (string.Equals(result.ExtraType, "sample", StringComparison.OrdinalIgnoreCase)) + { + item.ExtraType = ExtraType.Sample; + } + else + { + item.ExtraType = ExtraType.Clip; + } + } } } |
