|  | @@ -3,6 +3,7 @@ using System.Collections.Concurrent;
 | 
	
		
			
				|  |  |  using System.Collections.Generic;
 | 
	
		
			
				|  |  |  using System.IO;
 | 
	
		
			
				|  |  |  using System.Linq;
 | 
	
		
			
				|  |  | +using System.Net.Http;
 | 
	
		
			
				|  |  |  using System.Threading;
 | 
	
		
			
				|  |  |  using System.Threading.Tasks;
 | 
	
		
			
				|  |  |  using MediaBrowser.Common;
 | 
	
	
		
			
				|  | @@ -33,7 +34,7 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// The current installations
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        public List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; }
 | 
	
		
			
				|  |  | +        private List<(InstallationInfo info, CancellationTokenSource token)> _currentInstallations { get; set; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// The completed installations
 | 
	
	
		
			
				|  | @@ -47,49 +48,15 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Called when [plugin uninstalled].
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="plugin">The plugin.</param>
 | 
	
		
			
				|  |  | -        private void OnPluginUninstalled(IPlugin plugin)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            PluginUninstalled?.Invoke(this, new GenericEventArgs<IPlugin> { Argument = plugin });
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /// <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>
 | 
	
		
			
				|  |  | -        private void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            _logger.LogInformation("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.versionStr ?? string.Empty, newVersion.classification);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            PluginUpdated?.Invoke(this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            _applicationHost.NotifyPendingRestart();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Occurs when [plugin updated].
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Called when [plugin installed].
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="package">The package.</param>
 | 
	
		
			
				|  |  | -        private void OnPluginInstalled(PackageVersionInfo package)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo> { Argument = package });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            _applicationHost.NotifyPendingRestart();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// The _logger
 | 
	
	
		
			
				|  | @@ -111,7 +78,7 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |          private readonly IZipClient _zipClient;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public InstallationManager(
 | 
	
		
			
				|  |  | -            ILoggerFactory loggerFactory,
 | 
	
		
			
				|  |  | +            ILogger<InstallationManager> logger,
 | 
	
		
			
				|  |  |              IApplicationHost appHost,
 | 
	
		
			
				|  |  |              IApplicationPaths appPaths,
 | 
	
		
			
				|  |  |              IHttpClient httpClient,
 | 
	
	
		
			
				|  | @@ -120,15 +87,15 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |              IFileSystem fileSystem,
 | 
	
		
			
				|  |  |              IZipClient zipClient)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            if (loggerFactory == null)
 | 
	
		
			
				|  |  | +            if (logger == null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                throw new ArgumentNullException(nameof(loggerFactory));
 | 
	
		
			
				|  |  | +                throw new ArgumentNullException(nameof(logger));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            CurrentInstallations = new List<Tuple<InstallationInfo, CancellationTokenSource>>();
 | 
	
		
			
				|  |  | +            _currentInstallations = new List<(InstallationInfo, CancellationTokenSource)>();
 | 
	
		
			
				|  |  |              _completedInstallationsInternal = new ConcurrentBag<InstallationInfo>();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            _logger = loggerFactory.CreateLogger(nameof(InstallationManager));
 | 
	
		
			
				|  |  | +            _logger = logger;
 | 
	
		
			
				|  |  |              _applicationHost = appHost;
 | 
	
		
			
				|  |  |              _appPaths = appPaths;
 | 
	
		
			
				|  |  |              _httpClient = httpClient;
 | 
	
	
		
			
				|  | @@ -138,21 +105,12 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |              _zipClient = zipClient;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private static Version GetPackageVersion(PackageVersionInfo version)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            return new Version(ValueOrDefault(version.versionStr, "0.0.0.1"));
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static string ValueOrDefault(string str, string def)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            return string.IsNullOrEmpty(str) ? def : str;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets all available packages.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          /// <returns>Task{List{PackageInfo}}.</returns>
 | 
	
		
			
				|  |  | -        public async Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
 | 
	
		
			
				|  |  | +        public async Task<List<PackageInfo>> GetAvailablePackages(
 | 
	
		
			
				|  |  | +            CancellationToken cancellationToken,
 | 
	
		
			
				|  |  |              bool withRegistration = true,
 | 
	
		
			
				|  |  |              string packageType = null,
 | 
	
		
			
				|  |  |              Version applicationVersion = null)
 | 
	
	
		
			
				|  | @@ -172,22 +130,14 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
 | 
	
		
			
				|  |  |                  CancellationToken = cancellationToken,
 | 
	
		
			
				|  |  | -                Progress = new SimpleProgress<double>(),
 | 
	
		
			
				|  |  |                  CacheLength = GetCacheLength()
 | 
	
		
			
				|  |  | -            }, "GET").ConfigureAwait(false))
 | 
	
		
			
				|  |  | +            }, HttpMethod.Get).ConfigureAwait(false))
 | 
	
		
			
				|  |  | +            using (var stream = response.Content)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                using (var stream = response.Content)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private PackageVersionClass GetSystemUpdateLevel()
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            return _applicationHost.SystemUpdateLevel;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          private static TimeSpan GetCacheLength()
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              return TimeSpan.FromMinutes(3);
 | 
	
	
		
			
				|  | @@ -211,7 +161,7 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  package.versions = versions
 | 
	
		
			
				|  |  | -                    .OrderByDescending(GetPackageVersion)
 | 
	
		
			
				|  |  | +                    .OrderByDescending(x => x.Version)
 | 
	
		
			
				|  |  |                      .ToArray();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  if (package.versions.Length == 0)
 | 
	
	
		
			
				|  | @@ -294,7 +244,7 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |                  return null;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return package.versions.FirstOrDefault(v => GetPackageVersion(v).Equals(version) && v.classification == classification);
 | 
	
		
			
				|  |  | +            return package.versions.FirstOrDefault(v => v.Version == version && v.classification == classification);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -331,7 +281,7 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              return package.versions
 | 
	
		
			
				|  |  | -                .OrderByDescending(GetPackageVersion)
 | 
	
		
			
				|  |  | +                .OrderByDescending(x => x.Version)
 | 
	
		
			
				|  |  |                  .FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -346,14 +296,14 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var systemUpdateLevel = GetSystemUpdateLevel();
 | 
	
		
			
				|  |  | +            var systemUpdateLevel = _applicationHost.SystemUpdateLevel;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // Figure out what needs to be installed
 | 
	
		
			
				|  |  |              return _applicationHost.Plugins.Select(p =>
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
 | 
	
		
			
				|  |  | +                return latestPluginInfo != null && latestPluginInfo.Version > p.Version ? latestPluginInfo : null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              }).Where(i => i != null)
 | 
	
		
			
				|  |  |              .Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
 | 
	
	
		
			
				|  | @@ -368,7 +318,7 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |          /// <param name="cancellationToken">The cancellation token.</param>
 | 
	
		
			
				|  |  |          /// <returns>Task.</returns>
 | 
	
		
			
				|  |  |          /// <exception cref="ArgumentNullException">package</exception>
 | 
	
		
			
				|  |  | -        public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
 | 
	
		
			
				|  |  | +        public async Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (package == null)
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -391,12 +341,12 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var innerCancellationTokenSource = new CancellationTokenSource();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var tuple = new Tuple<InstallationInfo, CancellationTokenSource>(installationInfo, innerCancellationTokenSource);
 | 
	
		
			
				|  |  | +            var tuple = (installationInfo, innerCancellationTokenSource);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // Add it to the in-progress list
 | 
	
		
			
				|  |  | -            lock (CurrentInstallations)
 | 
	
		
			
				|  |  | +            lock (_currentInstallations)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                CurrentInstallations.Add(tuple);
 | 
	
		
			
				|  |  | +                _currentInstallations.Add(tuple);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var innerProgress = new ActionableProgress<double>();
 | 
	
	
		
			
				|  | @@ -421,11 +371,11 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              try
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                lock (CurrentInstallations)
 | 
	
		
			
				|  |  | +                lock (_currentInstallations)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    CurrentInstallations.Remove(tuple);
 | 
	
		
			
				|  |  | +                    _currentInstallations.Remove(tuple);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  _completedInstallationsInternal.Add(installationInfo);
 | 
	
	
		
			
				|  | @@ -434,9 +384,9 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              catch (OperationCanceledException)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                lock (CurrentInstallations)
 | 
	
		
			
				|  |  | +                lock (_currentInstallations)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    CurrentInstallations.Remove(tuple);
 | 
	
		
			
				|  |  | +                    _currentInstallations.Remove(tuple);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr);
 | 
	
	
		
			
				|  | @@ -449,9 +399,9 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  _logger.LogError(ex, "Package installation failed");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                lock (CurrentInstallations)
 | 
	
		
			
				|  |  | +                lock (_currentInstallations)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    CurrentInstallations.Remove(tuple);
 | 
	
		
			
				|  |  | +                    _currentInstallations.Remove(tuple);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  PackageInstallationFailed?.Invoke(this, new InstallationFailedEventArgs
 | 
	
	
		
			
				|  | @@ -477,16 +427,12 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |          /// <param name="progress">The progress.</param>
 | 
	
		
			
				|  |  |          /// <param name="cancellationToken">The cancellation token.</param>
 | 
	
		
			
				|  |  |          /// <returns>Task.</returns>
 | 
	
		
			
				|  |  | -        private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
 | 
	
		
			
				|  |  | +        private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            IPlugin plugin = null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (isPlugin)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                // Set last update time if we were installed before
 | 
	
		
			
				|  |  | -                plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
 | 
	
		
			
				|  |  | +            // Set last update time if we were installed before
 | 
	
		
			
				|  |  | +            IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
 | 
	
		
			
				|  |  |                             ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              string targetPath = plugin == null ? null : plugin.AssemblyFilePath;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -494,17 +440,20 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |              await PerformPackageInstallation(progress, targetPath, package, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // Do plugin-specific processing
 | 
	
		
			
				|  |  | -            if (isPlugin)
 | 
	
		
			
				|  |  | +            if (plugin == null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                if (plugin == null)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    OnPluginInstalled(package);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                else
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    OnPluginUpdated(plugin, package);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo>(package));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package)));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            _applicationHost.NotifyPendingRestart();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
 | 
	
	
		
			
				|  | @@ -622,11 +571,34 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |                  _config.SaveConfiguration();
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            OnPluginUninstalled(plugin);
 | 
	
		
			
				|  |  | +            PluginUninstalled?.Invoke(this, new GenericEventArgs<IPlugin> { Argument = plugin });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              _applicationHost.NotifyPendingRestart();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc/>
 | 
	
		
			
				|  |  | +        public bool CancelInstallation(Guid id)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            lock (_currentInstallations)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var install = _currentInstallations.Find(x => x.Item1.Id == id);
 | 
	
		
			
				|  |  | +                if (install == default((InstallationInfo, CancellationTokenSource)))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    return false;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                install.Item2.Cancel();
 | 
	
		
			
				|  |  | +                _currentInstallations.Remove(install);
 | 
	
		
			
				|  |  | +                return true;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public void Dispose()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            Dispose(true);
 | 
	
		
			
				|  |  | +            GC.SuppressFinalize(this);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Releases unmanaged and - optionally - managed resources.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
	
		
			
				|  | @@ -635,21 +607,16 @@ namespace Emby.Server.Implementations.Updates
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (dispose)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                lock (CurrentInstallations)
 | 
	
		
			
				|  |  | +                lock (_currentInstallations)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    foreach (var tuple in CurrentInstallations)
 | 
	
		
			
				|  |  | +                    foreach (var tuple in _currentInstallations)
 | 
	
		
			
				|  |  |                      {
 | 
	
		
			
				|  |  |                          tuple.Item2.Dispose();
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    CurrentInstallations.Clear();
 | 
	
		
			
				|  |  | +                    _currentInstallations.Clear();
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public void Dispose()
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            Dispose(true);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 |