AggregateFolder.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using MediaBrowser.Controller.IO;
  2. using MediaBrowser.Controller.Library;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using MediaBrowser.Controller.Providers;
  10. using MediaBrowser.Model.IO;
  11. using MediaBrowser.Model.Serialization;
  12. namespace MediaBrowser.Controller.Entities
  13. {
  14. /// <summary>
  15. /// Specialized folder that can have items added to it's children by external entities.
  16. /// Used for our RootFolder so plug-ins can add items.
  17. /// </summary>
  18. public class AggregateFolder : Folder
  19. {
  20. public AggregateFolder()
  21. {
  22. PhysicalLocationsList = new List<string>();
  23. }
  24. /// <summary>
  25. /// We don't support manual shortcuts
  26. /// </summary>
  27. [IgnoreDataMember]
  28. protected override bool SupportsShortcutChildren
  29. {
  30. get
  31. {
  32. return false;
  33. }
  34. }
  35. [IgnoreDataMember]
  36. public override bool IsPhysicalRoot
  37. {
  38. get { return true; }
  39. }
  40. public override bool CanDelete()
  41. {
  42. return false;
  43. }
  44. [IgnoreDataMember]
  45. public override bool SupportsPlayedStatus
  46. {
  47. get
  48. {
  49. return false;
  50. }
  51. }
  52. /// <summary>
  53. /// The _virtual children
  54. /// </summary>
  55. private readonly ConcurrentBag<BaseItem> _virtualChildren = new ConcurrentBag<BaseItem>();
  56. /// <summary>
  57. /// Gets the virtual children.
  58. /// </summary>
  59. /// <value>The virtual children.</value>
  60. public ConcurrentBag<BaseItem> VirtualChildren
  61. {
  62. get { return _virtualChildren; }
  63. }
  64. [IgnoreDataMember]
  65. public override IEnumerable<string> PhysicalLocations
  66. {
  67. get
  68. {
  69. return PhysicalLocationsList;
  70. }
  71. }
  72. public List<string> PhysicalLocationsList { get; set; }
  73. protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
  74. {
  75. return CreateResolveArgs(directoryService, true).FileSystemChildren;
  76. }
  77. private List<Guid> _childrenIds = null;
  78. private readonly object _childIdsLock = new object();
  79. protected override IEnumerable<BaseItem> LoadChildren()
  80. {
  81. lock (_childIdsLock)
  82. {
  83. if (_childrenIds == null || _childrenIds.Count == 0)
  84. {
  85. var list = base.LoadChildren().ToList();
  86. _childrenIds = list.Select(i => i.Id).ToList();
  87. return list;
  88. }
  89. return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList();
  90. }
  91. }
  92. private void ClearCache()
  93. {
  94. lock (_childIdsLock)
  95. {
  96. _childrenIds = null;
  97. }
  98. }
  99. private bool _requiresRefresh;
  100. public override bool RequiresRefresh()
  101. {
  102. var changed = base.RequiresRefresh() || _requiresRefresh;
  103. if (!changed)
  104. {
  105. var locations = PhysicalLocations.ToList();
  106. var newLocations = CreateResolveArgs(new DirectoryService(Logger, FileSystem), false).PhysicalLocations.ToList();
  107. if (!locations.SequenceEqual(newLocations))
  108. {
  109. changed = true;
  110. }
  111. }
  112. return changed;
  113. }
  114. public override bool BeforeMetadataRefresh()
  115. {
  116. ClearCache();
  117. var changed = base.BeforeMetadataRefresh() || _requiresRefresh;
  118. _requiresRefresh = false;
  119. return changed;
  120. }
  121. private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations)
  122. {
  123. ClearCache();
  124. var path = ContainingFolderPath;
  125. var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
  126. {
  127. FileInfo = FileSystem.GetDirectoryInfo(path),
  128. Path = path,
  129. Parent = Parent
  130. };
  131. // Gather child folder and files
  132. if (args.IsDirectory)
  133. {
  134. var isPhysicalRoot = args.IsPhysicalRoot;
  135. // When resolving the root, we need it's grandchildren (children of user views)
  136. var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
  137. var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
  138. // Need to remove subpaths that may have been resolved from shortcuts
  139. // Example: if \\server\movies exists, then strip out \\server\movies\action
  140. if (isPhysicalRoot)
  141. {
  142. var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Values);
  143. fileSystemDictionary = paths.ToDictionary(i => i.FullName);
  144. }
  145. args.FileSystemDictionary = fileSystemDictionary;
  146. }
  147. _requiresRefresh = _requiresRefresh || !args.PhysicalLocations.SequenceEqual(PhysicalLocations);
  148. if (setPhysicalLocations)
  149. {
  150. PhysicalLocationsList = args.PhysicalLocations.ToList();
  151. }
  152. return args;
  153. }
  154. protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
  155. {
  156. return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren);
  157. }
  158. protected override async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
  159. {
  160. ClearCache();
  161. await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService)
  162. .ConfigureAwait(false);
  163. ClearCache();
  164. }
  165. /// <summary>
  166. /// Adds the virtual child.
  167. /// </summary>
  168. /// <param name="child">The child.</param>
  169. /// <exception cref="System.ArgumentNullException"></exception>
  170. public void AddVirtualChild(BaseItem child)
  171. {
  172. if (child == null)
  173. {
  174. throw new ArgumentNullException();
  175. }
  176. _virtualChildren.Add(child);
  177. }
  178. /// <summary>
  179. /// Finds the virtual child.
  180. /// </summary>
  181. /// <param name="id">The id.</param>
  182. /// <returns>BaseItem.</returns>
  183. /// <exception cref="System.ArgumentNullException">id</exception>
  184. public BaseItem FindVirtualChild(Guid id)
  185. {
  186. if (id == Guid.Empty)
  187. {
  188. throw new ArgumentNullException("id");
  189. }
  190. return _virtualChildren.FirstOrDefault(i => i.Id == id);
  191. }
  192. }
  193. }