CollectionManager.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. using MediaBrowser.Common.IO;
  2. using MediaBrowser.Controller.Collections;
  3. using MediaBrowser.Controller.Entities;
  4. using MediaBrowser.Controller.Entities.Movies;
  5. using MediaBrowser.Controller.Library;
  6. using MediaBrowser.Controller.Providers;
  7. using MoreLinq;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. namespace MediaBrowser.Server.Implementations.Collections
  15. {
  16. public class CollectionManager : ICollectionManager
  17. {
  18. private readonly ILibraryManager _libraryManager;
  19. private readonly IFileSystem _fileSystem;
  20. private readonly ILibraryMonitor _iLibraryMonitor;
  21. public CollectionManager(ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor)
  22. {
  23. _libraryManager = libraryManager;
  24. _fileSystem = fileSystem;
  25. _iLibraryMonitor = iLibraryMonitor;
  26. }
  27. public Folder GetCollectionsFolder(string userId)
  28. {
  29. return _libraryManager.RootFolder.Children.Concat(_libraryManager.RootFolder).OfType<ManualCollectionsFolder>()
  30. .FirstOrDefault();
  31. }
  32. public async Task<BoxSet> CreateCollection(CollectionCreationOptions options)
  33. {
  34. var name = options.Name;
  35. // Need to use the [boxset] suffix
  36. // If internet metadata is not found, or if xml saving is off there will be no collection.xml
  37. // This could cause it to get re-resolved as a plain folder
  38. var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
  39. var parentFolder = GetParentFolder(options.ParentId);
  40. if (parentFolder == null)
  41. {
  42. throw new ArgumentException();
  43. }
  44. var path = Path.Combine(parentFolder.Path, folderName);
  45. _iLibraryMonitor.ReportFileSystemChangeBeginning(path);
  46. try
  47. {
  48. Directory.CreateDirectory(path);
  49. var collection = new BoxSet
  50. {
  51. Name = name,
  52. Parent = parentFolder,
  53. DisplayMediaType = "Collection",
  54. Path = path,
  55. IsLocked = options.IsLocked,
  56. ProviderIds = options.ProviderIds
  57. };
  58. await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false);
  59. await collection.RefreshMetadata(new MetadataRefreshOptions(), CancellationToken.None)
  60. .ConfigureAwait(false);
  61. if (options.ItemIdList.Count > 0)
  62. {
  63. await AddToCollection(collection.Id, options.ItemIdList);
  64. }
  65. return collection;
  66. }
  67. finally
  68. {
  69. // Refresh handled internally
  70. _iLibraryMonitor.ReportFileSystemChangeComplete(path, false);
  71. }
  72. }
  73. private Folder GetParentFolder(Guid? parentId)
  74. {
  75. if (parentId.HasValue)
  76. {
  77. if (parentId.Value == Guid.Empty)
  78. {
  79. throw new ArgumentNullException("parentId");
  80. }
  81. var folder = _libraryManager.GetItemById(parentId.Value) as Folder;
  82. // Find an actual physical folder
  83. if (folder is CollectionFolder)
  84. {
  85. var child = _libraryManager.RootFolder.Children.OfType<Folder>()
  86. .FirstOrDefault(i => folder.PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
  87. if (child != null)
  88. {
  89. return child;
  90. }
  91. }
  92. }
  93. return GetCollectionsFolder(string.Empty);
  94. }
  95. public async Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
  96. {
  97. var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
  98. if (collection == null)
  99. {
  100. throw new ArgumentException("No collection exists with the supplied Id");
  101. }
  102. var list = new List<LinkedChild>();
  103. var currentLinkedChildren = collection.GetLinkedChildren().ToList();
  104. foreach (var itemId in ids)
  105. {
  106. var item = _libraryManager.GetItemById(itemId);
  107. if (item == null)
  108. {
  109. throw new ArgumentException("No item exists with the supplied Id");
  110. }
  111. if (currentLinkedChildren.Any(i => i.Id == itemId))
  112. {
  113. throw new ArgumentException("Item already exists in collection");
  114. }
  115. list.Add(new LinkedChild
  116. {
  117. ItemName = item.Name,
  118. ItemYear = item.ProductionYear,
  119. ItemType = item.GetType().Name,
  120. Type = LinkedChildType.Manual
  121. });
  122. var supportsGrouping = item as ISupportsBoxSetGrouping;
  123. if (supportsGrouping != null)
  124. {
  125. var boxsetIdList = supportsGrouping.BoxSetIdList.ToList();
  126. if (!boxsetIdList.Contains(collectionId))
  127. {
  128. boxsetIdList.Add(collectionId);
  129. }
  130. supportsGrouping.BoxSetIdList = boxsetIdList;
  131. }
  132. }
  133. collection.LinkedChildren.AddRange(list);
  134. await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
  135. await collection.RefreshMetadata(CancellationToken.None).ConfigureAwait(false);
  136. }
  137. public async Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
  138. {
  139. var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
  140. if (collection == null)
  141. {
  142. throw new ArgumentException("No collection exists with the supplied Id");
  143. }
  144. var list = new List<LinkedChild>();
  145. foreach (var itemId in itemIds)
  146. {
  147. var child = collection.LinkedChildren.FirstOrDefault(i => i.ItemId.HasValue && i.ItemId.Value == itemId);
  148. if (child == null)
  149. {
  150. throw new ArgumentException("No collection title exists with the supplied Id");
  151. }
  152. list.Add(child);
  153. var childItem = _libraryManager.GetItemById(itemId);
  154. var supportsGrouping = childItem as ISupportsBoxSetGrouping;
  155. if (supportsGrouping != null)
  156. {
  157. var boxsetIdList = supportsGrouping.BoxSetIdList.ToList();
  158. boxsetIdList.Remove(collectionId);
  159. supportsGrouping.BoxSetIdList = boxsetIdList;
  160. }
  161. }
  162. var shortcutFiles = Directory
  163. .EnumerateFiles(collection.Path, "*", SearchOption.TopDirectoryOnly)
  164. .Where(i => _fileSystem.IsShortcut(i))
  165. .ToList();
  166. var shortcutFilesToDelete = list.Where(child => !string.IsNullOrWhiteSpace(child.Path) && child.Type == LinkedChildType.Shortcut)
  167. .Select(child => shortcutFiles.FirstOrDefault(i => string.Equals(child.Path, _fileSystem.ResolveShortcut(i), StringComparison.OrdinalIgnoreCase)))
  168. .Where(i => !string.IsNullOrWhiteSpace(i))
  169. .ToList();
  170. foreach (var file in shortcutFilesToDelete)
  171. {
  172. _iLibraryMonitor.ReportFileSystemChangeBeginning(file);
  173. }
  174. try
  175. {
  176. foreach (var file in shortcutFilesToDelete)
  177. {
  178. File.Delete(file);
  179. }
  180. foreach (var child in list)
  181. {
  182. collection.LinkedChildren.Remove(child);
  183. }
  184. }
  185. finally
  186. {
  187. foreach (var file in shortcutFilesToDelete)
  188. {
  189. _iLibraryMonitor.ReportFileSystemChangeComplete(file, false);
  190. }
  191. }
  192. await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
  193. await collection.RefreshMetadata(CancellationToken.None).ConfigureAwait(false);
  194. }
  195. public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
  196. {
  197. var itemsToCollapse = new List<ISupportsBoxSetGrouping>();
  198. var boxsets = new List<BaseItem>();
  199. var list = items.ToList();
  200. foreach (var item in list.OfType<ISupportsBoxSetGrouping>())
  201. {
  202. var currentBoxSets = item.BoxSetIdList
  203. .Select(i => _libraryManager.GetItemById(i))
  204. .Where(i => i != null && i.IsVisible(user))
  205. .ToList();
  206. if (currentBoxSets.Count > 0)
  207. {
  208. itemsToCollapse.Add(item);
  209. boxsets.AddRange(currentBoxSets);
  210. }
  211. }
  212. return list
  213. .Except(itemsToCollapse.Cast<BaseItem>())
  214. .Concat(boxsets)
  215. .DistinctBy(i => i.Id);
  216. }
  217. }
  218. }