diff --git a/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs index 9e5657e3c2..5f5203dae0 100644 --- a/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Api.HttpHandlers { string pluginName = QueryString["name"]; - return Kernel.Instance.PluginController.Plugins.First(p => p.Name.Equals(pluginName, StringComparison.OrdinalIgnoreCase)).Configuration; + return Kernel.Instance.Plugins.First(p => p.Name.Equals(pluginName, StringComparison.OrdinalIgnoreCase)).Configuration; } } } diff --git a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs index a558da163d..63797026d7 100644 --- a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Api.HttpHandlers { get { - var plugins = Kernel.Instance.PluginController.Plugins.Select(p => + var plugins = Kernel.Instance.Plugins.Select(p => { return new PluginInfo() { diff --git a/MediaBrowser.Api/HttpHandlers/UserConfigurationHandler.cs b/MediaBrowser.Api/HttpHandlers/UserConfigurationHandler.cs index faace9c31a..06c3ea86dd 100644 --- a/MediaBrowser.Api/HttpHandlers/UserConfigurationHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/UserConfigurationHandler.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Api.HttpHandlers { Guid userId = Guid.Parse(QueryString["userid"]); - return Kernel.Instance.ConfigurationController.GetUserConfiguration(userId); + return Kernel.Instance.GetUserConfiguration(userId); } } } diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 25889f9837..4560f70a15 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -31,6 +31,7 @@ + diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 6ced7a704a..ebc2ffcae8 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/MediaBrowser.Api/Plugin.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.Composition; using System.Reactive.Linq; using MediaBrowser.Api.HttpHandlers; using MediaBrowser.Common.Net; @@ -9,6 +10,7 @@ using MediaBrowser.Model.Plugins; namespace MediaBrowser.Api { + [Export(typeof(BasePlugin))] public class Plugin : BaseGenericPlugin { public override string Name diff --git a/MediaBrowser.Common/Configuration/ConfigurationController.cs b/MediaBrowser.Common/Configuration/ConfigurationController.cs deleted file mode 100644 index 547d195f5a..0000000000 --- a/MediaBrowser.Common/Configuration/ConfigurationController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.IO; -using MediaBrowser.Common.Json; - -namespace MediaBrowser.Common.Configuration -{ - public class ConfigurationController - where TConfigurationType : BaseConfiguration, new () - { - /// - /// The path to the configuration file - /// - public string Path { get; set; } - - public TConfigurationType Configuration { get; set; } - - public void Reload() - { - if (!File.Exists(Path)) - { - Configuration = new TConfigurationType(); - } - else - { - Configuration = JsonSerializer.DeserializeFromFile(Path); - } - } - - public void Save() - { - } - } -} diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs index 47e5d2c942..0edfeab25f 100644 --- a/MediaBrowser.Common/Kernel/BaseKernel.cs +++ b/MediaBrowser.Common/Kernel/BaseKernel.cs @@ -1,6 +1,11 @@ -using System.Configuration; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; +using System.Configuration; using System.IO; +using System.Linq; using System.Reflection; +using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Json; using MediaBrowser.Common.Logging; @@ -12,8 +17,7 @@ namespace MediaBrowser.Common.Kernel /// /// Represents a shared base kernel for both the UI and server apps /// - public abstract class BaseKernel - where TConfigurationContorllerType : ConfigurationController, new() + public abstract class BaseKernel where TConfigurationType : BaseConfiguration, new() { /// @@ -21,19 +25,39 @@ namespace MediaBrowser.Common.Kernel /// public string ProgramDataPath { get; private set; } + protected string PluginsPath + { + get + { + return Path.Combine(ProgramDataPath, "plugins"); + } + } + + protected string ConfigurationPath + { + get + { + return Path.Combine(ProgramDataPath, "config.js"); + } + } + /// /// Gets the current configuration /// - public TConfigurationContorllerType ConfigurationController { get; private set; } + public TConfigurationType Configuration { get; private set; } + /// + /// Gets the list of currently loaded plugins + /// + [ImportMany(typeof(BasePlugin))] + public IEnumerable Plugins { get; private set; } + /// /// Both the UI and server will have a built-in HttpServer. /// People will inevitably want remote control apps so it's needed in the UI too. /// public HttpServer HttpServer { get; private set; } - public PluginController PluginController { get; private set; } - /// /// Gets the kernel context. The UI kernel will have to override this. /// @@ -43,9 +67,6 @@ namespace MediaBrowser.Common.Kernel { ProgramDataPath = GetProgramDataPath(); - PluginController = new PluginController() { PluginsPath = Path.Combine(ProgramDataPath, "Plugins") }; - ConfigurationController = new TConfigurationContorllerType() { Path = Path.Combine(ProgramDataPath, "config.js") }; - Logger.LoggerInstance = new FileLogger(Path.Combine(ProgramDataPath, "Logs")); } @@ -55,7 +76,51 @@ namespace MediaBrowser.Common.Kernel ReloadHttpServer(); - ReloadPlugins(); + ReloadComposableParts(); + } + + protected void ReloadComposableParts() + { + if (!Directory.Exists(PluginsPath)) + { + Directory.CreateDirectory(PluginsPath); + } + + var catalog = new AggregateCatalog(Directory.GetDirectories(PluginsPath, "*", SearchOption.TopDirectoryOnly).Select(f => new DirectoryCatalog(f))); + + //catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); + //catalog.Catalogs.Add(new AssemblyCatalog(GetType().Assembly)); + + new CompositionContainer(catalog).ComposeParts(this); + + OnComposablePartsLoaded(); + } + + protected virtual void OnComposablePartsLoaded() + { + StartPlugins(); + } + + private void StartPlugins() + { + Parallel.For(0, Plugins.Count(), i => + { + var plugin = Plugins.ElementAt(i); + + plugin.ReloadConfiguration(); + + if (plugin.Enabled) + { + if (KernelContext == KernelContext.Server) + { + plugin.InitInServer(); + } + else + { + plugin.InitInUI(); + } + } + }); } /// @@ -87,9 +152,21 @@ namespace MediaBrowser.Common.Kernel private void ReloadConfiguration() { // Deserialize config - ConfigurationController.Reload(); + if (!File.Exists(ConfigurationPath)) + { + Configuration = new TConfigurationType(); + } + else + { + Configuration = JsonSerializer.DeserializeFromFile(ConfigurationPath); + } - Logger.LoggerInstance.LogSeverity = ConfigurationController.Configuration.LogSeverity; + Logger.LoggerInstance.LogSeverity = Configuration.LogSeverity; + } + + public void SaveConfiguration() + { + JsonSerializer.SerializeToFile(Configuration, ConfigurationPath); } private void ReloadHttpServer() @@ -99,13 +176,7 @@ namespace MediaBrowser.Common.Kernel HttpServer.Dispose(); } - HttpServer = new HttpServer("http://+:" + ConfigurationController.Configuration.HttpServerPortNumber + "/mediabrowser/"); - } - - protected virtual void ReloadPlugins() - { - // Find plugins - PluginController.Init(KernelContext); + HttpServer = new HttpServer("http://+:" + Configuration.HttpServerPortNumber + "/mediabrowser/"); } private static TConfigurationType GetConfiguration(string directory) diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index f7a223f1f5..26c850d5f3 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -34,6 +34,7 @@ ..\packages\ServiceStack.Text.3.8.5\lib\net35\ServiceStack.Text.dll + @@ -48,7 +49,6 @@ - @@ -67,7 +67,6 @@ - diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 7fdb2583ee..44f880fd2b 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -43,8 +43,22 @@ namespace MediaBrowser.Common.Plugins public abstract class BasePlugin { public abstract string Name { get; } - public string Path { get; set; } - public Version Version { get; set; } + + public string Path + { + get + { + return System.IO.Path.GetDirectoryName(GetType().Assembly.Location); + } + } + + public Version Version + { + get + { + return GetType().Assembly.GetName().Version; + } + } public BasePluginConfiguration Configuration { get; protected set; } diff --git a/MediaBrowser.Common/Plugins/PluginController.cs b/MediaBrowser.Common/Plugins/PluginController.cs deleted file mode 100644 index c26275436b..0000000000 --- a/MediaBrowser.Common/Plugins/PluginController.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using MediaBrowser.Common.Kernel; - -namespace MediaBrowser.Common.Plugins -{ - /// - /// Manages Plugins within the PluginsPath directory - /// - public class PluginController - { - public string PluginsPath { get; set; } - - /// - /// Gets the list of currently loaded plugins - /// - public IEnumerable Plugins { get; private set; } - - /// - /// Initializes the controller - /// - public void Init(KernelContext context) - { - AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve); - AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); - - Plugins = GetAllPlugins(); - - Parallel.For(0, Plugins.Count(), i => - { - var plugin = Plugins.ElementAt(i); - - plugin.ReloadConfiguration(); - - if (plugin.Enabled) - { - if (context == KernelContext.Server) - { - plugin.InitInServer(); - } - else - { - plugin.InitInUI(); - } - } - }); - } - - /// - /// Gets all plugins within PluginsPath - /// - /// - private IEnumerable GetAllPlugins() - { - if (!Directory.Exists(PluginsPath)) - { - Directory.CreateDirectory(PluginsPath); - } - - List plugins = new List(); - - foreach (string folder in Directory.GetDirectories(PluginsPath, "*", SearchOption.TopDirectoryOnly)) - { - BasePlugin plugin = GetPluginFromDirectory(folder); - - plugin.Path = folder; - - if (plugin != null) - { - plugins.Add(plugin); - } - } - - return plugins; - } - - private BasePlugin GetPluginFromDirectory(string path) - { - string dll = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly).FirstOrDefault(); - - if (!string.IsNullOrEmpty(dll)) - { - return GetPluginFromDll(dll); - } - - return null; - } - - private BasePlugin GetPluginFromDll(string path) - { - return GetPluginFromDll(Assembly.Load(File.ReadAllBytes(path))); - } - - private BasePlugin GetPluginFromDll(Assembly assembly) - { - var plugin = assembly.GetTypes().Where(type => typeof(BasePlugin).IsAssignableFrom(type)).FirstOrDefault(); - - if (plugin != null) - { - BasePlugin instance = plugin.GetConstructor(Type.EmptyTypes).Invoke(null) as BasePlugin; - - instance.Version = assembly.GetName().Version; - - return instance; - } - - return null; - } - - Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { - AssemblyName assemblyName = new AssemblyName(args.Name); - - IEnumerable dllPaths = Directory.GetFiles(PluginsPath, "*.dll", SearchOption.AllDirectories); - - string dll = dllPaths.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f) == assemblyName.Name); - - if (!string.IsNullOrEmpty(dll)) - { - return Assembly.Load(File.ReadAllBytes(dll)); - } - - return null; - } - } -} diff --git a/MediaBrowser.Configuration/MediaBrowser.Configuration.csproj b/MediaBrowser.Configuration/MediaBrowser.Configuration.csproj index 98b179f355..ff3cd25577 100644 --- a/MediaBrowser.Configuration/MediaBrowser.Configuration.csproj +++ b/MediaBrowser.Configuration/MediaBrowser.Configuration.csproj @@ -31,6 +31,7 @@ + False diff --git a/MediaBrowser.Configuration/Plugin.cs b/MediaBrowser.Configuration/Plugin.cs index f88dc7a4ca..ffa3e38d0d 100644 --- a/MediaBrowser.Configuration/Plugin.cs +++ b/MediaBrowser.Configuration/Plugin.cs @@ -1,8 +1,10 @@ -using MediaBrowser.Common.Plugins; +using System.ComponentModel.Composition; +using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Plugins; namespace MediaBrowser.Configuration { + [Export(typeof(BasePlugin))] public class Plugin : BaseGenericPlugin { public override string Name diff --git a/MediaBrowser.Controller/Configuration/ServerConfigurationController.cs b/MediaBrowser.Controller/Configuration/ServerConfigurationController.cs deleted file mode 100644 index 76c3cc43ce..0000000000 --- a/MediaBrowser.Controller/Configuration/ServerConfigurationController.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; - -namespace MediaBrowser.Controller.Configuration -{ - /// - /// Extends BaseConfigurationController by adding methods to get and set UIConfiguration data - /// - public class ServerConfigurationController : ConfigurationController - { - private string GetDictionaryKey(Guid userId, string deviceName) - { - string guidString = userId == Guid.Empty ? string.Empty : userId.ToString(); - - return deviceName + "-" + guidString; - } - - public UserConfiguration GetUserConfiguration(Guid userId) - { - return Configuration.DefaultUserConfiguration; - } - } -} diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 8b2688da74..7cf12ff3f4 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -17,7 +18,7 @@ using MediaBrowser.Model.Users; namespace MediaBrowser.Controller { - public class Kernel : BaseKernel + public class Kernel : BaseKernel { public static Kernel Instance { get; private set; } @@ -37,6 +38,12 @@ namespace MediaBrowser.Controller } } + /// + /// Gets the list of currently registered entity resolvers + /// + [ImportMany(typeof(IBaseItemResolver))] + public IEnumerable EntityResolvers { get; private set; } + /// /// Creates a kernal based on a Data path, which is akin to our current programdata path /// @@ -51,35 +58,27 @@ namespace MediaBrowser.Controller ItemController.PreBeginResolvePath += ItemController_PreBeginResolvePath; ItemController.BeginResolvePath += ItemController_BeginResolvePath; - - // Add support for core media types - audio, video, etc - AddBaseItemType(); - AddBaseItemType(); - AddBaseItemType(); } - /// - /// Tells the kernel to start spinning up - /// - public override void Init() + protected override void OnComposablePartsLoaded() { - base.Init(); + List resolvers = EntityResolvers.ToList(); + + // Add the internal resolvers + resolvers.Add(new VideoResolver()); + resolvers.Add(new AudioResolver()); + resolvers.Add(new FolderResolver()); + + EntityResolvers = resolvers; + + // The base class will fire up all the plugins + base.OnComposablePartsLoaded(); // Get users from users folder // Load root media folder Parallel.Invoke(ReloadUsers, ReloadRoot); } - /// - /// Registers a new BaseItem subclass - /// - public void AddBaseItemType() - where TBaseItemType : BaseItem, new() - where TResolverType : BaseItemResolver, new() - { - ItemController.AddResovler(); - } - /// /// Fires when a path is about to be resolved, but before child folders and files /// have been collected from the file system. @@ -147,6 +146,11 @@ namespace MediaBrowser.Controller } } + public UserConfiguration GetUserConfiguration(Guid userId) + { + return Configuration.DefaultUserConfiguration; + } + public void ReloadItem(BaseItem item) { Folder folder = item as Folder; @@ -250,7 +254,7 @@ namespace MediaBrowser.Controller { DateTime now = DateTime.Now; - UserConfiguration config = ConfigurationController.GetUserConfiguration(userId); + UserConfiguration config = GetUserConfiguration(userId); return GetParentalAllowedRecursiveChildren(parent, userId).Where(i => !(i is Folder) && (now - i.DateCreated).TotalDays < config.RecentItemDays); } diff --git a/MediaBrowser.Controller/Library/ItemController.cs b/MediaBrowser.Controller/Library/ItemController.cs index fee53e3bb6..cfca9e2ccb 100644 --- a/MediaBrowser.Controller/Library/ItemController.cs +++ b/MediaBrowser.Controller/Library/ItemController.cs @@ -13,18 +13,6 @@ namespace MediaBrowser.Controller.Library { public class ItemController { - private List Resolvers = new List(); - - /// - /// Registers a new BaseItem resolver. - /// - public void AddResovler() - where TBaseItemType : BaseItem, new() - where TResolverType : BaseItemResolver, new() - { - Resolvers.Insert(0, new TResolverType()); - } - #region PreBeginResolvePath Event /// /// Fires when a path is about to be resolved, but before child folders and files @@ -127,7 +115,7 @@ namespace MediaBrowser.Controller.Library private BaseItem ResolveItem(ItemResolveEventArgs args) { // If that didn't pan out, try the slow ones - foreach (IBaseItemResolver resolver in Resolvers) + foreach (IBaseItemResolver resolver in Kernel.Instance.EntityResolvers) { var item = resolver.ResolvePath(args); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 296c44d1a9..8ee013063c 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -31,6 +31,7 @@ + ..\packages\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll @@ -43,7 +44,6 @@ - diff --git a/MediaBrowser.Controller/Resolvers/AudioResolver.cs b/MediaBrowser.Controller/Resolvers/AudioResolver.cs index 2ca54e71d4..956af95f05 100644 --- a/MediaBrowser.Controller/Resolvers/AudioResolver.cs +++ b/MediaBrowser.Controller/Resolvers/AudioResolver.cs @@ -1,9 +1,11 @@ -using System.IO; +using System.ComponentModel.Composition; +using System.IO; using MediaBrowser.Controller.Events; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Resolvers { + [Export(typeof(IBaseItemResolver))] public class AudioResolver : BaseItemResolver