Kernel.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. using MediaBrowser.Common.Kernel;
  2. using MediaBrowser.Common.Logging;
  3. using MediaBrowser.Controller.Entities;
  4. using MediaBrowser.Controller.Entities.TV;
  5. using MediaBrowser.Controller.IO;
  6. using MediaBrowser.Controller.Library;
  7. using MediaBrowser.Controller.Providers;
  8. using MediaBrowser.Controller.Resolvers;
  9. using MediaBrowser.Controller.Weather;
  10. using MediaBrowser.Model.Authentication;
  11. using MediaBrowser.Model.Configuration;
  12. using MediaBrowser.Model.Progress;
  13. using MediaBrowser.Common.Extensions;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.ComponentModel.Composition;
  17. using System.IO;
  18. using System.Linq;
  19. using System.Reflection;
  20. using System.Security.Cryptography;
  21. using System.Text;
  22. using System.Threading.Tasks;
  23. namespace MediaBrowser.Controller
  24. {
  25. public class Kernel : BaseKernel<ServerConfiguration, ServerApplicationPaths>
  26. {
  27. #region Events
  28. /// <summary>
  29. /// Fires whenever any validation routine adds or removes items. The added and removed items are properties of the args.
  30. /// *** Will fire asynchronously. ***
  31. /// </summary>
  32. public event EventHandler<ChildrenChangedEventArgs> LibraryChanged;
  33. public void OnLibraryChanged(ChildrenChangedEventArgs args)
  34. {
  35. if (LibraryChanged != null)
  36. {
  37. Task.Run(() => LibraryChanged(this, args));
  38. }
  39. }
  40. #endregion
  41. public static Kernel Instance { get; private set; }
  42. public ItemController ItemController { get; private set; }
  43. public IEnumerable<User> Users { get; private set; }
  44. public Folder RootFolder { get; private set; }
  45. private DirectoryWatchers DirectoryWatchers { get; set; }
  46. private string MediaRootFolderPath
  47. {
  48. get
  49. {
  50. return ApplicationPaths.RootFolderPath;
  51. }
  52. }
  53. public override KernelContext KernelContext
  54. {
  55. get { return KernelContext.Server; }
  56. }
  57. /// <summary>
  58. /// Gets the list of currently registered weather prvoiders
  59. /// </summary>
  60. [ImportMany(typeof(BaseWeatherProvider))]
  61. public IEnumerable<BaseWeatherProvider> WeatherProviders { get; private set; }
  62. /// <summary>
  63. /// Gets the list of currently registered metadata prvoiders
  64. /// </summary>
  65. [ImportMany(typeof(BaseMetadataProvider))]
  66. private IEnumerable<BaseMetadataProvider> MetadataProvidersEnumerable { get; set; }
  67. /// <summary>
  68. /// Once MEF has loaded the resolvers, sort them by priority and store them in this array
  69. /// Given the sheer number of times they'll be iterated over it'll be faster to loop through an array
  70. /// </summary>
  71. private BaseMetadataProvider[] MetadataProviders { get; set; }
  72. /// <summary>
  73. /// Gets the list of currently registered entity resolvers
  74. /// </summary>
  75. [ImportMany(typeof(IBaseItemResolver))]
  76. private IEnumerable<IBaseItemResolver> EntityResolversEnumerable { get; set; }
  77. /// <summary>
  78. /// Once MEF has loaded the resolvers, sort them by priority and store them in this array
  79. /// Given the sheer number of times they'll be iterated over it'll be faster to loop through an array
  80. /// </summary>
  81. internal IBaseItemResolver[] EntityResolvers { get; private set; }
  82. /// <summary>
  83. /// Creates a kernel based on a Data path, which is akin to our current programdata path
  84. /// </summary>
  85. public Kernel()
  86. : base()
  87. {
  88. Instance = this;
  89. }
  90. /// <summary>
  91. /// Performs initializations that only occur once
  92. /// </summary>
  93. protected override void InitializeInternal(IProgress<TaskProgress> progress)
  94. {
  95. base.InitializeInternal(progress);
  96. ItemController = new ItemController();
  97. DirectoryWatchers = new DirectoryWatchers();
  98. ExtractFFMpeg();
  99. }
  100. /// <summary>
  101. /// Performs initializations that can be reloaded at anytime
  102. /// </summary>
  103. protected override async Task ReloadInternal(IProgress<TaskProgress> progress)
  104. {
  105. await base.ReloadInternal(progress).ConfigureAwait(false);
  106. ReportProgress(progress, "Loading Users");
  107. ReloadUsers();
  108. ReportProgress(progress, "Loading Media Library");
  109. await ReloadRoot(allowInternetProviders: false).ConfigureAwait(false);
  110. }
  111. /// <summary>
  112. /// Completely disposes the Kernel
  113. /// </summary>
  114. public override void Dispose()
  115. {
  116. base.Dispose();
  117. DirectoryWatchers.Stop();
  118. }
  119. protected override void OnComposablePartsLoaded()
  120. {
  121. // The base class will start up all the plugins
  122. base.OnComposablePartsLoaded();
  123. // Sort the resolvers by priority
  124. EntityResolvers = EntityResolversEnumerable.OrderBy(e => e.Priority).ToArray();
  125. // Sort the providers by priority
  126. MetadataProviders = MetadataProvidersEnumerable.OrderBy(e => e.Priority).ToArray();
  127. }
  128. public BaseItem ResolveItem(ItemResolveEventArgs args)
  129. {
  130. // Try first priority resolvers
  131. for (int i = 0; i < EntityResolvers.Length; i++)
  132. {
  133. var item = EntityResolvers[i].ResolvePath(args);
  134. if (item != null)
  135. {
  136. item.ResolveArgs = args;
  137. return item;
  138. }
  139. }
  140. return null;
  141. }
  142. private void ReloadUsers()
  143. {
  144. Users = GetAllUsers();
  145. }
  146. /// <summary>
  147. /// Reloads the root media folder
  148. /// </summary>
  149. public async Task ReloadRoot(bool allowInternetProviders = true)
  150. {
  151. if (!Directory.Exists(MediaRootFolderPath))
  152. {
  153. Directory.CreateDirectory(MediaRootFolderPath);
  154. }
  155. DirectoryWatchers.Stop();
  156. RootFolder = await ItemController.GetItem(MediaRootFolderPath, allowInternetProviders: allowInternetProviders).ConfigureAwait(false) as Folder;
  157. RootFolder.ChildrenChanged += RootFolder_ChildrenChanged;
  158. DirectoryWatchers.Start();
  159. }
  160. void RootFolder_ChildrenChanged(object sender, ChildrenChangedEventArgs e)
  161. {
  162. Logger.LogDebugInfo("Root Folder Children Changed. Added: " + e.ItemsAdded.Count + " Removed: " + e.ItemsRemoved.Count());
  163. //re-start the directory watchers
  164. DirectoryWatchers.Stop();
  165. DirectoryWatchers.Start();
  166. //Task.Delay(30000); //let's wait and see if more data gets filled in...
  167. var allChildren = RootFolder.RecursiveChildren;
  168. Logger.LogDebugInfo(string.Format("Loading complete. Movies: {0} Episodes: {1} Folders: {2}", allChildren.OfType<Entities.Movies.Movie>().Count(), allChildren.OfType<Entities.TV.Episode>().Count(), allChildren.Where(i => i is Folder && !(i is Series || i is Season)).Count()));
  169. //foreach (var child in allChildren)
  170. //{
  171. // Logger.LogDebugInfo("(" + child.GetType().Name + ") " + child.Name + " (" + child.Path + ")");
  172. //}
  173. }
  174. /// <summary>
  175. /// Gets the default user to use when EnableUserProfiles is false
  176. /// </summary>
  177. public User GetDefaultUser()
  178. {
  179. User user = Users.FirstOrDefault();
  180. return user;
  181. }
  182. /// <summary>
  183. /// Persists a User
  184. /// </summary>
  185. public void SaveUser(User user)
  186. {
  187. }
  188. /// <summary>
  189. /// Authenticates a User and returns a result indicating whether or not it succeeded
  190. /// </summary>
  191. public AuthenticationResult AuthenticateUser(User user, string password)
  192. {
  193. var result = new AuthenticationResult();
  194. // When EnableUserProfiles is false, only the default User can login
  195. if (!Configuration.EnableUserProfiles)
  196. {
  197. result.Success = user.Id == GetDefaultUser().Id;
  198. }
  199. else if (string.IsNullOrEmpty(user.Password))
  200. {
  201. result.Success = true;
  202. }
  203. else
  204. {
  205. password = password ?? string.Empty;
  206. result.Success = password.GetMD5().ToString().Equals(user.Password);
  207. }
  208. // Update LastActivityDate and LastLoginDate, then save
  209. if (result.Success)
  210. {
  211. user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
  212. SaveUser(user);
  213. }
  214. return result;
  215. }
  216. /// <summary>
  217. /// Finds a library item by Id
  218. /// </summary>
  219. public BaseItem GetItemById(Guid id)
  220. {
  221. if (id == Guid.Empty)
  222. {
  223. return RootFolder;
  224. }
  225. return RootFolder.FindItemById(id);
  226. }
  227. /// <summary>
  228. /// Gets all users within the system
  229. /// </summary>
  230. private IEnumerable<User> GetAllUsers()
  231. {
  232. var list = new List<User>();
  233. // Return a dummy user for now since all calls to get items requre a userId
  234. var user = new User { };
  235. user.Name = "Default User";
  236. user.Id = Guid.Parse("5d1cf7fce25943b790d140095457a42b");
  237. user.PrimaryImagePath = "D:\\Video\\TV\\Archer (2009)\\backdrop.jpg";
  238. list.Add(user);
  239. user = new User { };
  240. user.Name = "Abobader";
  241. user.Id = Guid.NewGuid();
  242. user.LastLoginDate = DateTime.UtcNow.AddDays(-1);
  243. user.LastActivityDate = DateTime.UtcNow.AddHours(-3);
  244. user.Password = ("1234").GetMD5().ToString();
  245. list.Add(user);
  246. user = new User { };
  247. user.Name = "Scottisafool";
  248. user.Id = Guid.NewGuid();
  249. list.Add(user);
  250. user = new User { };
  251. user.Name = "Redshirt";
  252. user.Id = Guid.NewGuid();
  253. list.Add(user);
  254. /*user = new User();
  255. user.Name = "Test User 4";
  256. user.Id = Guid.NewGuid();
  257. list.Add(user);
  258. user = new User();
  259. user.Name = "Test User 5";
  260. user.Id = Guid.NewGuid();
  261. list.Add(user);
  262. user = new User();
  263. user.Name = "Test User 6";
  264. user.Id = Guid.NewGuid();
  265. list.Add(user);*/
  266. return list;
  267. }
  268. /// <summary>
  269. /// Runs all metadata providers for an entity
  270. /// </summary>
  271. internal async Task ExecuteMetadataProviders(BaseEntity item, bool allowInternetProviders = true)
  272. {
  273. // Run them sequentially in order of priority
  274. for (int i = 0; i < MetadataProviders.Length; i++)
  275. {
  276. var provider = MetadataProviders[i];
  277. // Skip if internet providers are currently disabled
  278. if (provider.RequiresInternet && (!Configuration.EnableInternetProviders || !allowInternetProviders))
  279. {
  280. continue;
  281. }
  282. // Skip if the provider doesn't support the current item
  283. if (!provider.Supports(item))
  284. {
  285. continue;
  286. }
  287. try
  288. {
  289. await provider.FetchIfNeededAsync(item).ConfigureAwait(false);
  290. }
  291. catch (Exception ex)
  292. {
  293. Logger.LogException(ex);
  294. }
  295. }
  296. }
  297. private void ExtractFFMpeg()
  298. {
  299. ExtractFFMpeg(ApplicationPaths.FFMpegPath);
  300. ExtractFFMpeg(ApplicationPaths.FFProbePath);
  301. }
  302. /// <summary>
  303. /// Run these during Init.
  304. /// Can't run do this on-demand because there will be multiple workers accessing them at once and we'd have to lock them
  305. /// </summary>
  306. private void ExtractFFMpeg(string exe)
  307. {
  308. if (File.Exists(exe))
  309. {
  310. File.Delete(exe);
  311. }
  312. // Extract exe
  313. using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Controller.FFMpeg." + Path.GetFileName(exe)))
  314. {
  315. using (var fileStream = new FileStream(exe, FileMode.Create))
  316. {
  317. stream.CopyTo(fileStream);
  318. }
  319. }
  320. }
  321. }
  322. }