From af7aa597c35279e286ee88641854db69744e7b15 Mon Sep 17 00:00:00 2001 From: LukePulverenti Date: Thu, 28 Feb 2013 14:32:41 -0500 Subject: referenced core plugins, fixed some dashboard issues, extracted library manager --- MediaBrowser.Controller/Library/LibraryManager.cs | 562 ---------------------- 1 file changed, 562 deletions(-) delete mode 100644 MediaBrowser.Controller/Library/LibraryManager.cs (limited to 'MediaBrowser.Controller/Library/LibraryManager.cs') diff --git a/MediaBrowser.Controller/Library/LibraryManager.cs b/MediaBrowser.Controller/Library/LibraryManager.cs deleted file mode 100644 index 5dbf5c72d..000000000 --- a/MediaBrowser.Controller/Library/LibraryManager.cs +++ /dev/null @@ -1,562 +0,0 @@ -using MediaBrowser.Common.Events; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.IO; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Controller.ScheduledTasks; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MoreLinq; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Library -{ - /// - /// Class LibraryManager - /// - public class LibraryManager - { - #region LibraryChanged Event - /// - /// Fires whenever any validation routine adds or removes items. The added and removed items are properties of the args. - /// *** Will fire asynchronously. *** - /// - public event EventHandler LibraryChanged; - - /// - /// Raises the event. - /// - /// The instance containing the event data. - internal void OnLibraryChanged(ChildrenChangedEventArgs args) - { - EventHelper.QueueEventIfNotNull(LibraryChanged, this, args, _logger); - - // Had to put this in a separate method to avoid an implicitly captured closure - SendLibraryChangedWebSocketMessage(args); - } - - /// - /// Sends the library changed web socket message. - /// - /// The instance containing the event data. - private void SendLibraryChangedWebSocketMessage(ChildrenChangedEventArgs args) - { - // Notify connected ui's - Kernel.ServerManager.SendWebSocketMessage("LibraryChanged", () => DtoBuilder.GetLibraryUpdateInfo(args)); - } - #endregion - - /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// The _task manager - /// - private readonly ITaskManager _taskManager; - - private readonly IUserManager _userManager; - - /// - /// Gets or sets the kernel. - /// - /// The kernel. - private Kernel Kernel { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The kernel. - /// The logger. - /// The task manager. - public LibraryManager(Kernel kernel, ILogger logger, ITaskManager taskManager, IUserManager userManager) - { - Kernel = kernel; - _logger = logger; - _taskManager = taskManager; - _userManager = userManager; - - kernel.ConfigurationUpdated += kernel_ConfigurationUpdated; - } - - /// - /// Handles the ConfigurationUpdated event of the kernel control. - /// - /// The source of the event. - /// The instance containing the event data. - void kernel_ConfigurationUpdated(object sender, EventArgs e) - { - //// Figure out whether or not we should refresh people after the update is finished - //var refreshPeopleAfterUpdate = !oldConfiguration.EnableInternetProviders && config.EnableInternetProviders; - - //// This is true if internet providers has just been turned on, or if People have just been removed from InternetProviderExcludeTypes - //if (!refreshPeopleAfterUpdate) - //{ - // var oldConfigurationFetchesPeopleImages = oldConfiguration.InternetProviderExcludeTypes == null || !oldConfiguration.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase); - // var newConfigurationFetchesPeopleImages = config.InternetProviderExcludeTypes == null || !config.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase); - - // refreshPeopleAfterUpdate = newConfigurationFetchesPeopleImages && !oldConfigurationFetchesPeopleImages; - //} - - Task.Run(() => - { - // Any number of configuration settings could change the way the library is refreshed, so do that now - _taskManager.CancelIfRunningAndQueue(); - _taskManager.CancelIfRunningAndQueue(); - }); - } - - /// - /// Resolves the item. - /// - /// The args. - /// BaseItem. - public BaseItem ResolveItem(ItemResolveArgs args) - { - return Kernel.EntityResolvers.Select(r => r.ResolvePath(args)).FirstOrDefault(i => i != null); - } - - /// - /// Resolves a path into a BaseItem - /// - /// The path. - /// The parent. - /// The file info. - /// BaseItem. - /// - public BaseItem GetItem(string path, Folder parent = null, WIN32_FIND_DATA? fileInfo = null) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(); - } - - fileInfo = fileInfo ?? FileSystem.GetFileData(path); - - if (!fileInfo.HasValue) - { - return null; - } - - var args = new ItemResolveArgs - { - Parent = parent, - Path = path, - FileInfo = fileInfo.Value - }; - - // Return null if ignore rules deem that we should do so - if (Kernel.EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(args))) - { - return null; - } - - // Gather child folder and files - if (args.IsDirectory) - { - // When resolving the root, we need it's grandchildren (children of user views) - var flattenFolderDepth = args.IsPhysicalRoot ? 2 : 0; - - args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _logger, flattenFolderDepth: flattenFolderDepth, args: args); - } - - // Check to see if we should resolve based on our contents - if (args.IsDirectory && !EntityResolutionHelper.ShouldResolvePathContents(args)) - { - return null; - } - - return ResolveItem(args); - } - - /// - /// Resolves a set of files into a list of BaseItem - /// - /// - /// The files. - /// The parent. - /// List{``0}. - public List GetItems(IEnumerable files, Folder parent) - where T : BaseItem - { - var list = new List(); - - Parallel.ForEach(files, f => - { - try - { - var item = GetItem(f.Path, parent, f) as T; - - if (item != null) - { - lock (list) - { - list.Add(item); - } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error resolving path {0}", ex, f.Path); - } - }); - - return list; - } - - /// - /// Creates the root media folder - /// - /// AggregateFolder. - /// Cannot create the root folder until plugins have loaded - internal AggregateFolder CreateRootFolder() - { - if (Kernel.Plugins == null) - { - throw new InvalidOperationException("Cannot create the root folder until plugins have loaded"); - } - - var rootFolderPath = Kernel.ApplicationPaths.RootFolderPath; - var rootFolder = Kernel.ItemRepository.RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)GetItem(rootFolderPath); - - // Add in the plug-in folders - foreach (var child in Kernel.PluginFolderCreators) - { - rootFolder.AddVirtualChild(child.GetFolder()); - } - - return rootFolder; - } - - /// - /// Gets a Person - /// - /// The name. - /// if set to true [allow slow providers]. - /// Task{Person}. - public Task GetPerson(string name, bool allowSlowProviders = false) - { - return GetPerson(name, CancellationToken.None, allowSlowProviders); - } - - /// - /// Gets a Person - /// - /// The name. - /// The cancellation token. - /// if set to true [allow slow providers]. - /// Task{Person}. - private Task GetPerson(string name, CancellationToken cancellationToken, bool allowSlowProviders = false) - { - return GetImagesByNameItem(Kernel.ApplicationPaths.PeoplePath, name, cancellationToken, allowSlowProviders); - } - - /// - /// Gets a Studio - /// - /// The name. - /// if set to true [allow slow providers]. - /// Task{Studio}. - public Task GetStudio(string name, bool allowSlowProviders = false) - { - return GetImagesByNameItem(Kernel.ApplicationPaths.StudioPath, name, CancellationToken.None, allowSlowProviders); - } - - /// - /// Gets a Genre - /// - /// The name. - /// if set to true [allow slow providers]. - /// Task{Genre}. - public Task GetGenre(string name, bool allowSlowProviders = false) - { - return GetImagesByNameItem(Kernel.ApplicationPaths.GenrePath, name, CancellationToken.None, allowSlowProviders); - } - - /// - /// The us culture - /// - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// - /// Gets a Year - /// - /// The value. - /// if set to true [allow slow providers]. - /// Task{Year}. - /// - public Task GetYear(int value, bool allowSlowProviders = false) - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(); - } - - return GetImagesByNameItem(Kernel.ApplicationPaths.YearPath, value.ToString(UsCulture), CancellationToken.None, allowSlowProviders); - } - - /// - /// The images by name item cache - /// - private readonly ConcurrentDictionary ImagesByNameItemCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - /// - /// Generically retrieves an IBN item - /// - /// - /// The path. - /// The name. - /// The cancellation token. - /// if set to true [allow slow providers]. - /// Task{``0}. - /// - private Task GetImagesByNameItem(string path, string name, CancellationToken cancellationToken, bool allowSlowProviders = true) - where T : BaseItem, new() - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(); - } - - if (string.IsNullOrEmpty(name)) - { - throw new ArgumentNullException(); - } - - var key = Path.Combine(path, FileSystem.GetValidFilename(name)); - - var obj = ImagesByNameItemCache.GetOrAdd(key, keyname => CreateImagesByNameItem(path, name, cancellationToken, allowSlowProviders)); - - return obj as Task; - } - - /// - /// Creates an IBN item based on a given path - /// - /// - /// The path. - /// The name. - /// The cancellation token. - /// if set to true [allow slow providers]. - /// Task{``0}. - /// Path not created: + path - private async Task CreateImagesByNameItem(string path, string name, CancellationToken cancellationToken, bool allowSlowProviders = true) - where T : BaseItem, new() - { - cancellationToken.ThrowIfCancellationRequested(); - - _logger.Debug("Creating {0}: {1}", typeof(T).Name, name); - - path = Path.Combine(path, FileSystem.GetValidFilename(name)); - - var fileInfo = FileSystem.GetFileData(path); - - var isNew = false; - - if (!fileInfo.HasValue) - { - Directory.CreateDirectory(path); - fileInfo = FileSystem.GetFileData(path); - - if (!fileInfo.HasValue) - { - throw new IOException("Path not created: " + path); - } - - isNew = true; - } - - cancellationToken.ThrowIfCancellationRequested(); - - var id = path.GetMBId(typeof(T)); - - var item = Kernel.ItemRepository.RetrieveItem(id) as T; - if (item == null) - { - item = new T - { - Name = name, - Id = id, - DateCreated = fileInfo.Value.CreationTimeUtc, - DateModified = fileInfo.Value.LastWriteTimeUtc, - Path = path - }; - isNew = true; - } - - cancellationToken.ThrowIfCancellationRequested(); - - // Set this now so we don't cause additional file system access during provider executions - item.ResetResolveArgs(fileInfo); - - await item.RefreshMetadata(cancellationToken, isNew, allowSlowProviders: allowSlowProviders).ConfigureAwait(false); - - cancellationToken.ThrowIfCancellationRequested(); - - return item; - } - - /// - /// 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. - internal async Task ValidatePeople(CancellationToken cancellationToken, IProgress progress) - { - // Clear the IBN cache - ImagesByNameItemCache.Clear(); - - const int maxTasks = 250; - - var tasks = new List(); - - var includedPersonTypes = new[] { PersonType.Actor, PersonType.Director }; - - var people = Kernel.RootFolder.RecursiveChildren - .Where(c => c.People != null) - .SelectMany(c => c.People.Where(p => includedPersonTypes.Contains(p.Type))) - .DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase) - .ToList(); - - var numComplete = 0; - - foreach (var person in people) - { - if (tasks.Count > maxTasks) - { - await Task.WhenAll(tasks).ConfigureAwait(false); - tasks.Clear(); - - // Safe cancellation point, when there are no pending tasks - cancellationToken.ThrowIfCancellationRequested(); - } - - // Avoid accessing the foreach variable within the closure - var currentPerson = person; - - tasks.Add(Task.Run(async () => - { - cancellationToken.ThrowIfCancellationRequested(); - - try - { - await GetPerson(currentPerson.Name, cancellationToken, allowSlowProviders: true).ConfigureAwait(false); - } - catch (IOException ex) - { - _logger.ErrorException("Error validating IBN entry {0}", ex, currentPerson.Name); - } - - // Update progress - lock (progress) - { - numComplete++; - double percent = numComplete; - percent /= people.Count; - - progress.Report(100 * percent); - } - })); - } - - await Task.WhenAll(tasks).ConfigureAwait(false); - - progress.Report(100); - - _logger.Info("People validation complete"); - } - - /// - /// Reloads the root media folder - /// - /// The progress. - /// The cancellation token. - /// Task. - internal async Task ValidateMediaLibrary(IProgress progress, CancellationToken cancellationToken) - { - _logger.Info("Validating media library"); - - await Kernel.RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false); - - // Start by just validating the children of the root, but go no further - await Kernel.RootFolder.ValidateChildren(new Progress { }, cancellationToken, recursive: false); - - // Validate only the collection folders for each user, just to make them available as quickly as possible - var userCollectionFolderTasks = _userManager.Users.AsParallel().Select(user => user.ValidateCollectionFolders(new Progress { }, cancellationToken)); - await Task.WhenAll(userCollectionFolderTasks).ConfigureAwait(false); - - // Now validate the entire media library - await Kernel.RootFolder.ValidateChildren(progress, cancellationToken, recursive: true).ConfigureAwait(false); - - foreach (var user in _userManager.Users) - { - await user.ValidateMediaLibrary(new Progress { }, cancellationToken).ConfigureAwait(false); - } - } - - /// - /// Saves display preferences for a Folder - /// - /// The user. - /// The folder. - /// The data. - /// Task. - public Task SaveDisplayPreferencesForFolder(User user, Folder folder, DisplayPreferences data) - { - // Need to update all items with the same DisplayPrefsId - foreach (var child in Kernel.RootFolder.GetRecursiveChildren(user) - .OfType() - .Where(i => i.DisplayPrefsId == folder.DisplayPrefsId)) - { - child.AddOrUpdateDisplayPrefs(user, data); - } - - return Kernel.DisplayPreferencesRepository.SaveDisplayPrefs(folder, CancellationToken.None); - } - - /// - /// Gets the default view. - /// - /// IEnumerable{VirtualFolderInfo}. - public IEnumerable GetDefaultVirtualFolders() - { - return GetView(Kernel.ApplicationPaths.DefaultUserViewsPath); - } - - /// - /// Gets the view. - /// - /// The user. - /// IEnumerable{VirtualFolderInfo}. - public IEnumerable GetVirtualFolders(User user) - { - return GetView(user.RootFolderPath); - } - - /// - /// Gets the view. - /// - /// The path. - /// IEnumerable{VirtualFolderInfo}. - private IEnumerable GetView(string path) - { - return Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly) - .Select(dir => new VirtualFolderInfo - { - Name = Path.GetFileName(dir), - Locations = Directory.EnumerateFiles(dir, "*.lnk", SearchOption.TopDirectoryOnly).Select(FileSystem.ResolveShortcut).ToList() - }); - } - } -} -- cgit v1.2.3