ItemController.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using MediaBrowser.Common.Events;
  7. using MediaBrowser.Controller.Events;
  8. using MediaBrowser.Controller.IO;
  9. using MediaBrowser.Controller.Resolvers;
  10. using MediaBrowser.Model.Entities;
  11. namespace MediaBrowser.Controller.Library
  12. {
  13. public class ItemController
  14. {
  15. #region PreBeginResolvePath Event
  16. /// <summary>
  17. /// Fires when a path is about to be resolved, but before child folders and files
  18. /// have been collected from the file system.
  19. /// This gives listeners a chance to cancel the operation and cause the path to be ignored.
  20. /// </summary>
  21. public event EventHandler<PreBeginResolveEventArgs> PreBeginResolvePath;
  22. private bool OnPreBeginResolvePath(Folder parent, string path, FileAttributes attributes)
  23. {
  24. PreBeginResolveEventArgs args = new PreBeginResolveEventArgs()
  25. {
  26. Path = path,
  27. Parent = parent,
  28. FileAttributes = attributes,
  29. Cancel = false
  30. };
  31. if (PreBeginResolvePath != null)
  32. {
  33. PreBeginResolvePath(this, args);
  34. }
  35. return !args.Cancel;
  36. }
  37. #endregion
  38. #region BeginResolvePath Event
  39. /// <summary>
  40. /// Fires when a path is about to be resolved, but after child folders and files
  41. /// have been collected from the file system.
  42. /// This gives listeners a chance to cancel the operation and cause the path to be ignored.
  43. /// </summary>
  44. public event EventHandler<ItemResolveEventArgs> BeginResolvePath;
  45. private bool OnBeginResolvePath(ItemResolveEventArgs args)
  46. {
  47. if (BeginResolvePath != null)
  48. {
  49. BeginResolvePath(this, args);
  50. }
  51. return !args.Cancel;
  52. }
  53. #endregion
  54. #region Item Events
  55. /// <summary>
  56. /// Called when an item is being created.
  57. /// This should be used to fill item values, such as metadata
  58. /// </summary>
  59. public event EventHandler<GenericItemEventArgs<BaseItem>> ItemCreating;
  60. /// <summary>
  61. /// Called when an item has been created.
  62. /// This should be used to process or modify item values.
  63. /// </summary>
  64. public event EventHandler<GenericItemEventArgs<BaseItem>> ItemCreated;
  65. #endregion
  66. /// <summary>
  67. /// Called when an item has been created
  68. /// </summary>
  69. private void OnItemCreated(BaseItem item, Folder parent)
  70. {
  71. GenericItemEventArgs<BaseItem> args = new GenericItemEventArgs<BaseItem> { Item = item };
  72. if (ItemCreating != null)
  73. {
  74. ItemCreating(this, args);
  75. }
  76. if (ItemCreated != null)
  77. {
  78. ItemCreated(this, args);
  79. }
  80. }
  81. private void FireCreateEventsRecursive(Folder folder, Folder parent)
  82. {
  83. OnItemCreated(folder, parent);
  84. int count = folder.Children.Length;
  85. Parallel.For(0, count, i =>
  86. {
  87. BaseItem item = folder.Children[i];
  88. Folder childFolder = item as Folder;
  89. if (childFolder != null)
  90. {
  91. FireCreateEventsRecursive(childFolder, folder);
  92. }
  93. else
  94. {
  95. OnItemCreated(item, folder);
  96. }
  97. });
  98. }
  99. private BaseItem ResolveItem(ItemResolveEventArgs args)
  100. {
  101. // If that didn't pan out, try the slow ones
  102. foreach (IBaseItemResolver resolver in Kernel.Instance.EntityResolvers)
  103. {
  104. var item = resolver.ResolvePath(args);
  105. if (item != null)
  106. {
  107. return item;
  108. }
  109. }
  110. return null;
  111. }
  112. /// <summary>
  113. /// Resolves a path into a BaseItem
  114. /// </summary>
  115. public BaseItem GetItem(string path)
  116. {
  117. return GetItem(null, path);
  118. }
  119. /// <summary>
  120. /// Resolves a path into a BaseItem
  121. /// </summary>
  122. public BaseItem GetItem(Folder parent, string path)
  123. {
  124. BaseItem item = GetItemInternal(parent, path, File.GetAttributes(path));
  125. if (item != null)
  126. {
  127. var folder = item as Folder;
  128. if (folder != null)
  129. {
  130. FireCreateEventsRecursive(folder, parent);
  131. }
  132. else
  133. {
  134. OnItemCreated(item, parent);
  135. }
  136. }
  137. return item;
  138. }
  139. /// <summary>
  140. /// Resolves a path into a BaseItem
  141. /// </summary>
  142. private BaseItem GetItemInternal(Folder parent, string path, FileAttributes attributes)
  143. {
  144. if (!OnPreBeginResolvePath(parent, path, attributes))
  145. {
  146. return null;
  147. }
  148. IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren;
  149. // Gather child folder and files
  150. if (attributes.HasFlag(FileAttributes.Directory))
  151. {
  152. fileSystemChildren = Directory.GetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, FileAttributes>(f, File.GetAttributes(f)));
  153. bool isVirtualFolder = parent != null && parent.IsRoot;
  154. fileSystemChildren = FilterChildFileSystemEntries(fileSystemChildren, isVirtualFolder);
  155. }
  156. else
  157. {
  158. fileSystemChildren = new KeyValuePair<string, FileAttributes>[] { };
  159. }
  160. ItemResolveEventArgs args = new ItemResolveEventArgs()
  161. {
  162. Path = path,
  163. FileAttributes = attributes,
  164. FileSystemChildren = fileSystemChildren,
  165. Parent = parent,
  166. Cancel = false
  167. };
  168. // Fire BeginResolvePath to see if anyone wants to cancel this operation
  169. if (!OnBeginResolvePath(args))
  170. {
  171. return null;
  172. }
  173. BaseItem item = ResolveItem(args);
  174. var folder = item as Folder;
  175. if (folder != null)
  176. {
  177. // If it's a folder look for child entities
  178. AttachChildren(folder, fileSystemChildren);
  179. }
  180. return item;
  181. }
  182. /// <summary>
  183. /// Finds child BaseItems for a given Folder
  184. /// </summary>
  185. private void AttachChildren(Folder folder, IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren)
  186. {
  187. List<BaseItem> baseItemChildren = new List<BaseItem>();
  188. int count = fileSystemChildren.Count();
  189. // Resolve the child folder paths into entities
  190. Parallel.For(0, count, i =>
  191. {
  192. KeyValuePair<string, FileAttributes> child = fileSystemChildren.ElementAt(i);
  193. BaseItem item = GetItemInternal(folder, child.Key, child.Value);
  194. if (item != null)
  195. {
  196. lock (baseItemChildren)
  197. {
  198. baseItemChildren.Add(item);
  199. }
  200. }
  201. });
  202. // Sort them
  203. folder.Children = baseItemChildren.OrderBy(f =>
  204. {
  205. return string.IsNullOrEmpty(f.SortName) ? f.Name : f.SortName;
  206. }).ToArray();
  207. }
  208. /// <summary>
  209. /// Transforms shortcuts into their actual paths
  210. /// </summary>
  211. private List<KeyValuePair<string, FileAttributes>> FilterChildFileSystemEntries(IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren, bool flattenShortcuts)
  212. {
  213. List<KeyValuePair<string, FileAttributes>> returnFiles = new List<KeyValuePair<string, FileAttributes>>();
  214. // Loop through each file
  215. foreach (KeyValuePair<string, FileAttributes> file in fileSystemChildren)
  216. {
  217. // Folders
  218. if (file.Value.HasFlag(FileAttributes.Directory))
  219. {
  220. returnFiles.Add(file);
  221. }
  222. // If it's a shortcut, resolve it
  223. else if (Shortcut.IsShortcut(file.Key))
  224. {
  225. string newPath = Shortcut.ResolveShortcut(file.Key);
  226. FileAttributes newPathAttributes = File.GetAttributes(newPath);
  227. // Find out if the shortcut is pointing to a directory or file
  228. if (newPathAttributes.HasFlag(FileAttributes.Directory))
  229. {
  230. // If we're flattening then get the shortcut's children
  231. if (flattenShortcuts)
  232. {
  233. IEnumerable<KeyValuePair<string, FileAttributes>> newChildren = Directory.GetFileSystemEntries(newPath, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, FileAttributes>(f, File.GetAttributes(f)));
  234. returnFiles.AddRange(FilterChildFileSystemEntries(newChildren, false));
  235. }
  236. else
  237. {
  238. returnFiles.Add(new KeyValuePair<string, FileAttributes>(newPath, newPathAttributes));
  239. }
  240. }
  241. else
  242. {
  243. returnFiles.Add(new KeyValuePair<string, FileAttributes>(newPath, newPathAttributes));
  244. }
  245. }
  246. else
  247. {
  248. returnFiles.Add(file);
  249. }
  250. }
  251. return returnFiles;
  252. }
  253. public Person GetPerson(string name)
  254. {
  255. // not yet implemented
  256. return null;
  257. }
  258. public Studio GetStudio(string name)
  259. {
  260. // not yet implemented
  261. return null;
  262. }
  263. public Genre GetGenre(string name)
  264. {
  265. // not yet implemented
  266. return null;
  267. }
  268. public Year GetYear(int value)
  269. {
  270. // not yet implemented
  271. return null;
  272. }
  273. }
  274. }