diff --git a/MediaBrowser.Api/HttpHandlers/PluginAssemblyHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginAssemblyHandler.cs new file mode 100644 index 0000000000..9e98f012d1 --- /dev/null +++ b/MediaBrowser.Api/HttpHandlers/PluginAssemblyHandler.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +using System.Net; +using System.Threading.Tasks; +using MediaBrowser.Common.Net.Handlers; +using MediaBrowser.Controller; + +namespace MediaBrowser.Api.HttpHandlers +{ + class PluginAssemblyHandler : BaseHandler + { + public override Task GetContentType() + { + throw new NotImplementedException(); + } + + protected override Task WriteResponseToOutputStream(Stream stream) + { + throw new NotImplementedException(); + } + + public override Task ProcessRequest(HttpListenerContext ctx) + { + string filename = ctx.Request.QueryString["assemblyfilename"]; + + string path = Path.Combine(Kernel.Instance.ApplicationPaths.PluginsPath, filename); + + return new StaticFileHandler() { Path = path }.ProcessRequest(ctx); + } + } +} diff --git a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs index acfa5ebeb4..b05642e70f 100644 --- a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs @@ -21,7 +21,8 @@ namespace MediaBrowser.Api.HttpHandlers Name = p.Name, Enabled = p.Enabled, DownloadToUI = p.DownloadToUI, - Version = p.Version.ToString() + Version = p.Version.ToString(), + AssemblyFileName = p.AssemblyFileName }; }); diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 5913e695b7..8f387e0659 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -67,6 +67,7 @@ + diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 4e6ab54de3..7d66f437ab 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/MediaBrowser.Api/Plugin.cs @@ -113,6 +113,10 @@ namespace MediaBrowser.Api { return new DefaultUserHandler(); } + else if (localPath.EndsWith("/api/pluginassembly", StringComparison.OrdinalIgnoreCase)) + { + return new PluginAssemblyHandler(); + } return null; } diff --git a/MediaBrowser.ApiInteraction/ApiClient.cs b/MediaBrowser.ApiInteraction/ApiClient.cs index 621c700cf5..6384f3df99 100644 --- a/MediaBrowser.ApiInteraction/ApiClient.cs +++ b/MediaBrowser.ApiInteraction/ApiClient.cs @@ -569,7 +569,7 @@ namespace MediaBrowser.ApiInteraction /// /// Gets a list of plugins installed on the server /// - public async Task GetInstalledPlugins() + public async Task GetInstalledPluginsAsync() { string url = ApiUrl + "/plugins"; @@ -578,7 +578,17 @@ namespace MediaBrowser.ApiInteraction return DeserializeFromStream(stream); } } - + + /// + /// Gets a list of plugins installed on the server + /// + public Task GetPluginAssemblyAsync(PluginInfo plugin) + { + string url = ApiUrl + "/pluginassembly?assemblyfilename=" + plugin.AssemblyFileName; + + return GetStreamAsync(url); + } + /// /// Gets weather information for the default location as set in configuration /// @@ -655,7 +665,7 @@ namespace MediaBrowser.ApiInteraction return GetStreamAsync(url); } - + private T DeserializeFromStream(Stream stream) { return DeserializeFromStream(stream, SerializationFormat); diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs index 4f1bdae6b2..3704dbce02 100644 --- a/MediaBrowser.Common/Kernel/BaseKernel.cs +++ b/MediaBrowser.Common/Kernel/BaseKernel.cs @@ -59,21 +59,18 @@ namespace MediaBrowser.Common.Kernel ApplicationPaths = new TApplicationPathsType(); } - public virtual Task Init(IProgress progress) + public virtual async Task Init(IProgress progress) { - return Task.Run(() => - { - ReloadLogger(); + ReloadLogger(); - progress.Report(new TaskProgress() { Description = "Loading configuration", PercentComplete = 0 }); - ReloadConfiguration(); + progress.Report(new TaskProgress() { Description = "Loading configuration", PercentComplete = 0 }); + ReloadConfiguration(); - progress.Report(new TaskProgress() { Description = "Starting Http server", PercentComplete = 5 }); - ReloadHttpServer(); + progress.Report(new TaskProgress() { Description = "Starting Http server", PercentComplete = 5 }); + ReloadHttpServer(); - progress.Report(new TaskProgress() { Description = "Loading Plugins", PercentComplete = 10 }); - ReloadComposableParts(); - }); + progress.Report(new TaskProgress() { Description = "Loading Plugins", PercentComplete = 10 }); + await ReloadComposableParts().ConfigureAwait(false); } /// @@ -98,10 +95,25 @@ namespace MediaBrowser.Common.Kernel /// Uses MEF to locate plugins /// Subclasses can use this to locate types within plugins /// - protected void ReloadComposableParts() + protected virtual Task ReloadComposableParts() { - DisposeComposableParts(); + return Task.Run(() => + { + DisposeComposableParts(); + var container = GetCompositionContainer(includeCurrentAssembly: true); + + container.ComposeParts(this); + + OnComposablePartsLoaded(); + + container.Catalog.Dispose(); + container.Dispose(); + }); + } + + public CompositionContainer GetCompositionContainer(bool includeCurrentAssembly = false) + { // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that // This will prevent the .dll file from getting locked, and allow us to replace it when needed IEnumerable pluginAssemblies = Directory.GetFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly).Select(f => Assembly.Load(File.ReadAllBytes((f)))); @@ -112,17 +124,13 @@ namespace MediaBrowser.Common.Kernel // Uncomment this if it's ever needed //catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); - // Include composable parts in the subclass assembly - catalog.Catalogs.Add(new AssemblyCatalog(GetType().Assembly)); + if (includeCurrentAssembly) + { + // Include composable parts in the subclass assembly + catalog.Catalogs.Add(new AssemblyCatalog(GetType().Assembly)); + } - var container = new CompositionContainer(catalog); - - container.ComposeParts(this); - - OnComposablePartsLoaded(); - - catalog.Dispose(); - container.Dispose(); + return new CompositionContainer(catalog); } /// diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 90ee7d1ee7..aa5c1b561d 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -65,6 +65,7 @@ ..\packages\Rx-Linq.2.0.20823\lib\Net45\System.Reactive.Linq.dll + ..\packages\MahApps.Metro.0.9.0.0\lib\net40\System.Windows.Interactivity.dll diff --git a/MediaBrowser.Common/Net/MimeTypes.cs b/MediaBrowser.Common/Net/MimeTypes.cs index 41df463f71..3ec13ce83f 100644 --- a/MediaBrowser.Common/Net/MimeTypes.cs +++ b/MediaBrowser.Common/Net/MimeTypes.cs @@ -148,6 +148,12 @@ namespace MediaBrowser.Common.Net return "application/x-mpegURL"; } + // Misc + else if (ext.EndsWith("dll", StringComparison.OrdinalIgnoreCase)) + { + return "application/x-msdownload"; + } + throw new InvalidOperationException("Argument not supported: " + path); } } diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index d92d4a8c20..e36a31c682 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -63,6 +63,28 @@ namespace MediaBrowser.Common.Plugins } } + /// + /// Gets the name the assembly file + /// + public string AssemblyFileName + { + get + { + return GetType().Assembly.GetName().Name + ".dll"; + } + } + + /// + /// Gets the path to the assembly file + /// + public string AssemblyFilePath + { + get + { + return Path.Combine(Kernel.ApplicationPaths.PluginsPath, AssemblyFileName); + } + } + /// /// Gets or sets the current plugin configuration /// diff --git a/MediaBrowser.Model/DTO/PluginInfo.cs b/MediaBrowser.Model/DTO/PluginInfo.cs index 7ddadbc932..12a22b98fb 100644 --- a/MediaBrowser.Model/DTO/PluginInfo.cs +++ b/MediaBrowser.Model/DTO/PluginInfo.cs @@ -23,5 +23,11 @@ namespace MediaBrowser.Model.DTO [ProtoMember(5)] public string Version { get; set; } + + [ProtoMember(6)] + public string AssemblyFileName { get; set; } + + [ProtoMember(7)] + public string ConfigurationFileName { get; set; } } } diff --git a/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs b/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs index ee459810ef..17aca6c5cd 100644 --- a/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs +++ b/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("1.0.0.0")]