Kernel.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.Composition;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Security.Cryptography;
  7. using System.Text;
  8. using MediaBrowser.Common.Configuration;
  9. using MediaBrowser.Common.Kernel;
  10. using MediaBrowser.Controller.Configuration;
  11. using MediaBrowser.Controller.Events;
  12. using MediaBrowser.Controller.IO;
  13. using MediaBrowser.Controller.Library;
  14. using MediaBrowser.Controller.Resolvers;
  15. using MediaBrowser.Model.Configuration;
  16. using MediaBrowser.Model.DTO;
  17. using MediaBrowser.Model.Entities;
  18. using MediaBrowser.Model.Progress;
  19. using MediaBrowser.Model.Users;
  20. namespace MediaBrowser.Controller
  21. {
  22. public class Kernel : BaseKernel<ServerConfiguration>
  23. {
  24. public static Kernel Instance { get; private set; }
  25. public ItemController ItemController { get; private set; }
  26. public IEnumerable<User> Users { get; private set; }
  27. public Folder RootFolder { get; private set; }
  28. private DirectoryWatchers DirectoryWatchers { get; set; }
  29. private string MediaRootFolderPath
  30. {
  31. get
  32. {
  33. return ApplicationPaths.RootFolderPath;
  34. }
  35. }
  36. /// <summary>
  37. /// Gets the list of currently registered entity resolvers
  38. /// </summary>
  39. [ImportMany(typeof(IBaseItemResolver))]
  40. public IEnumerable<IBaseItemResolver> EntityResolvers { get; private set; }
  41. /// <summary>
  42. /// Creates a kernal based on a Data path, which is akin to our current programdata path
  43. /// </summary>
  44. public Kernel()
  45. : base()
  46. {
  47. Instance = this;
  48. ItemController = new ItemController();
  49. DirectoryWatchers = new DirectoryWatchers();
  50. ItemController.PreBeginResolvePath += ItemController_PreBeginResolvePath;
  51. ItemController.BeginResolvePath += ItemController_BeginResolvePath;
  52. }
  53. public override void Init(IProgress<TaskProgress> progress)
  54. {
  55. base.Init(progress);
  56. progress.Report(new TaskProgress() { Description = "Loading Users", PercentComplete = 15 });
  57. ReloadUsers();
  58. progress.Report(new TaskProgress() { Description = "Loading Media Library", PercentComplete = 20 });
  59. ReloadRoot();
  60. progress.Report(new TaskProgress() { Description = "Loading Complete", PercentComplete = 100 });
  61. }
  62. protected override void OnComposablePartsLoaded()
  63. {
  64. List<IBaseItemResolver> resolvers = EntityResolvers.ToList();
  65. // Add the internal resolvers
  66. resolvers.Add(new VideoResolver());
  67. resolvers.Add(new AudioResolver());
  68. resolvers.Add(new FolderResolver());
  69. EntityResolvers = resolvers;
  70. // The base class will start up all the plugins
  71. base.OnComposablePartsLoaded();
  72. }
  73. /// <summary>
  74. /// Fires when a path is about to be resolved, but before child folders and files
  75. /// have been collected from the file system.
  76. /// This gives us a chance to cancel it if needed, resulting in the path being ignored
  77. /// </summary>
  78. void ItemController_PreBeginResolvePath(object sender, PreBeginResolveEventArgs e)
  79. {
  80. if (e.IsHidden || e.IsSystemFile)
  81. {
  82. // Ignore hidden files and folders
  83. e.Cancel = true;
  84. }
  85. else if (Path.GetFileName(e.Path).Equals("trailers", StringComparison.OrdinalIgnoreCase))
  86. {
  87. // Ignore any folders named "trailers"
  88. e.Cancel = true;
  89. }
  90. }
  91. /// <summary>
  92. /// Fires when a path is about to be resolved, but after child folders and files
  93. /// This gives us a chance to cancel it if needed, resulting in the path being ignored
  94. /// </summary>
  95. void ItemController_BeginResolvePath(object sender, ItemResolveEventArgs e)
  96. {
  97. if (e.IsFolder)
  98. {
  99. if (e.ContainsFile(".ignore"))
  100. {
  101. // Ignore any folders containing a file called .ignore
  102. e.Cancel = true;
  103. }
  104. }
  105. }
  106. private void ReloadUsers()
  107. {
  108. Users = GetAllUsers();
  109. }
  110. /// <summary>
  111. /// Reloads the root media folder
  112. /// </summary>
  113. public void ReloadRoot()
  114. {
  115. if (!Directory.Exists(MediaRootFolderPath))
  116. {
  117. Directory.CreateDirectory(MediaRootFolderPath);
  118. }
  119. DirectoryWatchers.Stop();
  120. RootFolder = ItemController.GetItem(MediaRootFolderPath) as Folder;
  121. DirectoryWatchers.Start();
  122. }
  123. private static MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();
  124. public static Guid GetMD5(string str)
  125. {
  126. lock (md5Provider)
  127. {
  128. return new Guid(md5Provider.ComputeHash(Encoding.Unicode.GetBytes(str)));
  129. }
  130. }
  131. public UserConfiguration GetUserConfiguration(Guid userId)
  132. {
  133. return Configuration.DefaultUserConfiguration;
  134. }
  135. public void ReloadItem(BaseItem item)
  136. {
  137. Folder folder = item as Folder;
  138. if (folder != null && folder.IsRoot)
  139. {
  140. ReloadRoot();
  141. }
  142. else
  143. {
  144. if (!Directory.Exists(item.Path) && !File.Exists(item.Path))
  145. {
  146. ReloadItem(item.Parent);
  147. return;
  148. }
  149. BaseItem newItem = ItemController.GetItem(item.Parent, item.Path);
  150. List<BaseItem> children = item.Parent.Children.ToList();
  151. int index = children.IndexOf(item);
  152. children.RemoveAt(index);
  153. children.Insert(index, newItem);
  154. item.Parent.Children = children.ToArray();
  155. }
  156. }
  157. /// <summary>
  158. /// Finds a library item by Id
  159. /// </summary>
  160. public BaseItem GetItemById(Guid id)
  161. {
  162. if (id == Guid.Empty)
  163. {
  164. return RootFolder;
  165. }
  166. return RootFolder.FindById(id);
  167. }
  168. /// <summary>
  169. /// Determines if an item is allowed for a given user
  170. /// </summary>
  171. public bool IsParentalAllowed(BaseItem item, Guid userId)
  172. {
  173. // not yet implemented
  174. return true;
  175. }
  176. /// <summary>
  177. /// Gets allowed children of an item
  178. /// </summary>
  179. public IEnumerable<BaseItem> GetParentalAllowedChildren(Folder folder, Guid userId)
  180. {
  181. return folder.Children.Where(i => IsParentalAllowed(i, userId));
  182. }
  183. /// <summary>
  184. /// Gets allowed recursive children of an item
  185. /// </summary>
  186. public IEnumerable<BaseItem> GetParentalAllowedRecursiveChildren(Folder folder, Guid userId)
  187. {
  188. foreach (var item in GetParentalAllowedChildren(folder, userId))
  189. {
  190. yield return item;
  191. var subFolder = item as Folder;
  192. if (subFolder != null)
  193. {
  194. foreach (var subitem in GetParentalAllowedRecursiveChildren(subFolder, userId))
  195. {
  196. yield return subitem;
  197. }
  198. }
  199. }
  200. }
  201. /// <summary>
  202. /// Gets user data for an item, if there is any
  203. /// </summary>
  204. public UserItemData GetUserItemData(Guid userId, Guid itemId)
  205. {
  206. User user = Users.First(u => u.Id == userId);
  207. if (user.ItemData.ContainsKey(itemId))
  208. {
  209. return user.ItemData[itemId];
  210. }
  211. return null;
  212. }
  213. /// <summary>
  214. /// Gets all recently added items (recursive) within a folder, based on configuration and parental settings
  215. /// </summary>
  216. public IEnumerable<BaseItem> GetRecentlyAddedItems(Folder parent, Guid userId)
  217. {
  218. DateTime now = DateTime.Now;
  219. UserConfiguration config = GetUserConfiguration(userId);
  220. return GetParentalAllowedRecursiveChildren(parent, userId).Where(i => !(i is Folder) && (now - i.DateCreated).TotalDays < config.RecentItemDays);
  221. }
  222. /// <summary>
  223. /// Gets all recently added unplayed items (recursive) within a folder, based on configuration and parental settings
  224. /// </summary>
  225. public IEnumerable<BaseItem> GetRecentlyAddedUnplayedItems(Folder parent, Guid userId)
  226. {
  227. return GetRecentlyAddedItems(parent, userId).Where(i =>
  228. {
  229. var userdata = GetUserItemData(userId, i.Id);
  230. return userdata == null || userdata.PlayCount == 0;
  231. });
  232. }
  233. /// <summary>
  234. /// Gets all in-progress items (recursive) within a folder
  235. /// </summary>
  236. public IEnumerable<BaseItem> GetInProgressItems(Folder parent, Guid userId)
  237. {
  238. return GetParentalAllowedRecursiveChildren(parent, userId).Where(i =>
  239. {
  240. if (i is Folder)
  241. {
  242. return false;
  243. }
  244. var userdata = GetUserItemData(userId, i.Id);
  245. return userdata != null && userdata.PlaybackPosition.Ticks > 0;
  246. });
  247. }
  248. /// <summary>
  249. /// Finds all recursive items within a top-level parent that contain the given studio and are allowed for the current user
  250. /// </summary>
  251. public IEnumerable<BaseItem> GetItemsWithStudio(Folder parent, string studio, Guid userId)
  252. {
  253. return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.Studios != null && f.Studios.Any(s => s.Equals(studio, StringComparison.OrdinalIgnoreCase)));
  254. }
  255. /// <summary>
  256. /// Finds all recursive items within a top-level parent that contain the given person and are allowed for the current user
  257. /// </summary>
  258. /// <param name="personType">Specify this to limit results to a specific PersonType</param>
  259. public IEnumerable<BaseItem> GetItemsWithPerson(Folder parent, string person, PersonType? personType, Guid userId)
  260. {
  261. return GetParentalAllowedRecursiveChildren(parent, userId).Where(c =>
  262. {
  263. if (c.People != null)
  264. {
  265. if (personType.HasValue)
  266. {
  267. return c.People.Any(p => p.Name.Equals(person, StringComparison.OrdinalIgnoreCase) && p.PersonType == personType.Value);
  268. }
  269. else
  270. {
  271. return c.People.Any(p => p.Name.Equals(person, StringComparison.OrdinalIgnoreCase));
  272. }
  273. }
  274. return false;
  275. });
  276. }
  277. /// <summary>
  278. /// Finds all recursive items within a top-level parent that contain the given genre and are allowed for the current user
  279. /// </summary>
  280. public IEnumerable<BaseItem> GetItemsWithGenre(Folder parent, string genre, Guid userId)
  281. {
  282. return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.Genres != null && f.Genres.Any(s => s.Equals(genre, StringComparison.OrdinalIgnoreCase)));
  283. }
  284. /// <summary>
  285. /// Finds all recursive items within a top-level parent that contain the given year and are allowed for the current user
  286. /// </summary>
  287. public IEnumerable<BaseItem> GetItemsWithYear(Folder parent, int year, Guid userId)
  288. {
  289. return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.ProductionYear.HasValue && f.ProductionYear == year);
  290. }
  291. /// <summary>
  292. /// Finds all recursive items within a top-level parent that contain the given person and are allowed for the current user
  293. /// </summary>
  294. public IEnumerable<BaseItem> GetItemsWithPerson(Folder parent, string personName, Guid userId)
  295. {
  296. return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.People != null && f.People.Any(s => s.Name.Equals(personName, StringComparison.OrdinalIgnoreCase)));
  297. }
  298. /// <summary>
  299. /// Gets all years from all recursive children of a folder
  300. /// The CategoryInfo class is used to keep track of the number of times each year appears
  301. /// </summary>
  302. public IEnumerable<CategoryInfo<Year>> GetAllYears(Folder parent, Guid userId)
  303. {
  304. Dictionary<int, int> data = new Dictionary<int, int>();
  305. // Get all the allowed recursive children
  306. IEnumerable<BaseItem> allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId);
  307. foreach (var item in allItems)
  308. {
  309. // Add the year from the item to the data dictionary
  310. // If the year already exists, increment the count
  311. if (item.ProductionYear == null)
  312. {
  313. continue;
  314. }
  315. if (!data.ContainsKey(item.ProductionYear.Value))
  316. {
  317. data.Add(item.ProductionYear.Value, 1);
  318. }
  319. else
  320. {
  321. data[item.ProductionYear.Value]++;
  322. }
  323. }
  324. // Now go through the dictionary and create a Category for each studio
  325. List<CategoryInfo<Year>> list = new List<CategoryInfo<Year>>();
  326. foreach (int key in data.Keys)
  327. {
  328. // Get the original entity so that we can also supply the PrimaryImagePath
  329. Year entity = Kernel.Instance.ItemController.GetYear(key);
  330. if (entity != null)
  331. {
  332. list.Add(new CategoryInfo<Year>()
  333. {
  334. Item = entity,
  335. ItemCount = data[key]
  336. });
  337. }
  338. }
  339. return list;
  340. }
  341. /// <summary>
  342. /// Gets all studios from all recursive children of a folder
  343. /// The CategoryInfo class is used to keep track of the number of times each studio appears
  344. /// </summary>
  345. public IEnumerable<CategoryInfo<Studio>> GetAllStudios(Folder parent, Guid userId)
  346. {
  347. Dictionary<string, int> data = new Dictionary<string, int>();
  348. // Get all the allowed recursive children
  349. IEnumerable<BaseItem> allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId);
  350. foreach (var item in allItems)
  351. {
  352. // Add each studio from the item to the data dictionary
  353. // If the studio already exists, increment the count
  354. if (item.Studios == null)
  355. {
  356. continue;
  357. }
  358. foreach (string val in item.Studios)
  359. {
  360. if (!data.ContainsKey(val))
  361. {
  362. data.Add(val, 1);
  363. }
  364. else
  365. {
  366. data[val]++;
  367. }
  368. }
  369. }
  370. // Now go through the dictionary and create a Category for each studio
  371. List<CategoryInfo<Studio>> list = new List<CategoryInfo<Studio>>();
  372. foreach (string key in data.Keys)
  373. {
  374. // Get the original entity so that we can also supply the PrimaryImagePath
  375. Studio entity = Kernel.Instance.ItemController.GetStudio(key);
  376. if (entity != null)
  377. {
  378. list.Add(new CategoryInfo<Studio>()
  379. {
  380. Item = entity,
  381. ItemCount = data[key]
  382. });
  383. }
  384. }
  385. return list;
  386. }
  387. /// <summary>
  388. /// Gets all genres from all recursive children of a folder
  389. /// The CategoryInfo class is used to keep track of the number of times each genres appears
  390. /// </summary>
  391. public IEnumerable<CategoryInfo<Genre>> GetAllGenres(Folder parent, Guid userId)
  392. {
  393. Dictionary<string, int> data = new Dictionary<string, int>();
  394. // Get all the allowed recursive children
  395. IEnumerable<BaseItem> allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId);
  396. foreach (var item in allItems)
  397. {
  398. // Add each genre from the item to the data dictionary
  399. // If the genre already exists, increment the count
  400. if (item.Genres == null)
  401. {
  402. continue;
  403. }
  404. foreach (string val in item.Genres)
  405. {
  406. if (!data.ContainsKey(val))
  407. {
  408. data.Add(val, 1);
  409. }
  410. else
  411. {
  412. data[val]++;
  413. }
  414. }
  415. }
  416. // Now go through the dictionary and create a Category for each genre
  417. List<CategoryInfo<Genre>> list = new List<CategoryInfo<Genre>>();
  418. foreach (string key in data.Keys)
  419. {
  420. // Get the original entity so that we can also supply the PrimaryImagePath
  421. Genre entity = Kernel.Instance.ItemController.GetGenre(key);
  422. if (entity != null)
  423. {
  424. list.Add(new CategoryInfo<Genre>()
  425. {
  426. Item = entity,
  427. ItemCount = data[key]
  428. });
  429. }
  430. }
  431. return list;
  432. }
  433. /// <summary>
  434. /// Gets all users within the system
  435. /// </summary>
  436. private IEnumerable<User> GetAllUsers()
  437. {
  438. List<User> list = new List<User>();
  439. // Return a dummy user for now since all calls to get items requre a userId
  440. User user = new User();
  441. user.Name = "Default User";
  442. user.Id = Guid.Parse("5d1cf7fce25943b790d140095457a42b");
  443. list.Add(user);
  444. return list;
  445. }
  446. }
  447. }