From 3eb4091808735858b01855d298226d239be464af Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 3 Nov 2016 02:37:52 -0400 Subject: move additional classes to new server lib --- .../Library/LibraryManager.cs | 3067 -------------------- 1 file changed, 3067 deletions(-) delete mode 100644 MediaBrowser.Server.Implementations/Library/LibraryManager.cs (limited to 'MediaBrowser.Server.Implementations/Library/LibraryManager.cs') diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs deleted file mode 100644 index 18feaa849..000000000 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ /dev/null @@ -1,3067 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.IO; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; -using MediaBrowser.Naming.Audio; -using MediaBrowser.Naming.Common; -using MediaBrowser.Naming.TV; -using MediaBrowser.Naming.Video; -using MediaBrowser.Server.Implementations.Library.Validators; -using MediaBrowser.Server.Implementations.Logging; -using MediaBrowser.Server.Implementations.ScheduledTasks; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Channels; -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.Library; -using MediaBrowser.Model.Net; -using MediaBrowser.Server.Implementations.Library.Resolvers; -using SortOrder = MediaBrowser.Model.Entities.SortOrder; -using VideoResolver = MediaBrowser.Naming.Video.VideoResolver; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.Tasks; - -namespace MediaBrowser.Server.Implementations.Library -{ - /// - /// Class LibraryManager - /// - public class LibraryManager : ILibraryManager - { - /// - /// Gets or sets the postscan tasks. - /// - /// The postscan tasks. - private ILibraryPostScanTask[] PostscanTasks { get; set; } - - /// - /// Gets the intro providers. - /// - /// The intro providers. - private IIntroProvider[] IntroProviders { get; set; } - - /// - /// Gets the list of entity resolution ignore rules - /// - /// The entity resolution ignore rules. - private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } - - /// - /// Gets the list of BasePluginFolders added by plugins - /// - /// The plugin folders. - private IVirtualFolderCreator[] PluginFolderCreators { get; set; } - - /// - /// Gets the list of currently registered entity resolvers - /// - /// The entity resolvers enumerable. - private IItemResolver[] EntityResolvers { get; set; } - private IMultiItemResolver[] MultiItemResolvers { get; set; } - - /// - /// Gets or sets the comparers. - /// - /// The comparers. - private IBaseItemComparer[] Comparers { get; set; } - - /// - /// Gets the active item repository - /// - /// The item repository. - public IItemRepository ItemRepository { get; set; } - - /// - /// Occurs when [item added]. - /// - public event EventHandler ItemAdded; - - /// - /// Occurs when [item updated]. - /// - public event EventHandler ItemUpdated; - - /// - /// Occurs when [item removed]. - /// - public event EventHandler ItemRemoved; - - /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// The _task manager - /// - private readonly ITaskManager _taskManager; - - /// - /// The _user manager - /// - private readonly IUserManager _userManager; - - /// - /// The _user data repository - /// - private readonly IUserDataManager _userDataRepository; - - /// - /// Gets or sets the configuration manager. - /// - /// The configuration manager. - private IServerConfigurationManager ConfigurationManager { get; set; } - - /// - /// A collection of items that may be referenced from multiple physical places in the library - /// (typically, multiple user roots). We store them here and be sure they all reference a - /// single instance. - /// - /// The by reference items. - private ConcurrentDictionary ByReferenceItems { get; set; } - - private readonly Func _libraryMonitorFactory; - private readonly Func _providerManagerFactory; - private readonly Func _userviewManager; - public bool IsScanRunning { get; private set; } - - /// - /// The _library items cache - /// - private readonly ConcurrentDictionary _libraryItemsCache; - /// - /// Gets the library items cache. - /// - /// The library items cache. - private ConcurrentDictionary LibraryItemsCache - { - get - { - return _libraryItemsCache; - } - } - - private readonly IFileSystem _fileSystem; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The task manager. - /// The user manager. - /// The configuration manager. - /// The user data repository. - public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func libraryMonitorFactory, IFileSystem fileSystem, Func providerManagerFactory, Func userviewManager) - { - _logger = logger; - _taskManager = taskManager; - _userManager = userManager; - ConfigurationManager = configurationManager; - _userDataRepository = userDataRepository; - _libraryMonitorFactory = libraryMonitorFactory; - _fileSystem = fileSystem; - _providerManagerFactory = providerManagerFactory; - _userviewManager = userviewManager; - ByReferenceItems = new ConcurrentDictionary(); - _libraryItemsCache = new ConcurrentDictionary(); - - ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated; - - RecordConfigurationValues(configurationManager.Configuration); - } - - /// - /// Adds the parts. - /// - /// The rules. - /// The plugin folders. - /// The resolvers. - /// The intro providers. - /// The item comparers. - /// The postscan tasks. - public void AddParts(IEnumerable rules, - IEnumerable pluginFolders, - IEnumerable resolvers, - IEnumerable introProviders, - IEnumerable itemComparers, - IEnumerable postscanTasks) - { - EntityResolutionIgnoreRules = rules.ToArray(); - PluginFolderCreators = pluginFolders.ToArray(); - EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray(); - MultiItemResolvers = EntityResolvers.OfType().ToArray(); - IntroProviders = introProviders.ToArray(); - Comparers = itemComparers.ToArray(); - - PostscanTasks = postscanTasks.OrderBy(i => - { - var hasOrder = i as IHasOrder; - - return hasOrder == null ? 0 : hasOrder.Order; - - }).ToArray(); - } - - /// - /// The _root folder - /// - private volatile AggregateFolder _rootFolder; - /// - /// The _root folder sync lock - /// - private readonly object _rootFolderSyncLock = new object(); - /// - /// Gets the root folder. - /// - /// The root folder. - public AggregateFolder RootFolder - { - get - { - if (_rootFolder == null) - { - lock (_rootFolderSyncLock) - { - if (_rootFolder == null) - { - _rootFolder = CreateRootFolder(); - } - } - } - return _rootFolder; - } - } - - /// - /// The _season zero display name - /// - private string _seasonZeroDisplayName; - - private bool _wizardCompleted; - /// - /// Records the configuration values. - /// - /// The configuration. - private void RecordConfigurationValues(ServerConfiguration configuration) - { - _seasonZeroDisplayName = configuration.SeasonZeroDisplayName; - _wizardCompleted = configuration.IsStartupWizardCompleted; - } - - /// - /// Configurations the updated. - /// - /// The sender. - /// The instance containing the event data. - void ConfigurationUpdated(object sender, EventArgs e) - { - var config = ConfigurationManager.Configuration; - - var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName; - var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal); - var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted; - - RecordConfigurationValues(config); - - if (seasonZeroNameChanged || wizardChanged) - { - _taskManager.CancelIfRunningAndQueue(); - } - - if (seasonZeroNameChanged) - { - Task.Run(async () => - { - await UpdateSeasonZeroNames(newSeasonZeroName, CancellationToken.None).ConfigureAwait(false); - - }); - } - } - - /// - /// Updates the season zero names. - /// - /// The new name. - /// The cancellation token. - /// Task. - private async Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken) - { - var seasons = GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Season).Name }, - Recursive = true, - IndexNumber = 0 - - }).Cast() - .Where(i => !string.Equals(i.Name, newName, StringComparison.Ordinal)) - .ToList(); - - foreach (var season in seasons) - { - season.Name = newName; - - try - { - await UpdateItem(season, ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error saving {0}", ex, season.Path); - } - } - } - - public void RegisterItem(BaseItem item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - RegisterItem(item.Id, item); - } - - private void RegisterItem(Guid id, BaseItem item) - { - if (item is IItemByName) - { - if (!(item is MusicArtist)) - { - return; - } - } - - if (item.IsFolder) - { - if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder)) - { - if (item.SourceType != SourceType.Library) - { - return; - } - } - } - else - { - if (item is Photo) - { - return; - } - } - - LibraryItemsCache.AddOrUpdate(id, item, delegate { return item; }); - } - - public async Task DeleteItem(BaseItem item, DeleteOptions options) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - _logger.Debug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}", - item.GetType().Name, - item.Name ?? "Unknown name", - item.Path ?? string.Empty, - item.Id); - - var parent = item.Parent; - - var locationType = item.LocationType; - - var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren(false).ToList() - : new List(); - - foreach (var metadataPath in GetMetadataPaths(item, children)) - { - _logger.Debug("Deleting path {0}", metadataPath); - - try - { - _fileSystem.DeleteDirectory(metadataPath, true); - } - catch (DirectoryNotFoundException) - { - - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting {0}", ex, metadataPath); - } - } - - if (options.DeleteFileLocation && locationType != LocationType.Remote && locationType != LocationType.Virtual) - { - foreach (var path in item.GetDeletePaths().ToList()) - { - if (_fileSystem.DirectoryExists(path)) - { - _logger.Debug("Deleting path {0}", path); - _fileSystem.DeleteDirectory(path, true); - } - else if (_fileSystem.FileExists(path)) - { - _logger.Debug("Deleting path {0}", path); - _fileSystem.DeleteFile(path); - } - } - - if (parent != null) - { - await parent.ValidateChildren(new Progress(), CancellationToken.None) - .ConfigureAwait(false); - } - } - else if (parent != null) - { - parent.RemoveChild(item); - } - - await ItemRepository.DeleteItem(item.Id, CancellationToken.None).ConfigureAwait(false); - foreach (var child in children) - { - await ItemRepository.DeleteItem(child.Id, CancellationToken.None).ConfigureAwait(false); - } - - BaseItem removed; - _libraryItemsCache.TryRemove(item.Id, out removed); - - ReportItemRemoved(item); - } - - private IEnumerable GetMetadataPaths(BaseItem item, IEnumerable children) - { - var list = new List - { - item.GetInternalMetadataPath() - }; - - list.AddRange(children.Select(i => i.GetInternalMetadataPath())); - - return list; - } - - /// - /// Resolves the item. - /// - /// The args. - /// The resolvers. - /// BaseItem. - private BaseItem ResolveItem(ItemResolveArgs args, IItemResolver[] resolvers) - { - var item = (resolvers ?? EntityResolvers).Select(r => Resolve(args, r)) - .FirstOrDefault(i => i != null); - - if (item != null) - { - ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this); - } - - return item; - } - - 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; - } - } - - public Guid GetNewItemId(string key, Type type) - { - if (string.IsNullOrWhiteSpace(key)) - { - throw new ArgumentNullException("key"); - } - if (type == null) - { - throw new ArgumentNullException("type"); - } - - 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("/", "\\"); - } - - if (!ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) - { - key = key.ToLower(); - } - - key = type.FullName + key; - - return key.GetMD5(); - } - - /// - /// Ensure supplied item has only one instance throughout - /// - /// The item. - /// The proper instance to the item - public BaseItem GetOrAddByReferenceItem(BaseItem item) - { - // Add this item to our list if not there already - if (!ByReferenceItems.TryAdd(item.Id, item)) - { - // Already there - return the existing reference - item = ByReferenceItems[item.Id]; - } - return item; - } - - public BaseItem ResolvePath(FileSystemMetadata fileInfo, - Folder parent = null) - { - return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent); - } - - private BaseItem ResolvePath(FileSystemMetadata fileInfo, - IDirectoryService directoryService, - IItemResolver[] resolvers, - Folder parent = null, - string collectionType = null, - LibraryOptions libraryOptions = null) - { - if (fileInfo == null) - { - throw new ArgumentNullException("fileInfo"); - } - - var fullPath = fileInfo.FullName; - - if (string.IsNullOrWhiteSpace(collectionType) && parent != null) - { - collectionType = GetContentTypeOverride(fullPath, true); - } - - var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) - { - Parent = parent, - Path = fullPath, - FileInfo = fileInfo, - CollectionType = collectionType, - LibraryOptions = libraryOptions - }; - - // Return null if ignore rules deem that we should do so - if (IgnoreFile(args.FileInfo, args.Parent)) - { - return null; - } - - // Gather child folder and files - if (args.IsDirectory) - { - var isPhysicalRoot = args.IsPhysicalRoot; - - // When resolving the root, we need it's grandchildren (children of user views) - var flattenFolderDepth = isPhysicalRoot ? 2 : 0; - - var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf); - - // Need to remove subpaths that may have been resolved from shortcuts - // Example: if \\server\movies exists, then strip out \\server\movies\action - if (isPhysicalRoot) - { - var paths = NormalizeRootPathList(fileSystemDictionary.Values); - - fileSystemDictionary = paths.ToDictionary(i => i.FullName); - } - - args.FileSystemDictionary = fileSystemDictionary; - } - - // Check to see if we should resolve based on our contents - if (args.IsDirectory && !ShouldResolvePathContents(args)) - { - return null; - } - - return ResolveItem(args, resolvers); - } - - private readonly List _ignoredPaths = new List(); - - public void RegisterIgnoredPath(string path) - { - lock (_ignoredPaths) - { - _ignoredPaths.Add(path); - } - } - public void UnRegisterIgnoredPath(string path) - { - lock (_ignoredPaths) - { - _ignoredPaths.Remove(path); - } - } - - public bool IgnoreFile(FileSystemMetadata file, BaseItem parent) - { - if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent))) - { - return true; - } - - //lock (_ignoredPaths) - { - if (_ignoredPaths.Contains(file.FullName, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - } - return false; - } - - public IEnumerable NormalizeRootPathList(IEnumerable paths) - { - var originalList = paths.ToList(); - - var list = originalList.Where(i => i.IsDirectory) - .Select(i => _fileSystem.NormalizePath(i.FullName)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath))) - .ToList(); - - foreach (var dupe in dupes) - { - _logger.Info("Found duplicate path: {0}", dupe); - } - - var newList = list.Except(dupes, StringComparer.OrdinalIgnoreCase).Select(_fileSystem.GetDirectoryInfo).ToList(); - newList.AddRange(originalList.Where(i => !i.IsDirectory)); - return newList; - } - - /// - /// Determines whether a path should be ignored based on its contents - called after the contents have been read - /// - /// The args. - /// true if XXXX, false otherwise - private static bool ShouldResolvePathContents(ItemResolveArgs args) - { - // Ignore any folders containing a file called .ignore - return !args.ContainsFileSystemEntryByName(".ignore"); - } - - public IEnumerable ResolvePaths(IEnumerable files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType) - { - return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers); - } - - public IEnumerable ResolvePaths(IEnumerable files, - IDirectoryService directoryService, - Folder parent, - LibraryOptions libraryOptions, - string collectionType, - IItemResolver[] resolvers) - { - var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList(); - - if (parent != null) - { - var multiItemResolvers = resolvers == null ? MultiItemResolvers : resolvers.OfType().ToArray(); - - foreach (var resolver in multiItemResolvers) - { - var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService); - - if (result != null && result.Items.Count > 0) - { - var items = new List(); - items.AddRange(result.Items); - - foreach (var item in items) - { - ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService); - } - items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions)); - return items; - } - } - } - - return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers, libraryOptions); - } - - private IEnumerable ResolveFileList(IEnumerable fileList, - IDirectoryService directoryService, - Folder parent, - string collectionType, - IItemResolver[] resolvers, - LibraryOptions libraryOptions) - { - return fileList.Select(f => - { - try - { - return ResolvePath(f, directoryService, resolvers, parent, collectionType, libraryOptions); - } - catch (Exception ex) - { - _logger.ErrorException("Error resolving path {0}", ex, f.FullName); - return null; - } - }).Where(i => i != null); - } - - /// - /// Creates the root media folder - /// - /// AggregateFolder. - /// Cannot create the root folder until plugins have loaded - public AggregateFolder CreateRootFolder() - { - var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath; - - _fileSystem.CreateDirectory(rootFolderPath); - - var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)); - - // Add in the plug-in folders - foreach (var child in PluginFolderCreators) - { - var folder = child.GetFolder(); - - if (folder != null) - { - if (folder.Id == Guid.Empty) - { - if (string.IsNullOrWhiteSpace(folder.Path)) - { - folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType()); - } - else - { - folder.Id = GetNewItemId(folder.Path, folder.GetType()); - } - } - - var dbItem = GetItemById(folder.Id) as BasePluginFolder; - - if (dbItem != null && string.Equals(dbItem.Path, folder.Path, StringComparison.OrdinalIgnoreCase)) - { - folder = dbItem; - } - - if (folder.ParentId != rootFolder.Id) - { - folder.ParentId = rootFolder.Id; - var task = folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); - Task.WaitAll(task); - } - - rootFolder.AddVirtualChild(folder); - - RegisterItem(folder); - } - } - - return rootFolder; - } - - private volatile UserRootFolder _userRootFolder; - private readonly object _syncLock = new object(); - public Folder GetUserRootFolder() - { - if (_userRootFolder == null) - { - lock (_syncLock) - { - if (_userRootFolder == null) - { - var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - - _fileSystem.CreateDirectory(userRootPath); - - var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder; - - if (tmpItem == null) - { - tmpItem = (UserRootFolder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath)); - } - - _userRootFolder = tmpItem; - } - } - } - - return _userRootFolder; - } - - public BaseItem FindByPath(string path, bool? isFolder) - { - // If this returns multiple items it could be tricky figuring out which one is correct. - // In most cases, the newest one will be and the others obsolete but not yet cleaned up - - var query = new InternalItemsQuery - { - Path = path, - IsFolder = isFolder, - SortBy = new[] { ItemSortBy.DateCreated }, - SortOrder = SortOrder.Descending, - Limit = 1 - }; - - return GetItemList(query) - .FirstOrDefault(); - } - - /// - /// Gets a Person - /// - /// The name. - /// Task{Person}. - public Person GetPerson(string name) - { - return CreateItemByName(Person.GetPath(name), name); - } - - /// - /// Gets a Studio - /// - /// The name. - /// Task{Studio}. - public Studio GetStudio(string name) - { - return CreateItemByName(Studio.GetPath(name), name); - } - - /// - /// Gets a Genre - /// - /// The name. - /// Task{Genre}. - public Genre GetGenre(string name) - { - return CreateItemByName(Genre.GetPath(name), name); - } - - /// - /// Gets the genre. - /// - /// The name. - /// Task{MusicGenre}. - public MusicGenre GetMusicGenre(string name) - { - return CreateItemByName(MusicGenre.GetPath(name), name); - } - - /// - /// Gets the game genre. - /// - /// The name. - /// Task{GameGenre}. - public GameGenre GetGameGenre(string name) - { - return CreateItemByName(GameGenre.GetPath(name), name); - } - - /// - /// Gets a Year - /// - /// The value. - /// Task{Year}. - /// - public Year GetYear(int value) - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException("Years less than or equal to 0 are invalid."); - } - - var name = value.ToString(CultureInfo.InvariantCulture); - - return CreateItemByName(Year.GetPath(name), name); - } - - /// - /// Gets a Genre - /// - /// The name. - /// Task{Genre}. - public MusicArtist GetArtist(string name) - { - return CreateItemByName(MusicArtist.GetPath(name), name); - } - - private T CreateItemByName(string path, string name) - where T : BaseItem, new() - { - if (typeof(T) == typeof(MusicArtist)) - { - var existing = GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(T).Name }, - Name = name - - }).Cast() - .OrderBy(i => i.IsAccessedByName ? 1 : 0) - .Cast() - .FirstOrDefault(); - - if (existing != null) - { - return existing; - } - } - - var id = GetNewItemId(path, typeof(T)); - - var item = GetItemById(id) as T; - - if (item == null) - { - item = new T - { - Name = name, - Id = id, - DateCreated = DateTime.UtcNow, - DateModified = DateTime.UtcNow, - Path = path - }; - - var task = CreateItem(item, CancellationToken.None); - Task.WaitAll(task); - } - - return item; - } - - public IEnumerable GetAlbumArtists(IEnumerable items) - { - var names = items - .SelectMany(i => i.AlbumArtists) - .DistinctNames() - .Select(i => - { - try - { - var artist = GetArtist(i); - - return artist; - } - catch - { - // Already logged at lower levels - return null; - } - }) - .Where(i => i != null); - - return names; - } - - public IEnumerable GetArtists(IEnumerable items) - { - var names = items - .SelectMany(i => i.AllArtists) - .DistinctNames() - .Select(i => - { - try - { - var artist = GetArtist(i); - - return artist; - } - catch - { - // Already logged at lower levels - return null; - } - }) - .Where(i => i != null); - - return names; - } - - /// - /// Validate and refresh the People sub-set of the IBN. - /// The items are stored in the db but not loaded into memory until actually requested by an operation. - /// - /// The cancellation token. - /// The progress. - /// Task. - public Task ValidatePeople(CancellationToken cancellationToken, IProgress progress) - { - // Ensure the location is available. - _fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); - - return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress); - } - - /// - /// Reloads the root media folder - /// - /// The progress. - /// The cancellation token. - /// Task. - public Task ValidateMediaLibrary(IProgress progress, CancellationToken cancellationToken) - { - // Just run the scheduled task so that the user can see it - _taskManager.CancelIfRunningAndQueue(); - - return Task.FromResult(true); - } - - /// - /// Queues the library scan. - /// - public void QueueLibraryScan() - { - // Just run the scheduled task so that the user can see it - _taskManager.QueueScheduledTask(); - } - - /// - /// Validates the media library internal. - /// - /// The progress. - /// The cancellation token. - /// Task. - public async Task ValidateMediaLibraryInternal(IProgress progress, CancellationToken cancellationToken) - { - IsScanRunning = true; - _libraryMonitorFactory().Stop(); - - try - { - await PerformLibraryValidation(progress, cancellationToken).ConfigureAwait(false); - } - finally - { - _libraryMonitorFactory().Start(); - IsScanRunning = false; - } - } - - private async Task PerformLibraryValidation(IProgress progress, CancellationToken cancellationToken) - { - _logger.Info("Validating media library"); - - await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false); - - progress.Report(.5); - - // Start by just validating the children of the root, but go no further - await RootFolder.ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false); - - progress.Report(1); - - var userRoot = GetUserRootFolder(); - - await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false); - - await userRoot.ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); - progress.Report(2); - - var innerProgress = new ActionableProgress(); - - innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73)); - - // Now validate the entire media library - await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false); - - progress.Report(75); - - innerProgress = new ActionableProgress(); - - innerProgress.RegisterAction(pct => progress.Report(75 + pct * .25)); - - // Run post-scan tasks - await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false); - - progress.Report(100); - } - - /// - /// Runs the post scan tasks. - /// - /// The progress. - /// The cancellation token. - /// Task. - private async Task RunPostScanTasks(IProgress progress, CancellationToken cancellationToken) - { - var tasks = PostscanTasks.ToList(); - - var numComplete = 0; - var numTasks = tasks.Count; - - foreach (var task in tasks) - { - var innerProgress = new ActionableProgress(); - - // Prevent access to modified closure - var currentNumComplete = numComplete; - - innerProgress.RegisterAction(pct => - { - double innerPercent = currentNumComplete * 100 + pct; - innerPercent /= numTasks; - progress.Report(innerPercent); - }); - - _logger.Debug("Running post-scan task {0}", task.GetType().Name); - - try - { - await task.Run(innerProgress, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - _logger.Info("Post-scan task cancelled: {0}", task.GetType().Name); - } - catch (Exception ex) - { - _logger.ErrorException("Error running postscan task", ex); - } - - numComplete++; - double percent = numComplete; - percent /= numTasks; - progress.Report(percent * 100); - } - - progress.Report(100); - } - - /// - /// Gets the default view. - /// - /// IEnumerable{VirtualFolderInfo}. - public IEnumerable GetVirtualFolders() - { - return GetView(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath); - } - - /// - /// Gets the view. - /// - /// The path. - /// IEnumerable{VirtualFolderInfo}. - private IEnumerable GetView(string path) - { - var topLibraryFolders = GetUserRootFolder().Children.ToList(); - - return _fileSystem.GetDirectoryPaths(path) - .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders)); - } - - private VirtualFolderInfo GetVirtualFolderInfo(string dir, List allCollectionFolders) - { - var info = new VirtualFolderInfo - { - Name = Path.GetFileName(dir), - - Locations = Directory.EnumerateFiles(dir, "*.mblink", SearchOption.TopDirectoryOnly) - .Select(_fileSystem.ResolveShortcut) - .OrderBy(i => i) - .ToList(), - - CollectionType = GetCollectionType(dir) - }; - - var libraryFolder = allCollectionFolders.FirstOrDefault(i => string.Equals(i.Path, dir, StringComparison.OrdinalIgnoreCase)); - - if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary)) - { - info.PrimaryImageItemId = libraryFolder.Id.ToString("N"); - } - - if (libraryFolder != null) - { - info.ItemId = libraryFolder.Id.ToString("N"); - info.LibraryOptions = GetLibraryOptions(libraryFolder); - } - - return info; - } - - private string GetCollectionType(string path) - { - return _fileSystem.GetFiles(path, false) - .Where(i => string.Equals(i.Extension, ".collection", StringComparison.OrdinalIgnoreCase)) - .Select(i => _fileSystem.GetFileNameWithoutExtension(i)) - .FirstOrDefault(); - } - - /// - /// Gets the item by id. - /// - /// The id. - /// BaseItem. - /// id - public BaseItem GetItemById(Guid id) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - BaseItem item; - - if (LibraryItemsCache.TryGetValue(id, out item)) - { - return item; - } - - item = RetrieveItem(id); - - //_logger.Debug("GetitemById {0}", id); - - if (item != null) - { - RegisterItem(item); - } - - return item; - } - - public IEnumerable GetItemList(InternalItemsQuery query) - { - if (query.Recursive && query.ParentId.HasValue) - { - var parent = GetItemById(query.ParentId.Value); - if (parent != null) - { - SetTopParentIdsOrAncestors(query, new List { parent }); - query.ParentId = null; - } - } - - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - return ItemRepository.GetItemList(query); - } - - public IEnumerable GetItemList(InternalItemsQuery query, IEnumerable parentIds) - { - var parents = parentIds.Select(i => GetItemById(new Guid(i))).Where(i => i != null).ToList(); - - SetTopParentIdsOrAncestors(query, parents); - - if (query.AncestorIds.Length == 0 && query.TopParentIds.Length == 0) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - } - - return ItemRepository.GetItemList(query); - } - - public QueryResult QueryItems(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - if (query.EnableTotalRecordCount) - { - return ItemRepository.GetItems(query); - } - - return new QueryResult - { - Items = ItemRepository.GetItemList(query).ToArray() - }; - } - - public List GetItemIds(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - return ItemRepository.GetItemIdsList(query); - } - - public QueryResult> GetStudios(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetStudios(query); - } - - public QueryResult> GetGenres(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetGenres(query); - } - - public QueryResult> GetGameGenres(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetGameGenres(query); - } - - public QueryResult> GetMusicGenres(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetMusicGenres(query); - } - - public QueryResult> GetAllArtists(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetAllArtists(query); - } - - public QueryResult> GetArtists(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetArtists(query); - } - - private void SetTopParentOrAncestorIds(InternalItemsQuery query) - { - if (query.AncestorIds.Length == 0) - { - return; - } - - var parents = query.AncestorIds.Select(i => GetItemById(new Guid(i))).ToList(); - - if (parents.All(i => - { - if (i is ICollectionFolder || i is UserView) - { - return true; - } - - //_logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name); - return false; - - })) - { - // Optimize by querying against top level views - query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray(); - query.AncestorIds = new string[] { }; - } - } - - public QueryResult> GetAlbumArtists(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetAlbumArtists(query); - } - - public QueryResult GetItemsResult(InternalItemsQuery query) - { - if (query.Recursive && query.ParentId.HasValue) - { - var parent = GetItemById(query.ParentId.Value); - if (parent != null) - { - SetTopParentIdsOrAncestors(query, new List { parent }); - query.ParentId = null; - } - } - - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - if (query.EnableTotalRecordCount) - { - return ItemRepository.GetItems(query); - } - - return new QueryResult - { - Items = ItemRepository.GetItemList(query).ToArray() - }; - } - - private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List parents) - { - if (parents.All(i => - { - if (i is ICollectionFolder || i is UserView) - { - return true; - } - - //_logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name); - return false; - - })) - { - // Optimize by querying against top level views - query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray(); - } - else - { - // We need to be able to query from any arbitrary ancestor up the tree - query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray(); - } - } - - private void AddUserToQuery(InternalItemsQuery query, User user) - { - if (query.AncestorIds.Length == 0 && - !query.ParentId.HasValue && - query.ChannelIds.Length == 0 && - query.TopParentIds.Length == 0 && - string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey) - && query.ItemIds.Length == 0) - { - var userViews = _userviewManager().GetUserViews(new UserViewQuery - { - UserId = user.Id.ToString("N"), - IncludeHidden = true - - }, CancellationToken.None).Result.ToList(); - - query.TopParentIds = userViews.SelectMany(i => GetTopParentsForQuery(i, user)).Select(i => i.Id.ToString("N")).ToArray(); - } - } - - private IEnumerable GetTopParentsForQuery(BaseItem item, User user) - { - var view = item as UserView; - - if (view != null) - { - if (string.Equals(view.ViewType, CollectionType.LiveTv)) - { - return new[] { view }; - } - if (string.Equals(view.ViewType, CollectionType.Channels)) - { - var channelResult = BaseItem.ChannelManager.GetChannelsInternal(new ChannelQuery - { - UserId = user.Id.ToString("N") - - }, CancellationToken.None).Result; - - return channelResult.Items; - } - - // Translate view into folders - if (view.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(view.DisplayParentId); - if (displayParent != null) - { - return GetTopParentsForQuery(displayParent, user); - } - return new BaseItem[] { }; - } - if (view.ParentId != Guid.Empty) - { - var displayParent = GetItemById(view.ParentId); - if (displayParent != null) - { - return GetTopParentsForQuery(displayParent, user); - } - return new BaseItem[] { }; - } - - // Handle grouping - if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)) - { - return user.RootFolder - .GetChildren(user, true) - .OfType() - .Where(i => string.IsNullOrWhiteSpace(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase)) - .Where(i => user.IsFolderGrouped(i.Id)) - .SelectMany(i => GetTopParentsForQuery(i, user)); - } - return new BaseItem[] { }; - } - - var collectionFolder = item as CollectionFolder; - if (collectionFolder != null) - { - return collectionFolder.GetPhysicalParents(); - } - - var topParent = item.GetTopParent(); - if (topParent != null) - { - return new[] { topParent }; - } - return new BaseItem[] { }; - } - - /// - /// Gets the intros. - /// - /// The item. - /// The user. - /// IEnumerable{System.String}. - public async Task> GetIntros(BaseItem item, User 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); - } - - /// - /// Gets the intros. - /// - /// The provider. - /// The item. - /// The user. - /// Task<IEnumerable<IntroInfo>>. - private async Task> 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(); - } - } - - /// - /// Gets all intro files. - /// - /// IEnumerable{System.String}. - public IEnumerable GetAllIntroFiles() - { - return IntroProviders.SelectMany(i => - { - try - { - return i.GetAllIntroFiles().ToList(); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting intro files", ex); - - return new List(); - } - }); - } - - /// - /// Resolves the intro. - /// - /// The info. - /// Video. - private Video ResolveIntro(IntroInfo info) - { - Video video = null; - - if (info.ItemId.HasValue) - { - // Get an existing item by Id - video = GetItemById(info.ItemId.Value) as Video; - - if (video == null) - { - _logger.Error("Unable to locate item with Id {0}.", info.ItemId.Value); - } - } - else if (!string.IsNullOrEmpty(info.Path)) - { - try - { - // Try to resolve the path into a video - video = ResolvePath(_fileSystem.GetFileSystemInfo(info.Path)) as Video; - - if (video == null) - { - _logger.Error("Intro resolver returned null for {0}.", info.Path); - } - else - { - // Pull the saved db item that will include metadata - var dbItem = GetItemById(video.Id) as Video; - - if (dbItem != null) - { - video = dbItem; - } - else - { - return null; - } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error resolving path {0}.", ex, info.Path); - } - } - else - { - _logger.Error("IntroProvider returned an IntroInfo with null Path and ItemId."); - } - - return video; - } - - /// - /// Sorts the specified sort by. - /// - /// The items. - /// The user. - /// The sort by. - /// The sort order. - /// IEnumerable{BaseItem}. - public IEnumerable Sort(IEnumerable items, User user, IEnumerable sortBy, SortOrder sortOrder) - { - var isFirst = true; - - IOrderedEnumerable orderedItems = null; - - foreach (var orderBy in sortBy.Select(o => GetComparer(o, user)).Where(c => c != null)) - { - if (isFirst) - { - orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, orderBy) : items.OrderBy(i => i, orderBy); - } - else - { - orderedItems = sortOrder == SortOrder.Descending ? orderedItems.ThenByDescending(i => i, orderBy) : orderedItems.ThenBy(i => i, orderBy); - } - - isFirst = false; - } - - return orderedItems ?? items; - } - - /// - /// Gets the comparer. - /// - /// The name. - /// The user. - /// IBaseItemComparer. - private IBaseItemComparer GetComparer(string name, User user) - { - var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase)); - - if (comparer != null) - { - // If it requires a user, create a new one, and assign the user - if (comparer is IUserBaseItemComparer) - { - var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType()); - - userComparer.User = user; - userComparer.UserManager = _userManager; - userComparer.UserDataRepository = _userDataRepository; - - return userComparer; - } - } - - return comparer; - } - - /// - /// Creates the item. - /// - /// The item. - /// The cancellation token. - /// Task. - public Task CreateItem(BaseItem item, CancellationToken cancellationToken) - { - return CreateItems(new[] { item }, cancellationToken); - } - - /// - /// Creates the items. - /// - /// The items. - /// The cancellation token. - /// Task. - public async Task CreateItems(IEnumerable items, CancellationToken cancellationToken) - { - var list = items.ToList(); - - await ItemRepository.SaveItems(list, cancellationToken).ConfigureAwait(false); - - foreach (var item in list) - { - RegisterItem(item); - } - - if (ItemAdded != null) - { - foreach (var item in list) - { - try - { - ItemAdded(this, new ItemChangeEventArgs { Item = item }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ItemAdded event handler", ex); - } - } - } - } - - /// - /// Updates the item. - /// - /// The item. - /// The update reason. - /// The cancellation token. - /// Task. - public async Task UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken) - { - var locationType = item.LocationType; - if (locationType != LocationType.Remote && locationType != LocationType.Virtual) - { - await _providerManagerFactory().SaveMetadata(item, updateReason).ConfigureAwait(false); - } - - item.DateLastSaved = DateTime.UtcNow; - - 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); - - RegisterItem(item); - - if (ItemUpdated != null) - { - try - { - ItemUpdated(this, new ItemChangeEventArgs - { - Item = item, - UpdateReason = updateReason - }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ItemUpdated event handler", ex); - } - } - } - - /// - /// Reports the item removed. - /// - /// The item. - public void ReportItemRemoved(BaseItem item) - { - if (ItemRemoved != null) - { - try - { - ItemRemoved(this, new ItemChangeEventArgs { Item = item }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ItemRemoved event handler", ex); - } - } - } - - /// - /// Retrieves the item. - /// - /// The id. - /// BaseItem. - public BaseItem RetrieveItem(Guid id) - { - return ItemRepository.RetrieveItem(id); - } - - public IEnumerable GetCollectionFolders(BaseItem item) - { - while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null) - { - item = item.GetParent(); - } - - if (item == null) - { - return new List(); - } - - return GetUserRootFolder().Children - .OfType() - .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase)); - } - - public LibraryOptions GetLibraryOptions(BaseItem item) - { - var collectionFolder = item as CollectionFolder; - if (collectionFolder == null) - { - collectionFolder = GetCollectionFolders(item) - .OfType() - .FirstOrDefault(); - } - - var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); - - if (options.SchemaVersion < 3) - { - options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta; - options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders; - } - - if (options.SchemaVersion < 2) - { - var chapterOptions = ConfigurationManager.GetConfiguration("chapters"); - options.ExtractChapterImagesDuringLibraryScan = chapterOptions.ExtractDuringLibraryScan; - - if (collectionFolder != null) - { - if (string.Equals(collectionFolder.CollectionType, "movies", StringComparison.OrdinalIgnoreCase)) - { - options.EnableChapterImageExtraction = chapterOptions.EnableMovieChapterImageExtraction; - } - else if (string.Equals(collectionFolder.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) - { - options.EnableChapterImageExtraction = chapterOptions.EnableEpisodeChapterImageExtraction; - } - } - } - - return options; - } - - public string GetContentType(BaseItem item) - { - string configuredContentType = GetConfiguredContentType(item, false); - if (!string.IsNullOrWhiteSpace(configuredContentType)) - { - return configuredContentType; - } - configuredContentType = GetConfiguredContentType(item, true); - if (!string.IsNullOrWhiteSpace(configuredContentType)) - { - return configuredContentType; - } - return GetInheritedContentType(item); - } - - public string GetInheritedContentType(BaseItem item) - { - var type = GetTopFolderContentType(item); - - if (!string.IsNullOrWhiteSpace(type)) - { - return type; - } - - return item.GetParents() - .Select(GetConfiguredContentType) - .LastOrDefault(i => !string.IsNullOrWhiteSpace(i)); - } - - public string GetConfiguredContentType(BaseItem item) - { - return GetConfiguredContentType(item, false); - } - - public string GetConfiguredContentType(string path) - { - 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 && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path))); - if (nameValuePair != null) - { - return nameValuePair.Value; - } - return null; - } - - private string GetTopFolderContentType(BaseItem item) - { - if (item == null) - { - return null; - } - - while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null) - { - item = item.GetParent(); - } - - return GetUserRootFolder().Children - .OfType() - .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path)) - .Select(i => i.CollectionType) - .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); - } - - private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24); - //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1); - - public Task GetNamedView(User user, - string name, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - return GetNamedView(user, name, null, viewType, sortName, cancellationToken); - } - - public async Task GetNamedView(string name, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "views"); - - path = Path.Combine(path, _fileSystem.GetValidFilename(viewType)); - - var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView)); - - var item = GetItemById(id) as UserView; - - var refresh = false; - - if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName - }; - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - refresh = true; - } - - if (!refresh) - { - refresh = DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - } - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - await item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None).ConfigureAwait(false); - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Not sure why this is necessary but need to figure it out - // View images are not getting utilized without this - ForceSave = true - }); - } - - return item; - } - - public async Task GetNamedView(User user, - string name, - string parentId, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty) + (viewType ?? string.Empty); - - var id = GetNewItemId(idValues, typeof(UserView)); - - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); - - var item = GetItemById(id) as UserView; - - var isNew = false; - - if (item == null) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName, - UserId = user.Id - }; - - if (!string.IsNullOrWhiteSpace(parentId)) - { - item.DisplayParentId = new Guid(parentId); - } - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - isNew = true; - } - - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Need to force save to increment DateLastSaved - ForceSave = true - }); - } - - return item; - } - - public async Task GetShadowView(BaseItem parent, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - if (parent == null) - { - throw new ArgumentNullException("parent"); - } - - var name = parent.Name; - var parentId = parent.Id; - - var idValues = "38_namedview_" + name + parentId + (viewType ?? string.Empty); - - var id = GetNewItemId(idValues, typeof(UserView)); - - var path = parent.Path; - - var item = GetItemById(id) as UserView; - - var isNew = false; - - if (item == null) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName - }; - - item.DisplayParentId = parentId; - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - isNew = true; - } - - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Need to force save to increment DateLastSaved - ForceSave = true - }); - } - - return item; - } - - public async Task GetNamedView(string name, - string parentId, - string viewType, - string sortName, - string uniqueId, - CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - - var idValues = "37_namedview_" + name + (parentId ?? string.Empty) + (viewType ?? string.Empty); - if (!string.IsNullOrWhiteSpace(uniqueId)) - { - idValues += uniqueId; - } - - var id = GetNewItemId(idValues, typeof(UserView)); - - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); - - var item = GetItemById(id) as UserView; - - var isNew = false; - - if (item == null) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName - }; - - if (!string.IsNullOrWhiteSpace(parentId)) - { - item.DisplayParentId = new Guid(parentId); - } - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - isNew = true; - } - - if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) - { - item.ViewType = viewType; - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); - } - - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Need to force save to increment DateLastSaved - ForceSave = true - }); - } - - return item; - } - - public bool IsVideoFile(string path, LibraryOptions libraryOptions) - { - var resolver = new VideoResolver(GetNamingOptions(libraryOptions), new PatternsLogger()); - return resolver.IsVideoFile(path); - } - - public bool IsVideoFile(string path) - { - return IsVideoFile(path, new LibraryOptions()); - } - - public bool IsAudioFile(string path, LibraryOptions libraryOptions) - { - var parser = new AudioFileParser(GetNamingOptions(libraryOptions)); - return parser.IsAudioFile(path); - } - - public bool IsAudioFile(string path) - { - return IsAudioFile(path, new LibraryOptions()); - } - - public int? GetSeasonNumberFromPath(string path) - { - return new SeasonPathParser(GetNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber; - } - - public bool FillMissingEpisodeNumbersFromPath(Episode episode) - { - var resolver = new EpisodeResolver(GetNamingOptions(), - new PatternsLogger()); - - var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || - episode.VideoType == VideoType.HdDvd; - - var locationType = episode.LocationType; - - var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ? - resolver.Resolve(episode.Path, isFolder) : - 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 NamingOptions GetNamingOptions() - { - return GetNamingOptions(new LibraryOptions()); - } - - public NamingOptions GetNamingOptions(LibraryOptions libraryOptions) - { - var options = new ExtendedNamingOptions(); - - // These cause apps to have problems - options.AudioFileExtensions.Remove(".m3u"); - options.AudioFileExtensions.Remove(".wpl"); - - if (!libraryOptions.EnableArchiveMediaFiles) - { - options.AudioFileExtensions.Remove(".rar"); - options.AudioFileExtensions.Remove(".zip"); - } - - if (!libraryOptions.EnableArchiveMediaFiles) - { - options.VideoFileExtensions.Remove(".rar"); - options.VideoFileExtensions.Remove(".zip"); - } - - return options; - } - - public ItemLookupInfo ParseName(string name) - { - var resolver = new VideoResolver(GetNamingOptions(), new PatternsLogger()); - - var result = resolver.CleanDateTime(name); - var cleanName = resolver.CleanString(result.Name); - - return new ItemLookupInfo - { - Name = cleanName.Name, - Year = result.Year - }; - } - - public IEnumerable