From 119dfc3ac70db7536e86191eb3c89ffa1fd4f576 Mon Sep 17 00:00:00 2001 From: LukePulverenti Luke Pulverenti luke pulverenti Date: Thu, 20 Sep 2012 11:25:22 -0400 Subject: Adding the UI to the same repo. Made some default theme progress --- MediaBrowser.UI/Controller/PluginUpdater.cs | 231 ++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 MediaBrowser.UI/Controller/PluginUpdater.cs (limited to 'MediaBrowser.UI/Controller/PluginUpdater.cs') diff --git a/MediaBrowser.UI/Controller/PluginUpdater.cs b/MediaBrowser.UI/Controller/PluginUpdater.cs new file mode 100644 index 000000000..d9fa48749 --- /dev/null +++ b/MediaBrowser.UI/Controller/PluginUpdater.cs @@ -0,0 +1,231 @@ +using MediaBrowser.Common.Logging; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Common.Serialization; +using MediaBrowser.Model.DTO; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace MediaBrowser.UI.Controller +{ + /// + /// This keeps ui plugin assemblies in sync with plugins installed on the server + /// + public class PluginUpdater + { + /// + /// Gets the list of currently installed UI plugins + /// + [ImportMany(typeof(BasePlugin))] + private IEnumerable CurrentPlugins { get; set; } + + private CompositionContainer CompositionContainer { get; set; } + + public async Task UpdatePlugins() + { + // First load the plugins that are currently installed + ReloadComposableParts(); + + Logger.LogInfo("Downloading list of installed plugins"); + PluginInfo[] allInstalledPlugins = await UIKernel.Instance.ApiClient.GetInstalledPluginsAsync().ConfigureAwait(false); + + IEnumerable uiPlugins = allInstalledPlugins.Where(p => p.DownloadToUI); + + PluginUpdateResult result = new PluginUpdateResult(); + + result.DeletedPlugins = DeleteUninstalledPlugins(uiPlugins); + + await DownloadPluginAssemblies(uiPlugins, result).ConfigureAwait(false); + + // If any new assemblies were downloaded we'll have to reload the CurrentPlugins list + if (result.NewlyInstalledPlugins.Any()) + { + ReloadComposableParts(); + } + + result.UpdatedConfigurations = await DownloadPluginConfigurations(uiPlugins).ConfigureAwait(false); + + CompositionContainer.Dispose(); + + return result; + } + + /// + /// Downloads plugin assemblies from the server, if they need to be installed or updated. + /// + private async Task DownloadPluginAssemblies(IEnumerable uiPlugins, PluginUpdateResult result) + { + List newlyInstalledPlugins = new List(); + List updatedPlugins = new List(); + + // Loop through the list of plugins that are on the server + foreach (PluginInfo pluginInfo in uiPlugins) + { + // See if it is already installed in the UI + BasePlugin installedPlugin = CurrentPlugins.FirstOrDefault(p => p.AssemblyFileName.Equals(pluginInfo.AssemblyFileName, StringComparison.OrdinalIgnoreCase)); + + // Download the plugin if it is not present, or if the current version is out of date + bool downloadPlugin = installedPlugin == null; + + if (installedPlugin != null) + { + Version serverVersion = Version.Parse(pluginInfo.Version); + + downloadPlugin = serverVersion > installedPlugin.Version; + } + + if (downloadPlugin) + { + await DownloadPlugin(pluginInfo).ConfigureAwait(false); + + if (installedPlugin == null) + { + newlyInstalledPlugins.Add(pluginInfo); + } + else + { + updatedPlugins.Add(pluginInfo); + } + } + } + + result.NewlyInstalledPlugins = newlyInstalledPlugins; + result.UpdatedPlugins = updatedPlugins; + } + + /// + /// Downloads plugin configurations from the server. + /// + private async Task> DownloadPluginConfigurations(IEnumerable uiPlugins) + { + List updatedPlugins = new List(); + + // Loop through the list of plugins that are on the server + foreach (PluginInfo pluginInfo in uiPlugins) + { + // See if it is already installed in the UI + BasePlugin installedPlugin = CurrentPlugins.First(p => p.AssemblyFileName.Equals(pluginInfo.AssemblyFileName, StringComparison.OrdinalIgnoreCase)); + + if (installedPlugin.ConfigurationDateLastModified < pluginInfo.ConfigurationDateLastModified) + { + await DownloadPluginConfiguration(installedPlugin, pluginInfo).ConfigureAwait(false); + + updatedPlugins.Add(pluginInfo); + } + } + + return updatedPlugins; + } + + /// + /// Downloads a plugin assembly from the server + /// + private async Task DownloadPlugin(PluginInfo plugin) + { + Logger.LogInfo("Downloading {0} Plugin", plugin.Name); + + string path = Path.Combine(UIKernel.Instance.ApplicationPaths.PluginsPath, plugin.AssemblyFileName); + + // First download to a MemoryStream. This way if the download is cut off, we won't be left with a partial file + using (MemoryStream memoryStream = new MemoryStream()) + { + Stream assemblyStream = await UIKernel.Instance.ApiClient.GetPluginAssemblyAsync(plugin).ConfigureAwait(false); + + await assemblyStream.CopyToAsync(memoryStream).ConfigureAwait(false); + + memoryStream.Position = 0; + + using (FileStream fileStream = new FileStream(path, FileMode.Create)) + { + await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false); + } + } + } + + /// + /// Downloads the latest configuration for a plugin + /// + private async Task DownloadPluginConfiguration(BasePlugin plugin, PluginInfo pluginInfo) + { + Logger.LogInfo("Downloading {0} Configuration", plugin.Name); + + object config = await UIKernel.Instance.ApiClient.GetPluginConfigurationAsync(pluginInfo, plugin.ConfigurationType).ConfigureAwait(false); + + XmlSerializer.SerializeToFile(config, plugin.ConfigurationFilePath); + + File.SetLastWriteTimeUtc(plugin.ConfigurationFilePath, pluginInfo.ConfigurationDateLastModified); + } + + /// + /// Deletes any plugins that have been uninstalled from the server + /// + private IEnumerable DeleteUninstalledPlugins(IEnumerable uiPlugins) + { + var deletedPlugins = new List(); + + foreach (BasePlugin plugin in CurrentPlugins) + { + PluginInfo latest = uiPlugins.FirstOrDefault(p => p.AssemblyFileName.Equals(plugin.AssemblyFileName, StringComparison.OrdinalIgnoreCase)); + + if (latest == null) + { + DeletePlugin(plugin); + + deletedPlugins.Add(plugin.Name); + } + } + + return deletedPlugins; + } + + /// + /// Deletes an installed ui plugin. + /// Leaves config and data behind in the event it is later re-installed + /// + private void DeletePlugin(BasePlugin plugin) + { + Logger.LogInfo("Deleting {0} Plugin", plugin.Name); + + string path = plugin.AssemblyFilePath; + + if (File.Exists(path)) + { + File.Delete(path); + } + } + + /// + /// Re-uses MEF within the kernel to discover installed plugins + /// + private void ReloadComposableParts() + { + if (CompositionContainer != null) + { + CompositionContainer.Dispose(); + } + + CompositionContainer = UIKernel.Instance.GetCompositionContainer(); + + CompositionContainer.ComposeParts(this); + + CompositionContainer.Catalog.Dispose(); + + foreach (BasePlugin plugin in CurrentPlugins) + { + plugin.Initialize(UIKernel.Instance, false); + } + } + } + + public class PluginUpdateResult + { + public IEnumerable DeletedPlugins { get; set; } + public IEnumerable NewlyInstalledPlugins { get; set; } + public IEnumerable UpdatedPlugins { get; set; } + public IEnumerable UpdatedConfigurations { get; set; } + } +} -- cgit v1.2.3