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