From a5f97dcaa830e672ed1aad65166719f56d3d4495 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 30 Jun 2013 22:27:50 -0400 Subject: move installation manager down to common --- MediaBrowser.Api/PackageService.cs | 2 +- MediaBrowser.Api/PluginService.cs | 2 +- .../BaseApplicationHost.cs | 9 + .../MediaBrowser.Common.Implementations.csproj | 1 + .../Updates/InstallationManager.cs | 511 ++++++++++++++++++++ MediaBrowser.Common/MediaBrowser.Common.csproj | 1 + .../Updates/IInstallationManager.cs | 114 +++++ .../MediaBrowser.Controller.csproj | 1 - .../Updates/IInstallationManager.cs | 114 ----- MediaBrowser.Providers/Savers/XmlSaverHelpers.cs | 2 +- .../MediaBrowser.Server.Implementations.csproj | 4 +- .../ScheduledTasks/PluginUpdateTask.cs | 2 +- .../Updates/InstallationManager.cs | 513 --------------------- MediaBrowser.ServerApplication/ApplicationHost.cs | 12 +- .../EntryPoints/WebSocketEvents.cs | 2 +- 15 files changed, 646 insertions(+), 644 deletions(-) create mode 100644 MediaBrowser.Common.Implementations/Updates/InstallationManager.cs create mode 100644 MediaBrowser.Common/Updates/IInstallationManager.cs delete mode 100644 MediaBrowser.Controller/Updates/IInstallationManager.cs delete mode 100644 MediaBrowser.Server.Implementations/Updates/InstallationManager.cs diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index 0e1a377099..de6831d133 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -1,6 +1,6 @@ using MediaBrowser.Common; using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Updates; +using MediaBrowser.Common.Updates; using MediaBrowser.Model.Updates; using ServiceStack.ServiceHost; using System; diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs index 33c09bbfea..6c0face6b3 100644 --- a/MediaBrowser.Api/PluginService.cs +++ b/MediaBrowser.Api/PluginService.cs @@ -1,7 +1,7 @@ using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Security; -using MediaBrowser.Controller.Updates; +using MediaBrowser.Common.Updates; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index a0c1a2f0fe..ef90a856b1 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -148,6 +148,12 @@ namespace MediaBrowser.Common.Implementations /// The configuration manager. protected IConfigurationManager ConfigurationManager { get; private set; } + /// + /// Gets or sets the installation manager. + /// + /// The installation manager. + protected IInstallationManager InstallationManager { get; set; } + /// /// Initializes a new instance of the class. /// @@ -282,6 +288,9 @@ namespace MediaBrowser.Common.Implementations PackageManager = new PackageManager(SecurityManager, NetworkManager, HttpClient, ApplicationPaths, JsonSerializer, Logger); RegisterSingleInstance(PackageManager); + + InstallationManager = new InstallationManager(HttpClient, PackageManager, JsonSerializer, Logger, this); + RegisterSingleInstance(InstallationManager); }); } diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index e2a56e9120..2797c54797 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -85,6 +85,7 @@ + diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs new file mode 100644 index 0000000000..ba2cd7baa0 --- /dev/null +++ b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs @@ -0,0 +1,511 @@ +using MediaBrowser.Common.Events; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Common.Progress; +using MediaBrowser.Common.Updates; +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.Common.Implementations.Updates +{ + /// + /// Manages all install, uninstall and update operations (both plugins and system) + /// + public class InstallationManager : IInstallationManager + { + public event EventHandler> PackageInstalling; + public event EventHandler> PackageInstallationCompleted; + public event EventHandler> PackageInstallationFailed; + public event EventHandler> PackageInstallationCancelled; + + /// + /// The current installations + /// + public List> CurrentInstallations { get; set; } + + /// + /// The completed installations + /// + public ConcurrentBag CompletedInstallations { get; set; } + + #region PluginUninstalled Event + /// + /// Occurs when [plugin uninstalled]. + /// + public event EventHandler> PluginUninstalled; + + /// + /// Called when [plugin uninstalled]. + /// + /// The plugin. + private void OnPluginUninstalled(IPlugin plugin) + { + EventHelper.QueueEventIfNotNull(PluginUninstalled, this, new GenericEventArgs { Argument = plugin }, _logger); + } + #endregion + + #region PluginUpdated Event + /// + /// Occurs when [plugin updated]. + /// + public event EventHandler>> PluginUpdated; + /// + /// Called when [plugin updated]. + /// + /// The plugin. + /// The new version. + private 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> { Argument = new Tuple(plugin, newVersion) }, _logger); + + ApplicationHost.NotifyPendingRestart(); + } + #endregion + + #region PluginInstalled Event + /// + /// Occurs when [plugin updated]. + /// + public event EventHandler> PluginInstalled; + /// + /// Called when [plugin installed]. + /// + /// The package. + private void OnPluginInstalled(PackageVersionInfo package) + { + _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.version, package.classification); + + EventHelper.QueueEventIfNotNull(PluginInstalled, this, new GenericEventArgs { Argument = package }, _logger); + + ApplicationHost.NotifyPendingRestart(); + } + #endregion + + /// + /// The _logger + /// + private readonly ILogger _logger; + + /// + /// The package manager + /// + private readonly IPackageManager _packageManager; + + /// + /// Gets the json serializer. + /// + /// The json serializer. + protected IJsonSerializer JsonSerializer { get; private set; } + + /// + /// Gets the HTTP client. + /// + /// The HTTP client. + protected IHttpClient HttpClient { get; private set; } + + /// + /// Gets the application host. + /// + /// The application host. + protected IApplicationHost ApplicationHost { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The HTTP client. + /// The package manager. + /// The json serializer. + /// The logger. + /// The app host. + /// zipClient + public InstallationManager(IHttpClient httpClient, IPackageManager packageManager, IJsonSerializer jsonSerializer, ILogger logger, IApplicationHost appHost) + { + 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>(); + CompletedInstallations = new ConcurrentBag(); + JsonSerializer = jsonSerializer; + HttpClient = httpClient; + ApplicationHost = appHost; + _packageManager = packageManager; + _logger = logger; + } + + /// + /// Gets all available packages. + /// + /// The cancellation token. + /// Type of the package. + /// The application version. + /// Task{List{PackageInfo}}. + public async Task> GetAvailablePackages(CancellationToken cancellationToken, + PackageType? packageType = null, + Version applicationVersion = null) + { + var packages = (await _packageManager.GetAvailablePackages(cancellationToken).ConfigureAwait(false)).ToList(); + + return FilterPackages(packages, packageType, applicationVersion); + } + + /// + /// Gets all available packages. + /// + /// The cancellation token. + /// Type of the package. + /// The application version. + /// Task{List{PackageInfo}}. + protected async Task> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken, + PackageType? packageType = null, + Version applicationVersion = null) + { + var packages = (await _packageManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false)).ToList(); + return FilterPackages(packages, packageType, applicationVersion); + } + + protected IEnumerable FilterPackages(List packages, PackageType? packageType, Version applicationVersion) + { + 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; + } + + /// + /// Determines whether [is package version up to date] [the specified package version info]. + /// + /// The package version info. + /// The application version. + /// true if [is package version up to date] [the specified package version info]; otherwise, false. + 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; + } + + /// + /// Gets the package. + /// + /// The name. + /// The classification. + /// The version. + /// Task{PackageVersionInfo}. + public async Task 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); + } + + /// + /// Gets the latest compatible version. + /// + /// The name. + /// The classification. + /// Task{PackageVersionInfo}. + public async Task GetLatestCompatibleVersion(string name, PackageVersionClass classification = PackageVersionClass.Release) + { + var packages = await GetAvailablePackages(CancellationToken.None).ConfigureAwait(false); + + return GetLatestCompatibleVersion(packages, name, classification); + } + + /// + /// Gets the latest compatible version. + /// + /// The available packages. + /// The name. + /// The classification. + /// PackageVersionInfo. + public PackageVersionInfo GetLatestCompatibleVersion(IEnumerable 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)); + } + + /// + /// Gets the available plugin updates including registration information for each one. + /// Used with API and catalog. + /// + /// if set to true [with auto update enabled]. + /// The cancellation token. + /// Task{IEnumerable{PackageVersionInfo}}. + public async Task> GetAvailablePluginUpdates(bool withAutoUpdateEnabled, CancellationToken cancellationToken) + { + var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); + return FilterCatalog(catalog, withAutoUpdateEnabled); + } + + /// + /// Gets the available plugin updates from a static resource - no registration information. + /// Used for update checks. + /// + /// if set to true [with auto update enabled]. + /// The cancellation token. + /// Task{IEnumerable{PackageVersionInfo}}. + public async Task> GetAvailablePluginUpdatesWithoutRegistrationInfo(bool withAutoUpdateEnabled, CancellationToken cancellationToken) + { + var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false); + return FilterCatalog(catalog, withAutoUpdateEnabled); + } + + protected IEnumerable FilterCatalog(IEnumerable catalog, bool withAutoUpdateEnabled) + { + + 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 => string.Equals(i.Name, p.name, StringComparison.OrdinalIgnoreCase))) + .Where(p => p != null && !string.IsNullOrWhiteSpace(p.sourceUrl)); + } + + /// + /// Installs the package. + /// + /// The package. + /// The progress. + /// The cancellation token. + /// Task. + /// package + public async Task InstallPackage(PackageVersionInfo package, IProgress 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, innerCancellationTokenSource); + + // Add it to the in-progress list + lock (CurrentInstallations) + { + CurrentInstallations.Add(tuple); + } + + var innerProgress = new ActionableProgress(); + + // 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 { 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 { 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 { Argument = installationInfo }, _logger); + + throw; + } + catch (Exception ex) + { + _logger.ErrorException("Package installation failed", ex); + + lock (CurrentInstallations) + { + CurrentInstallations.Remove(tuple); + } + + EventHelper.QueueEventIfNotNull(PackageInstallationFailed, this, new GenericEventArgs { Argument = installationInfo }, _logger); + + throw; + } + finally + { + // Dispose the progress object and remove the installation from the in-progress list + + innerProgress.Dispose(); + tuple.Item2.Dispose(); + } + } + + /// + /// Installs the package internal. + /// + /// The package. + /// The progress. + /// The cancellation token. + /// Task. + private async Task InstallPackageInternal(PackageVersionInfo package, IProgress 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) + { + OnPluginUpdated(plugin, package); + } + else + { + OnPluginInstalled(package); + } + + } + } + + /// + /// Uninstalls a plugin + /// + /// The plugin. + /// + public void UninstallPlugin(IPlugin plugin) + { + plugin.OnUninstalling(); + + // Remove it the quick way for now + ApplicationHost.RemovePlugin(plugin); + + File.Delete(plugin.AssemblyFilePath); + + OnPluginUninstalled(plugin); + + ApplicationHost.NotifyPendingRestart(); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + lock (CurrentInstallations) + { + foreach (var tuple in CurrentInstallations) + { + tuple.Item2.Dispose(); + } + + CurrentInstallations.Clear(); + } + } + } + + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 9a81537bff..f4acca25da 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -106,6 +106,7 @@ + diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs new file mode 100644 index 0000000000..72b581325c --- /dev/null +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -0,0 +1,114 @@ +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.Common.Updates +{ + public interface IInstallationManager : IDisposable + { + event EventHandler> PackageInstalling; + event EventHandler> PackageInstallationCompleted; + event EventHandler> PackageInstallationFailed; + event EventHandler> PackageInstallationCancelled; + + /// + /// The current installations + /// + List> CurrentInstallations { get; set; } + + /// + /// The completed installations + /// + ConcurrentBag CompletedInstallations { get; set; } + + /// + /// Occurs when [plugin uninstalled]. + /// + event EventHandler> PluginUninstalled; + + /// + /// Occurs when [plugin updated]. + /// + event EventHandler>> PluginUpdated; + + /// + /// Occurs when [plugin updated]. + /// + event EventHandler> PluginInstalled; + + /// + /// Gets all available packages. + /// + /// The cancellation token. + /// Type of the package. + /// The application version. + /// Task{List{PackageInfo}}. + Task> GetAvailablePackages(CancellationToken cancellationToken, + PackageType? packageType = null, + Version applicationVersion = null); + + /// + /// Gets the package. + /// + /// The name. + /// The classification. + /// The version. + /// Task{PackageVersionInfo}. + Task GetPackage(string name, PackageVersionClass classification, Version version); + + /// + /// Gets the latest compatible version. + /// + /// The name. + /// The classification. + /// Task{PackageVersionInfo}. + Task GetLatestCompatibleVersion(string name, PackageVersionClass classification = PackageVersionClass.Release); + + /// + /// Gets the latest compatible version. + /// + /// The available packages. + /// The name. + /// The classification. + /// PackageVersionInfo. + PackageVersionInfo GetLatestCompatibleVersion(IEnumerable availablePackages, string name, PackageVersionClass classification = PackageVersionClass.Release); + + /// + /// Gets the available plugin updates including registration info. + /// + /// if set to true [with auto update enabled]. + /// The cancellation token. + /// Task{IEnumerable{PackageVersionInfo}}. + Task> GetAvailablePluginUpdates(bool withAutoUpdateEnabled, CancellationToken cancellationToken); + + /// + /// Gets the available plugin updates from a static resource (not including registration info). + /// + /// if set to true [with auto update enabled]. + /// The cancellation token. + /// Task{IEnumerable{PackageVersionInfo}}. + Task> GetAvailablePluginUpdatesWithoutRegistrationInfo(bool withAutoUpdateEnabled, CancellationToken cancellationToken); + + /// + /// Installs the package. + /// + /// The package. + /// The progress. + /// The cancellation token. + /// Task. + /// package + Task InstallPackage(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken); + + /// + /// Uninstalls a plugin + /// + /// The plugin. + /// + void UninstallPlugin(IPlugin plugin); + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 77879ed756..eea344a632 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -160,7 +160,6 @@ - diff --git a/MediaBrowser.Controller/Updates/IInstallationManager.cs b/MediaBrowser.Controller/Updates/IInstallationManager.cs deleted file mode 100644 index c7f064279e..0000000000 --- a/MediaBrowser.Controller/Updates/IInstallationManager.cs +++ /dev/null @@ -1,114 +0,0 @@ -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> PackageInstalling; - event EventHandler> PackageInstallationCompleted; - event EventHandler> PackageInstallationFailed; - event EventHandler> PackageInstallationCancelled; - - /// - /// The current installations - /// - List> CurrentInstallations { get; set; } - - /// - /// The completed installations - /// - ConcurrentBag CompletedInstallations { get; set; } - - /// - /// Occurs when [plugin uninstalled]. - /// - event EventHandler> PluginUninstalled; - - /// - /// Occurs when [plugin updated]. - /// - event EventHandler>> PluginUpdated; - - /// - /// Occurs when [plugin updated]. - /// - event EventHandler> PluginInstalled; - - /// - /// Gets all available packages. - /// - /// The cancellation token. - /// Type of the package. - /// The application version. - /// Task{List{PackageInfo}}. - Task> GetAvailablePackages(CancellationToken cancellationToken, - PackageType? packageType = null, - Version applicationVersion = null); - - /// - /// Gets the package. - /// - /// The name. - /// The classification. - /// The version. - /// Task{PackageVersionInfo}. - Task GetPackage(string name, PackageVersionClass classification, Version version); - - /// - /// Gets the latest compatible version. - /// - /// The name. - /// The classification. - /// Task{PackageVersionInfo}. - Task GetLatestCompatibleVersion(string name, PackageVersionClass classification = PackageVersionClass.Release); - - /// - /// Gets the latest compatible version. - /// - /// The available packages. - /// The name. - /// The classification. - /// PackageVersionInfo. - PackageVersionInfo GetLatestCompatibleVersion(IEnumerable availablePackages, string name, PackageVersionClass classification = PackageVersionClass.Release); - - /// - /// Gets the available plugin updates including registration info. - /// - /// if set to true [with auto update enabled]. - /// The cancellation token. - /// Task{IEnumerable{PackageVersionInfo}}. - Task> GetAvailablePluginUpdates(bool withAutoUpdateEnabled, CancellationToken cancellationToken); - - /// - /// Gets the available plugin updates from a static resource (not including registration info). - /// - /// if set to true [with auto update enabled]. - /// The cancellation token. - /// Task{IEnumerable{PackageVersionInfo}}. - Task> GetAvailablePluginUpdatesWithoutRegistrationInfo(bool withAutoUpdateEnabled, CancellationToken cancellationToken); - - /// - /// Installs the package. - /// - /// The package. - /// The progress. - /// The cancellation token. - /// Task. - /// package - Task InstallPackage(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken); - - /// - /// Uninstalls a plugin - /// - /// The plugin. - /// - void UninstallPlugin(IPlugin plugin); - } -} \ No newline at end of file diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index 86a8520e09..5a72fb5bff 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -156,7 +156,7 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(item.OfficialRating) + ""); } - builder.Append("" + SecurityElement.Escape(item.DateCreated.ToString(UsCulture)) + ""); + builder.Append("" + SecurityElement.Escape(item.DateCreated.ToString("G")) + ""); builder.Append("" + item.DontFetchMeta.ToString().ToLower() + ""); diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 041c9db6b8..b2e60a4018 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -184,7 +184,6 @@ - @@ -286,6 +285,9 @@ PreserveNewest + + +