diff options
Diffstat (limited to 'MediaBrowser.Controller')
| -rw-r--r-- | MediaBrowser.Controller/Kernel.cs | 14 | ||||
| -rw-r--r-- | MediaBrowser.Controller/MediaBrowser.Controller.csproj | 2 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Updates/IInstallationManager.cs | 106 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Updates/InstallationManager.cs | 494 |
4 files changed, 112 insertions, 504 deletions
diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 9f97b1719..947fb8e6d 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -48,12 +48,6 @@ namespace MediaBrowser.Controller public FFMpegManager FFMpegManager { get; private set; } /// <summary> - /// Gets the installation manager. - /// </summary> - /// <value>The installation manager.</value> - public InstallationManager InstallationManager { get; set; } - - /// <summary> /// Gets or sets the file system manager. /// </summary> /// <value>The file system manager.</value> @@ -331,10 +325,12 @@ namespace MediaBrowser.Controller { var info = base.GetSystemInfo(); - if (InstallationManager != null) + var installationManager = ApplicationHost.Resolve<IInstallationManager>(); + + if (installationManager != null) { - info.InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToArray(); - info.CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(); + info.InProgressInstallations = installationManager.CurrentInstallations.Select(i => i.Item1).ToArray(); + info.CompletedInstallations = installationManager.CompletedInstallations.ToArray(); } return info; diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 0fc97b049..bdf464bbf 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -185,7 +185,7 @@ <Compile Include="Providers\MediaInfo\FFProbeVideoInfoProvider.cs" /> <Compile Include="Sorting\BaseItemComparer.cs" /> <Compile Include="Sorting\SortOrder.cs" /> - <Compile Include="Updates\InstallationManager.cs" /> + <Compile Include="Updates\IInstallationManager.cs" /> <Compile Include="Weather\IWeatherProvider.cs" /> <Compile Include="Providers\BaseItemXmlParser.cs" /> </ItemGroup> diff --git a/MediaBrowser.Controller/Updates/IInstallationManager.cs b/MediaBrowser.Controller/Updates/IInstallationManager.cs new file mode 100644 index 000000000..dc6aaf0c9 --- /dev/null +++ b/MediaBrowser.Controller/Updates/IInstallationManager.cs @@ -0,0 +1,106 @@ +using MediaBrowser.Common.Events; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Updates; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Updates +{ + public interface IInstallationManager : IDisposable + { + event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstalling; + event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationCompleted; + event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationFailed; + event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationCancelled; + + /// <summary> + /// The current installations + /// </summary> + List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; } + + /// <summary> + /// The completed installations + /// </summary> + ConcurrentBag<InstallationInfo> CompletedInstallations { get; set; } + + /// <summary> + /// Occurs when [plugin uninstalled]. + /// </summary> + event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled; + + /// <summary> + /// Occurs when [plugin updated]. + /// </summary> + event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated; + + /// <summary> + /// Occurs when [plugin updated]. + /// </summary> + event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; + + /// <summary> + /// Gets all available packages. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="packageType">Type of the package.</param> + /// <param name="applicationVersion">The application version.</param> + /// <returns>Task{List{PackageInfo}}.</returns> + Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken, + PackageType? packageType = null, + Version applicationVersion = null); + + /// <summary> + /// Gets the package. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="classification">The classification.</param> + /// <param name="version">The version.</param> + /// <returns>Task{PackageVersionInfo}.</returns> + Task<PackageVersionInfo> GetPackage(string name, PackageVersionClass classification, Version version); + + /// <summary> + /// Gets the latest compatible version. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="classification">The classification.</param> + /// <returns>Task{PackageVersionInfo}.</returns> + Task<PackageVersionInfo> GetLatestCompatibleVersion(string name, PackageVersionClass classification = PackageVersionClass.Release); + + /// <summary> + /// Gets the latest compatible version. + /// </summary> + /// <param name="availablePackages">The available packages.</param> + /// <param name="name">The name.</param> + /// <param name="classification">The classification.</param> + /// <returns>PackageVersionInfo.</returns> + PackageVersionInfo GetLatestCompatibleVersion(IEnumerable<PackageInfo> availablePackages, string name, PackageVersionClass classification = PackageVersionClass.Release); + + /// <summary> + /// Gets the available plugin updates. + /// </summary> + /// <param name="withAutoUpdateEnabled">if set to <c>true</c> [with auto update enabled].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns> + Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(bool withAutoUpdateEnabled, CancellationToken cancellationToken); + + /// <summary> + /// Installs the package. + /// </summary> + /// <param name="package">The package.</param> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException">package</exception> + Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken); + + /// <summary> + /// Uninstalls a plugin + /// </summary> + /// <param name="plugin">The plugin.</param> + /// <exception cref="System.ArgumentException"></exception> + void UninstallPlugin(IPlugin plugin); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Updates/InstallationManager.cs b/MediaBrowser.Controller/Updates/InstallationManager.cs deleted file mode 100644 index 9a2733ce0..000000000 --- a/MediaBrowser.Controller/Updates/InstallationManager.cs +++ /dev/null @@ -1,494 +0,0 @@ -using MediaBrowser.Common; -using MediaBrowser.Common.Events; -using MediaBrowser.Common.Kernel; -using MediaBrowser.Common.Net; -using MediaBrowser.Common.Plugins; -using MediaBrowser.Common.Progress; -using MediaBrowser.Common.Updates; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Updates; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Updates -{ - /// <summary> - /// Manages all install, uninstall and update operations (both plugins and system) - /// </summary> - public class InstallationManager : BaseManager<Kernel> - { - public event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstalling; - public event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationCompleted; - public event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationFailed; - public event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationCancelled; - - /// <summary> - /// The current installations - /// </summary> - public List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; } - - /// <summary> - /// The completed installations - /// </summary> - public ConcurrentBag<InstallationInfo> CompletedInstallations { get; set; } - - #region PluginUninstalled Event - /// <summary> - /// Occurs when [plugin uninstalled]. - /// </summary> - public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled; - - /// <summary> - /// Called when [plugin uninstalled]. - /// </summary> - /// <param name="plugin">The plugin.</param> - private void OnPluginUninstalled(IPlugin plugin) - { - EventHelper.QueueEventIfNotNull(PluginUninstalled, this, new GenericEventArgs<IPlugin> { Argument = plugin }, _logger); - } - #endregion - - #region PluginUpdated Event - /// <summary> - /// Occurs when [plugin updated]. - /// </summary> - public event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated; - /// <summary> - /// Called when [plugin updated]. - /// </summary> - /// <param name="plugin">The plugin.</param> - /// <param name="newVersion">The new version.</param> - public void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion) - { - _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.version, newVersion.classification); - - EventHelper.QueueEventIfNotNull(PluginUpdated, this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) }, _logger); - - Kernel.NotifyPendingRestart(); - } - #endregion - - #region PluginInstalled Event - /// <summary> - /// Occurs when [plugin updated]. - /// </summary> - public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; - /// <summary> - /// Called when [plugin installed]. - /// </summary> - /// <param name="package">The package.</param> - public void OnPluginInstalled(PackageVersionInfo package) - { - _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.version, package.classification); - - EventHelper.QueueEventIfNotNull(PluginInstalled, this, new GenericEventArgs<PackageVersionInfo> { Argument = package }, _logger); - - Kernel.NotifyPendingRestart(); - } - #endregion - - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - /// <summary> - /// The _network manager - /// </summary> - private readonly INetworkManager _networkManager; - - /// <summary> - /// The package manager - /// </summary> - private readonly IPackageManager _packageManager; - - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - protected IJsonSerializer JsonSerializer { get; private set; } - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// Gets the application host. - /// </summary> - /// <value>The application host.</value> - protected IApplicationHost ApplicationHost { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="InstallationManager" /> class. - /// </summary> - /// <param name="kernel">The kernel.</param> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="networkManager">The network manager.</param> - /// <param name="packageManager">The package manager.</param> - /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="logger">The logger.</param> - /// <param name="appHost">The app host.</param> - /// <exception cref="System.ArgumentNullException">zipClient</exception> - public InstallationManager(Kernel kernel, IHttpClient httpClient, INetworkManager networkManager, IPackageManager packageManager, IJsonSerializer jsonSerializer, ILogger logger, IApplicationHost appHost) - : base(kernel) - { - if (networkManager == null) - { - throw new ArgumentNullException("networkManager"); - } - if (packageManager == null) - { - throw new ArgumentNullException("packageManager"); - } - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - if (jsonSerializer == null) - { - throw new ArgumentNullException("jsonSerializer"); - } - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - - CurrentInstallations = new List<Tuple<InstallationInfo, CancellationTokenSource>>(); - CompletedInstallations = new ConcurrentBag<InstallationInfo>(); - JsonSerializer = jsonSerializer; - HttpClient = httpClient; - ApplicationHost = appHost; - _networkManager = networkManager; - _packageManager = packageManager; - _logger = logger; - } - - /// <summary> - /// Gets all available packages. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="packageType">Type of the package.</param> - /// <param name="applicationVersion">The application version.</param> - /// <returns>Task{List{PackageInfo}}.</returns> - public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken, - PackageType? packageType = null, - Version applicationVersion = null) - { - var packages = (await _packageManager.GetAvailablePackages(cancellationToken).ConfigureAwait(false)).ToList(); - - if (packageType.HasValue) - { - packages = packages.Where(p => p.type == packageType.Value).ToList(); - } - - // If an app version was supplied, filter the versions for each package to only include supported versions - if (applicationVersion != null) - { - foreach (var package in packages) - { - package.versions = package.versions.Where(v => IsPackageVersionUpToDate(v, applicationVersion)).ToList(); - } - } - - // Remove packages with no versions - packages = packages.Where(p => p.versions.Any()).ToList(); - - return packages; - } - - /// <summary> - /// Determines whether [is package version up to date] [the specified package version info]. - /// </summary> - /// <param name="packageVersionInfo">The package version info.</param> - /// <param name="applicationVersion">The application version.</param> - /// <returns><c>true</c> if [is package version up to date] [the specified package version info]; otherwise, <c>false</c>.</returns> - private bool IsPackageVersionUpToDate(PackageVersionInfo packageVersionInfo, Version applicationVersion) - { - if (string.IsNullOrEmpty(packageVersionInfo.requiredVersionStr)) - { - return true; - } - - Version requiredVersion; - - return Version.TryParse(packageVersionInfo.requiredVersionStr, out requiredVersion) && applicationVersion >= requiredVersion; - } - - /// <summary> - /// Gets the package. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="classification">The classification.</param> - /// <param name="version">The version.</param> - /// <returns>Task{PackageVersionInfo}.</returns> - public async Task<PackageVersionInfo> GetPackage(string name, PackageVersionClass classification, Version version) - { - var packages = await GetAvailablePackages(CancellationToken.None).ConfigureAwait(false); - - var package = packages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase)); - - if (package == null) - { - return null; - } - - return package.versions.FirstOrDefault(v => v.version.Equals(version) && v.classification == classification); - } - - /// <summary> - /// Gets the latest compatible version. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="classification">The classification.</param> - /// <returns>Task{PackageVersionInfo}.</returns> - public async Task<PackageVersionInfo> GetLatestCompatibleVersion(string name, PackageVersionClass classification = PackageVersionClass.Release) - { - var packages = await GetAvailablePackages(CancellationToken.None).ConfigureAwait(false); - - return GetLatestCompatibleVersion(packages, name, classification); - } - - /// <summary> - /// Gets the latest compatible version. - /// </summary> - /// <param name="availablePackages">The available packages.</param> - /// <param name="name">The name.</param> - /// <param name="classification">The classification.</param> - /// <returns>PackageVersionInfo.</returns> - public PackageVersionInfo GetLatestCompatibleVersion(IEnumerable<PackageInfo> availablePackages, string name, PackageVersionClass classification = PackageVersionClass.Release) - { - var package = availablePackages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase)); - - if (package == null) - { - return null; - } - - return package.versions - .OrderByDescending(v => v.version) - .FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, ApplicationHost.ApplicationVersion)); - } - - /// <summary> - /// Gets the available plugin updates. - /// </summary> - /// <param name="withAutoUpdateEnabled">if set to <c>true</c> [with auto update enabled].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns> - public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(bool withAutoUpdateEnabled, CancellationToken cancellationToken) - { - var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); - - var plugins = ApplicationHost.Plugins; - - if (withAutoUpdateEnabled) - { - plugins = plugins.Where(p => p.Configuration.EnableAutoUpdate); - } - - // Figure out what needs to be installed - return plugins.Select(p => - { - var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Configuration.UpdateClass); - - return latestPluginInfo != null && latestPluginInfo.version > p.Version ? latestPluginInfo : null; - - }).Where(p => !CompletedInstallations.Any(i => i.Name.Equals(p.name, StringComparison.OrdinalIgnoreCase))) - .Where(p => p != null && !string.IsNullOrWhiteSpace(p.sourceUrl)); - } - - /// <summary> - /// Installs the package. - /// </summary> - /// <param name="package">The package.</param> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException">package</exception> - public async Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken) - { - if (package == null) - { - throw new ArgumentNullException("package"); - } - - if (progress == null) - { - throw new ArgumentNullException("progress"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - var installationInfo = new InstallationInfo - { - Id = Guid.NewGuid(), - Name = package.name, - UpdateClass = package.classification, - Version = package.versionStr - }; - - var innerCancellationTokenSource = new CancellationTokenSource(); - - var tuple = new Tuple<InstallationInfo, CancellationTokenSource>(installationInfo, innerCancellationTokenSource); - - // Add it to the in-progress list - lock (CurrentInstallations) - { - CurrentInstallations.Add(tuple); - } - - var innerProgress = new ActionableProgress<double> { }; - - // Whenever the progress updates, update the outer progress object and InstallationInfo - innerProgress.RegisterAction(percent => - { - progress.Report(percent); - - installationInfo.PercentComplete = percent; - }); - - var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token; - - EventHelper.QueueEventIfNotNull(PackageInstalling, this, new GenericEventArgs<InstallationInfo>() { Argument = installationInfo }, _logger); - - try - { - await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false); - - lock (CurrentInstallations) - { - CurrentInstallations.Remove(tuple); - } - - CompletedInstallations.Add(installationInfo); - - EventHelper.QueueEventIfNotNull(PackageInstallationCompleted, this, new GenericEventArgs<InstallationInfo>() { Argument = installationInfo }, _logger); - } - catch (OperationCanceledException) - { - lock (CurrentInstallations) - { - CurrentInstallations.Remove(tuple); - } - - _logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr); - - EventHelper.QueueEventIfNotNull(PackageInstallationCancelled, this, new GenericEventArgs<InstallationInfo>() { Argument = installationInfo }, _logger); - - throw; - } - catch - { - lock (CurrentInstallations) - { - CurrentInstallations.Remove(tuple); - } - - EventHelper.QueueEventIfNotNull(PackageInstallationFailed, this, new GenericEventArgs<InstallationInfo>() { Argument = installationInfo }, _logger); - - throw; - } - finally - { - // Dispose the progress object and remove the installation from the in-progress list - - innerProgress.Dispose(); - tuple.Item2.Dispose(); - } - } - - /// <summary> - /// Installs the package internal. - /// </summary> - /// <param name="package">The package.</param> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken) - { - // Do the install - await _packageManager.InstallPackage(progress, package, cancellationToken).ConfigureAwait(false); - - // Do plugin-specific processing - if (!(Path.GetExtension(package.targetFilename) ?? "").Equals(".zip", StringComparison.OrdinalIgnoreCase)) - { - // Set last update time if we were installed before - var plugin = ApplicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase)); - - if (plugin != null) - { - // Synchronize the UpdateClass value - if (plugin.Configuration.UpdateClass != package.classification) - { - plugin.Configuration.UpdateClass = package.classification; - plugin.SaveConfiguration(); - } - - OnPluginUpdated(plugin, package); - } - else - { - OnPluginInstalled(package); - } - - } - } - - /// <summary> - /// Uninstalls a plugin - /// </summary> - /// <param name="plugin">The plugin.</param> - /// <exception cref="System.ArgumentException"></exception> - public void UninstallPlugin(IPlugin plugin) - { - if (plugin.IsCorePlugin) - { - throw new ArgumentException(string.Format("{0} cannot be uninstalled because it is a core plugin.", plugin.Name)); - } - - plugin.OnUninstalling(); - - // Remove it the quick way for now - ApplicationHost.RemovePlugin(plugin); - - File.Delete(plugin.AssemblyFilePath); - - OnPluginUninstalled(plugin); - - Kernel.NotifyPendingRestart(); - } - - /// <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) - { - lock (CurrentInstallations) - { - foreach (var tuple in CurrentInstallations) - { - tuple.Item2.Dispose(); - } - - CurrentInstallations.Clear(); - } - } - base.Dispose(dispose); - } - } -} |
