using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities { /// /// Specialized Folder class that points to a subset of the physical folders in the system. /// It is created from the user-specific folders within the system root /// public class CollectionFolder : Folder, ICollectionFolder { public CollectionFolder() { PhysicalLocationsList = new List(); } /// /// Gets a value indicating whether this instance is virtual folder. /// /// true if this instance is virtual folder; otherwise, false. [IgnoreDataMember] public override bool IsVirtualFolder { get { return true; } } public string CollectionType { get; set; } /// /// Allow different display preferences for each collection folder /// /// The display prefs id. [IgnoreDataMember] public override Guid DisplayPreferencesId { get { return Id; } } [IgnoreDataMember] public override IEnumerable PhysicalLocations { get { return PhysicalLocationsList; } } public List PhysicalLocationsList { get; set; } protected override IEnumerable GetFileSystemChildren(IDirectoryService directoryService) { return CreateResolveArgs(directoryService).FileSystemChildren; } private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService) { var path = ContainingFolderPath; var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager) { FileInfo = new DirectoryInfo(path), Path = path, Parent = Parent }; // 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 = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys); fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName); } args.FileSystemDictionary = fileSystemDictionary; } PhysicalLocationsList = args.PhysicalLocations.ToList(); return args; } // Cache this since it will be used a lot /// /// The null task result /// private static readonly Task NullTaskResult = Task.FromResult(null); /// /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes /// ***Currently does not contain logic to maintain items that are unavailable in the file system*** /// /// The progress. /// The cancellation token. /// if set to true [recursive]. /// if set to true [refresh child metadata]. /// The refresh options. /// The directory service. /// Task. protected override Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { CreateResolveArgs(directoryService); ResetDynamicChildren(); return NullTaskResult; } private List _linkedChildren; /// /// Our children are actually just references to the ones in the physical root... /// /// The linked children. public override List LinkedChildren { get { return _linkedChildren ?? (_linkedChildren = GetLinkedChildrenInternal()); } set { base.LinkedChildren = value; } } private List GetLinkedChildrenInternal() { Dictionary locationsDicionary; try { locationsDicionary = PhysicalLocations.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); } catch (IOException ex) { Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path); return new List(); } return LibraryManager.RootFolder.Children .OfType() .Where(i => i.Path != null && locationsDicionary.ContainsKey(i.Path)) .SelectMany(c => c.LinkedChildren) .ToList(); } private IEnumerable _actualChildren; /// /// Our children are actually just references to the ones in the physical root... /// /// The actual children. protected override IEnumerable ActualChildren { get { return _actualChildren ?? (_actualChildren = GetActualChildren()); } } private IEnumerable GetActualChildren() { Dictionary locationsDicionary; try { locationsDicionary = PhysicalLocations.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); } catch (IOException ex) { Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path); return new BaseItem[] { }; } return LibraryManager.RootFolder.Children .OfType() .Where(i => i.Path != null && locationsDicionary.ContainsKey(i.Path)) .SelectMany(c => c.Children) .ToList(); } public void ResetDynamicChildren() { _actualChildren = null; _linkedChildren = null; } } }