mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-07-05 05:42:52 +01:00
Pushing missing changes
This commit is contained in:
60
MediaBrowser.UI/Controller/BaseTheme.cs
Normal file
60
MediaBrowser.UI/Controller/BaseTheme.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MediaBrowser.UI.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseTheme
|
||||
/// </summary>
|
||||
public abstract class BaseTheme : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the global resources.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{ResourceDictionary}.</returns>
|
||||
public abstract IEnumerable<ResourceDictionary> GetGlobalResources();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list page.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>Page.</returns>
|
||||
public abstract Page GetListPage(DtoBaseItem item);
|
||||
/// <summary>
|
||||
/// Gets the detail page.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>Page.</returns>
|
||||
public abstract Page GetDetailPage(DtoBaseItem item);
|
||||
/// <summary>
|
||||
/// Gets the home page.
|
||||
/// </summary>
|
||||
/// <returns>Page.</returns>
|
||||
public abstract Page GetHomePage();
|
||||
/// <summary>
|
||||
/// Gets the login page.
|
||||
/// </summary>
|
||||
/// <returns>Page.</returns>
|
||||
public abstract Page GetLoginPage();
|
||||
/// <summary>
|
||||
/// Gets the internal player page.
|
||||
/// </summary>
|
||||
/// <returns>Page.</returns>
|
||||
public abstract Page GetInternalPlayerPage();
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the weather.
|
||||
/// </summary>
|
||||
public abstract void DisplayWeather();
|
||||
}
|
||||
}
|
||||
@@ -1,231 +1,306 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// This keeps ui plugin assemblies in sync with plugins installed on the server
|
||||
/// </summary>
|
||||
public class PluginUpdater
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of currently installed UI plugins
|
||||
/// </summary>
|
||||
[ImportMany(typeof(BasePlugin))]
|
||||
private IEnumerable<BasePlugin> CurrentPlugins { get; set; }
|
||||
|
||||
private CompositionContainer CompositionContainer { get; set; }
|
||||
|
||||
public async Task<PluginUpdateResult> 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<PluginInfo> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads plugin assemblies from the server, if they need to be installed or updated.
|
||||
/// </summary>
|
||||
private async Task DownloadPluginAssemblies(IEnumerable<PluginInfo> uiPlugins, PluginUpdateResult result)
|
||||
{
|
||||
List<PluginInfo> newlyInstalledPlugins = new List<PluginInfo>();
|
||||
List<PluginInfo> updatedPlugins = new List<PluginInfo>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads plugin configurations from the server.
|
||||
/// </summary>
|
||||
private async Task<List<PluginInfo>> DownloadPluginConfigurations(IEnumerable<PluginInfo> uiPlugins)
|
||||
{
|
||||
List<PluginInfo> updatedPlugins = new List<PluginInfo>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a plugin assembly from the server
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the latest configuration for a plugin
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes any plugins that have been uninstalled from the server
|
||||
/// </summary>
|
||||
private IEnumerable<string> DeleteUninstalledPlugins(IEnumerable<PluginInfo> uiPlugins)
|
||||
{
|
||||
var deletedPlugins = new List<string>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an installed ui plugin.
|
||||
/// Leaves config and data behind in the event it is later re-installed
|
||||
/// </summary>
|
||||
private void DeletePlugin(BasePlugin plugin)
|
||||
{
|
||||
Logger.LogInfo("Deleting {0} Plugin", plugin.Name);
|
||||
|
||||
string path = plugin.AssemblyFilePath;
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-uses MEF within the kernel to discover installed plugins
|
||||
/// </summary>
|
||||
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<string> DeletedPlugins { get; set; }
|
||||
public IEnumerable<PluginInfo> NewlyInstalledPlugins { get; set; }
|
||||
public IEnumerable<PluginInfo> UpdatedPlugins { get; set; }
|
||||
public IEnumerable<PluginInfo> UpdatedConfigurations { get; set; }
|
||||
}
|
||||
}
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Logging;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.UI.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// This keeps ui plugin assemblies in sync with plugins installed on the server
|
||||
/// </summary>
|
||||
public class PluginUpdater
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the plugins.
|
||||
/// </summary>
|
||||
/// <returns>Task{PluginUpdateResult}.</returns>
|
||||
public async Task<PluginUpdateResult> UpdatePlugins()
|
||||
{
|
||||
Logger.LogInfo("Downloading list of installed plugins");
|
||||
var allInstalledPlugins = await UIKernel.Instance.ApiClient.GetInstalledPluginsAsync().ConfigureAwait(false);
|
||||
|
||||
var uiPlugins = allInstalledPlugins.Where(p => p.DownloadToUI).ToList();
|
||||
|
||||
var result = new PluginUpdateResult { };
|
||||
|
||||
result.DeletedPlugins = DeleteUninstalledPlugins(uiPlugins);
|
||||
|
||||
await DownloadPluginAssemblies(uiPlugins, result).ConfigureAwait(false);
|
||||
|
||||
result.UpdatedConfigurations = await DownloadPluginConfigurations(uiPlugins).ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads plugin assemblies from the server, if they need to be installed or updated.
|
||||
/// </summary>
|
||||
/// <param name="uiPlugins">The UI plugins.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <returns>Task.</returns>
|
||||
private async Task DownloadPluginAssemblies(IEnumerable<PluginInfo> uiPlugins, PluginUpdateResult result)
|
||||
{
|
||||
var newlyInstalledPlugins = new List<PluginInfo>();
|
||||
var updatedPlugins = new List<PluginInfo>();
|
||||
|
||||
// Loop through the list of plugins that are on the server
|
||||
foreach (var pluginInfo in uiPlugins)
|
||||
{
|
||||
// See if it is already installed in the UI
|
||||
var currentAssemblyPath = Path.Combine(UIKernel.Instance.ApplicationPaths.PluginsPath, pluginInfo.AssemblyFileName);
|
||||
|
||||
var isPluginInstalled = File.Exists(currentAssemblyPath);
|
||||
|
||||
// Download the plugin if it is not present, or if the current version is out of date
|
||||
bool downloadPlugin;
|
||||
|
||||
if (!isPluginInstalled)
|
||||
{
|
||||
downloadPlugin = true;
|
||||
Logger.LogInfo("{0} is not installed and needs to be downloaded.", pluginInfo.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
var serverVersion = Version.Parse(pluginInfo.Version);
|
||||
|
||||
var fileVersion = FileVersionInfo.GetVersionInfo(currentAssemblyPath).FileVersion ?? string.Empty;
|
||||
|
||||
downloadPlugin = string.IsNullOrEmpty(fileVersion) || Version.Parse(fileVersion) < serverVersion;
|
||||
|
||||
if (downloadPlugin)
|
||||
{
|
||||
Logger.LogInfo("{0} has an updated version on the server and needs to be downloaded. Server version: {1}, UI version: {2}", pluginInfo.Name, serverVersion, fileVersion);
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadPlugin)
|
||||
{
|
||||
if (UIKernel.Instance.ApplicationVersion < Version.Parse(pluginInfo.MinimumRequiredUIVersion))
|
||||
{
|
||||
Logger.LogWarning("Can't download new version of {0} because the application needs to be updated first.", pluginInfo.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await DownloadPlugin(pluginInfo).ConfigureAwait(false);
|
||||
|
||||
if (isPluginInstalled)
|
||||
{
|
||||
updatedPlugins.Add(pluginInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
newlyInstalledPlugins.Add(pluginInfo);
|
||||
}
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
Logger.LogException("Error downloading {0} configuration", ex, pluginInfo.Name);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.LogException("Error saving plugin assembly for {0}", ex, pluginInfo.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.NewlyInstalledPlugins = newlyInstalledPlugins;
|
||||
result.UpdatedPlugins = updatedPlugins;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads plugin configurations from the server.
|
||||
/// </summary>
|
||||
/// <param name="uiPlugins">The UI plugins.</param>
|
||||
/// <returns>Task{List{PluginInfo}}.</returns>
|
||||
private async Task<List<PluginInfo>> DownloadPluginConfigurations(IEnumerable<PluginInfo> uiPlugins)
|
||||
{
|
||||
var updatedPlugins = new List<PluginInfo>();
|
||||
|
||||
// Loop through the list of plugins that are on the server
|
||||
foreach (var pluginInfo in uiPlugins
|
||||
.Where(p => UIKernel.Instance.ApplicationVersion >= Version.Parse(p.MinimumRequiredUIVersion)))
|
||||
{
|
||||
// See if it is already installed in the UI
|
||||
var path = Path.Combine(UIKernel.Instance.ApplicationPaths.PluginConfigurationsPath, pluginInfo.ConfigurationFileName);
|
||||
|
||||
var download = false;
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
download = true;
|
||||
Logger.LogInfo("{0} configuration was not found needs to be downloaded.", pluginInfo.Name);
|
||||
}
|
||||
else if (File.GetLastWriteTimeUtc(path) < pluginInfo.ConfigurationDateLastModified)
|
||||
{
|
||||
download = true;
|
||||
Logger.LogInfo("{0} has an updated configuration on the server and needs to be downloaded.", pluginInfo.Name);
|
||||
}
|
||||
|
||||
if (download)
|
||||
{
|
||||
if (UIKernel.Instance.ApplicationVersion < Version.Parse(pluginInfo.MinimumRequiredUIVersion))
|
||||
{
|
||||
Logger.LogWarning("Can't download updated configuration of {0} because the application needs to be updated first.", pluginInfo.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await DownloadPluginConfiguration(pluginInfo, path).ConfigureAwait(false);
|
||||
|
||||
updatedPlugins.Add(pluginInfo);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
Logger.LogException("Error downloading {0} configuration", ex, pluginInfo.Name);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.LogException("Error saving plugin configuration to {0}", ex, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedPlugins;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a plugin assembly from the server
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin.</param>
|
||||
/// <returns>Task.</returns>
|
||||
private async Task DownloadPlugin(PluginInfo plugin)
|
||||
{
|
||||
Logger.LogInfo("Downloading {0} Plugin", plugin.Name);
|
||||
|
||||
var 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 (var memoryStream = new MemoryStream())
|
||||
{
|
||||
var assemblyStream = await UIKernel.Instance.ApiClient.GetPluginAssemblyAsync(plugin).ConfigureAwait(false);
|
||||
|
||||
await assemblyStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using (var fileStream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the latest configuration for a plugin
|
||||
/// </summary>
|
||||
/// <param name="pluginInfo">The plugin info.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>Task.</returns>
|
||||
private async Task DownloadPluginConfiguration(PluginInfo pluginInfo, string path)
|
||||
{
|
||||
Logger.LogInfo("Downloading {0} Configuration", pluginInfo.Name);
|
||||
|
||||
// First download to a MemoryStream. This way if the download is cut off, we won't be left with a partial file
|
||||
using (var stream = await UIKernel.Instance.ApiClient.GetPluginConfigurationFileAsync(pluginInfo.UniqueId).ConfigureAwait(false))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
|
||||
{
|
||||
await memoryStream.CopyToAsync(fs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File.SetLastWriteTimeUtc(path, pluginInfo.ConfigurationDateLastModified);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes any plugins that have been uninstalled from the server
|
||||
/// </summary>
|
||||
/// <param name="uiPlugins">The UI plugins.</param>
|
||||
/// <returns>IEnumerable{System.String}.</returns>
|
||||
private IEnumerable<string> DeleteUninstalledPlugins(IEnumerable<PluginInfo> uiPlugins)
|
||||
{
|
||||
var deletedPlugins = new List<string>();
|
||||
|
||||
foreach (var plugin in Directory.EnumerateFiles(UIKernel.Instance.ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
|
||||
.Select(Path.GetFileName)
|
||||
.ToList())
|
||||
{
|
||||
var serverPlugin = uiPlugins.FirstOrDefault(p => p.AssemblyFileName.Equals(plugin, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (serverPlugin == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeletePlugin(plugin);
|
||||
|
||||
deletedPlugins.Add(plugin);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.LogException("Error deleting plugin assembly {0}", ex, plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deletedPlugins;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an installed ui plugin.
|
||||
/// Leaves config and data behind in the event it is later re-installed
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin.</param>
|
||||
private void DeletePlugin(string plugin)
|
||||
{
|
||||
Logger.LogInfo("Deleting {0} Plugin", plugin);
|
||||
|
||||
if (File.Exists(plugin))
|
||||
{
|
||||
File.Delete(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class PluginUpdateResult
|
||||
/// </summary>
|
||||
public class PluginUpdateResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the deleted plugins.
|
||||
/// </summary>
|
||||
/// <value>The deleted plugins.</value>
|
||||
public IEnumerable<string> DeletedPlugins { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the newly installed plugins.
|
||||
/// </summary>
|
||||
/// <value>The newly installed plugins.</value>
|
||||
public IEnumerable<PluginInfo> NewlyInstalledPlugins { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the updated plugins.
|
||||
/// </summary>
|
||||
/// <value>The updated plugins.</value>
|
||||
public IEnumerable<PluginInfo> UpdatedPlugins { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the updated configurations.
|
||||
/// </summary>
|
||||
/// <value>The updated configurations.</value>
|
||||
public IEnumerable<PluginInfo> UpdatedConfigurations { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,201 @@
|
||||
using MediaBrowser.ApiInteraction;
|
||||
using MediaBrowser.Common.Kernel;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Progress;
|
||||
using MediaBrowser.UI.Configuration;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.UI.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// This controls application logic as well as server interaction within the UI.
|
||||
/// </summary>
|
||||
public class UIKernel : BaseKernel<UIApplicationConfiguration, UIApplicationPaths>
|
||||
{
|
||||
public static UIKernel Instance { get; private set; }
|
||||
|
||||
public ApiClient ApiClient { get; private set; }
|
||||
public DtoUser CurrentUser { get; set; }
|
||||
public ServerConfiguration ServerConfiguration { get; set; }
|
||||
|
||||
public UIKernel()
|
||||
: base()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override KernelContext KernelContext
|
||||
{
|
||||
get { return KernelContext.Ui; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Give the UI a different url prefix so that they can share the same port, in case they are installed on the same machine.
|
||||
/// </summary>
|
||||
protected override string HttpServerUrlPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return "http://+:" + Configuration.HttpServerPortNumber + "/mediabrowser/ui/";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs initializations that can be reloaded at anytime
|
||||
/// </summary>
|
||||
protected override async Task ReloadInternal(IProgress<TaskProgress> progress)
|
||||
{
|
||||
ReloadApiClient();
|
||||
|
||||
await new PluginUpdater().UpdatePlugins().ConfigureAwait(false);
|
||||
|
||||
await base.ReloadInternal(progress).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates and installs new plugin assemblies and configurations from the server
|
||||
/// </summary>
|
||||
protected async Task<PluginUpdateResult> UpdatePlugins()
|
||||
{
|
||||
return await new PluginUpdater().UpdatePlugins().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the current ApiClient and creates a new one
|
||||
/// </summary>
|
||||
private void ReloadApiClient()
|
||||
{
|
||||
DisposeApiClient();
|
||||
|
||||
ApiClient = new ApiClient
|
||||
{
|
||||
ServerHostName = Configuration.ServerHostName,
|
||||
ServerApiPort = Configuration.ServerApiPort
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the current ApiClient
|
||||
/// </summary>
|
||||
private void DisposeApiClient()
|
||||
{
|
||||
if (ApiClient != null)
|
||||
{
|
||||
ApiClient.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
DisposeApiClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Net;
|
||||
using System.Net.Cache;
|
||||
using System.Net.Http;
|
||||
using MediaBrowser.ApiInteraction;
|
||||
using MediaBrowser.Common.Kernel;
|
||||
using MediaBrowser.Common.Logging;
|
||||
using MediaBrowser.Model.Connectivity;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.UI.Configuration;
|
||||
using MediaBrowser.UI.Playback;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.UI.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// This controls application logic as well as server interaction within the UI.
|
||||
/// </summary>
|
||||
public class UIKernel : BaseKernel<UIApplicationConfiguration, UIApplicationPaths>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance.
|
||||
/// </summary>
|
||||
/// <value>The instance.</value>
|
||||
public static UIKernel Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the API client.
|
||||
/// </summary>
|
||||
/// <value>The API client.</value>
|
||||
public ApiClient ApiClient { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the playback manager.
|
||||
/// </summary>
|
||||
/// <value>The playback manager.</value>
|
||||
public PlaybackManager PlaybackManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UIKernel" /> class.
|
||||
/// </summary>
|
||||
public UIKernel()
|
||||
: base()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media players.
|
||||
/// </summary>
|
||||
/// <value>The media players.</value>
|
||||
[ImportMany(typeof(BaseMediaPlayer))]
|
||||
public IEnumerable<BaseMediaPlayer> MediaPlayers { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of currently loaded themes
|
||||
/// </summary>
|
||||
/// <value>The themes.</value>
|
||||
[ImportMany(typeof(BaseTheme))]
|
||||
public IEnumerable<BaseTheme> Themes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the kernel context.
|
||||
/// </summary>
|
||||
/// <value>The kernel context.</value>
|
||||
public override KernelContext KernelContext
|
||||
{
|
||||
get { return KernelContext.Ui; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UDP server port number.
|
||||
/// </summary>
|
||||
/// <value>The UDP server port number.</value>
|
||||
public override int UdpServerPortNumber
|
||||
{
|
||||
get { return 7360; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Give the UI a different url prefix so that they can share the same port, in case they are installed on the same machine.
|
||||
/// </summary>
|
||||
/// <value>The HTTP server URL prefix.</value>
|
||||
public override string HttpServerUrlPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return "http://+:" + Configuration.HttpServerPortNumber + "/mediabrowserui/";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reload api client and update plugins after loading configuration
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
protected override async Task OnConfigurationLoaded()
|
||||
{
|
||||
ReloadApiClient();
|
||||
|
||||
try
|
||||
{
|
||||
await new PluginUpdater().UpdatePlugins().ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
Logger.ErrorException("Error updating plugins from the server", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the current ApiClient and creates a new one
|
||||
/// </summary>
|
||||
private void ReloadApiClient()
|
||||
{
|
||||
DisposeApiClient();
|
||||
|
||||
var logger = LogManager.GetLogger("ApiClient");
|
||||
|
||||
ApiClient = new ApiClient(logger, new AsyncHttpClient(new WebRequestHandler
|
||||
{
|
||||
AutomaticDecompression = DecompressionMethods.Deflate,
|
||||
CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate)
|
||||
}))
|
||||
{
|
||||
ServerHostName = Configuration.ServerHostName,
|
||||
ServerApiPort = Configuration.ServerApiPort,
|
||||
ClientType = ClientType.Pc,
|
||||
DeviceName = Environment.MachineName,
|
||||
SerializationFormat = SerializationFormats.Json
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the internal.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
protected override Task ReloadInternal()
|
||||
{
|
||||
PlaybackManager = new PlaybackManager(this);
|
||||
|
||||
return base.ReloadInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the composable part assemblies.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{Assembly}.</returns>
|
||||
protected override IEnumerable<Assembly> GetComposablePartAssemblies()
|
||||
{
|
||||
var runningDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
|
||||
|
||||
return base.GetComposablePartAssemblies().Concat(new[] {
|
||||
|
||||
Assembly.Load(File.ReadAllBytes(Path.Combine(runningDirectory, "MediaBrowser.Plugins.DefaultTheme.dll")))
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [composable parts loaded].
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
protected override async Task OnComposablePartsLoaded()
|
||||
{
|
||||
await base.OnComposablePartsLoaded().ConfigureAwait(false);
|
||||
|
||||
// Once plugins have loaded give the api a reference to our protobuf serializer
|
||||
DataSerializer.DynamicSerializer = ProtobufSerializer.TypeModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the current ApiClient
|
||||
/// </summary>
|
||||
private void DisposeApiClient()
|
||||
{
|
||||
if (ApiClient != null)
|
||||
{
|
||||
ApiClient.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected override void Dispose(bool dispose)
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
DisposeApiClient();
|
||||
}
|
||||
|
||||
base.Dispose(dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user