Update all on-disk plugins

This commit is contained in:
crobibero 2020-09-30 17:37:30 -06:00
parent c7b3d4a90c
commit 53d8023def
4 changed files with 162 additions and 65 deletions

View File

@ -4,7 +4,6 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -30,7 +29,6 @@ using Emby.Server.Implementations.Cryptography;
using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Data;
using Emby.Server.Implementations.Devices; using Emby.Server.Implementations.Devices;
using Emby.Server.Implementations.Dto; using Emby.Server.Implementations.Dto;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.Security; using Emby.Server.Implementations.HttpServer.Security;
using Emby.Server.Implementations.IO; using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Library;
@ -1026,62 +1024,36 @@ namespace Emby.Server.Implementations
protected abstract void RestartInternal(); protected abstract void RestartInternal();
/// <summary> /// <inheritdoc/>
/// Comparison function used in <see cref="GetPlugins" />. public IEnumerable<LocalPlugin> GetLocalPlugins(string path, bool cleanup = true)
/// </summary>
/// <param name="a">Item to compare.</param>
/// <param name="b">Item to compare with.</param>
/// <returns>Boolean result of the operation.</returns>
private static int VersionCompare(
(Version PluginVersion, string Name, string Path) a,
(Version PluginVersion, string Name, string Path) b)
{ {
int compare = string.Compare(a.Name, b.Name, true, CultureInfo.InvariantCulture); var minimumVersion = new Version(0, 0, 0, 1);
var versions = new List<LocalPlugin>();
if (compare == 0)
{
return a.PluginVersion.CompareTo(b.PluginVersion);
}
return compare;
}
/// <summary>
/// Returns a list of plugins to install.
/// </summary>
/// <param name="path">Path to check.</param>
/// <param name="cleanup">True if an attempt should be made to delete old plugs.</param>
/// <returns>Enumerable list of dlls to load.</returns>
private IEnumerable<string> GetPlugins(string path, bool cleanup = true)
{
var dllList = new List<string>();
var versions = new List<(Version PluginVersion, string Name, string Path)>();
var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly); var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly);
string metafile;
foreach (var dir in directories) foreach (var dir in directories)
{ {
try try
{ {
metafile = Path.Combine(dir, "meta.json"); var metafile = Path.Combine(dir, "meta.json");
if (File.Exists(metafile)) if (File.Exists(metafile))
{ {
var manifest = _jsonSerializer.DeserializeFromFile<PluginManifest>(metafile); var manifest = _jsonSerializer.DeserializeFromFile<PluginManifest>(metafile);
if (!Version.TryParse(manifest.TargetAbi, out var targetAbi)) if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
{ {
targetAbi = new Version(0, 0, 0, 1); targetAbi = minimumVersion;
} }
if (!Version.TryParse(manifest.Version, out var version)) if (!Version.TryParse(manifest.Version, out var version))
{ {
version = new Version(0, 0, 0, 1); version = minimumVersion;
} }
if (ApplicationVersion >= targetAbi) if (ApplicationVersion >= targetAbi)
{ {
// Only load Plugins if the plugin is built for this version or below. // Only load Plugins if the plugin is built for this version or below.
versions.Add((version, manifest.Name, dir)); versions.Add(new LocalPlugin(manifest.Guid, manifest.Name, version, dir));
} }
} }
else else
@ -1090,15 +1062,15 @@ namespace Emby.Server.Implementations
metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1]; metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1];
int versionIndex = dir.LastIndexOf('_'); int versionIndex = dir.LastIndexOf('_');
if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver)) if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version parsedVersion))
{ {
// Versioned folder. // Versioned folder.
versions.Add((ver, metafile, dir)); versions.Add(new LocalPlugin(Guid.Empty, metafile, parsedVersion, dir));
} }
else else
{ {
// Un-versioned folder - Add it under the path name and version 0.0.0.1. // Un-versioned folder - Add it under the path name and version 0.0.0.1.
versions.Add((new Version(0, 0, 0, 1), metafile, dir)); versions.Add(new LocalPlugin(Guid.Empty, metafile, minimumVersion, dir));
} }
} }
} }
@ -1109,14 +1081,14 @@ namespace Emby.Server.Implementations
} }
string lastName = string.Empty; string lastName = string.Empty;
versions.Sort(VersionCompare); versions.Sort(LocalPlugin.Compare);
// Traverse backwards through the list. // Traverse backwards through the list.
// The first item will be the latest version. // The first item will be the latest version.
for (int x = versions.Count - 1; x >= 0; x--) for (int x = versions.Count - 1; x >= 0; x--)
{ {
if (!string.Equals(lastName, versions[x].Name, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(lastName, versions[x].Name, StringComparison.OrdinalIgnoreCase))
{ {
dllList.AddRange(Directory.EnumerateFiles(versions[x].Path, "*.dll", SearchOption.AllDirectories)); versions[x].DllFiles.AddRange(Directory.EnumerateFiles(versions[x].Path, "*.dll", SearchOption.AllDirectories));
lastName = versions[x].Name; lastName = versions[x].Name;
continue; continue;
} }
@ -1124,6 +1096,7 @@ namespace Emby.Server.Implementations
if (!string.IsNullOrEmpty(lastName) && cleanup) if (!string.IsNullOrEmpty(lastName) && cleanup)
{ {
// Attempt a cleanup of old folders. // Attempt a cleanup of old folders.
versions.RemoveAt(x);
try try
{ {
Logger.LogDebug("Deleting {Path}", versions[x].Path); Logger.LogDebug("Deleting {Path}", versions[x].Path);
@ -1136,7 +1109,7 @@ namespace Emby.Server.Implementations
} }
} }
return dllList; return versions;
} }
/// <summary> /// <summary>
@ -1147,21 +1120,24 @@ namespace Emby.Server.Implementations
{ {
if (Directory.Exists(ApplicationPaths.PluginsPath)) if (Directory.Exists(ApplicationPaths.PluginsPath))
{ {
foreach (var file in GetPlugins(ApplicationPaths.PluginsPath)) foreach (var plugin in GetLocalPlugins(ApplicationPaths.PluginsPath))
{ {
Assembly plugAss; foreach (var file in plugin.DllFiles)
try
{ {
plugAss = Assembly.LoadFrom(file); Assembly plugAss;
} try
catch (FileLoadException ex) {
{ plugAss = Assembly.LoadFrom(file);
Logger.LogError(ex, "Failed to load assembly {Path}", file); }
continue; catch (FileLoadException ex)
} {
Logger.LogError(ex, "Failed to load assembly {Path}", file);
continue;
}
Logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file); Logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file);
yield return plugAss; yield return plugAss;
}
} }
} }

View File

@ -15,14 +15,13 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
using MediaBrowser.Common.System; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates; using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MediaBrowser.Model.System;
namespace Emby.Server.Implementations.Updates namespace Emby.Server.Implementations.Updates
{ {
@ -45,7 +44,7 @@ namespace Emby.Server.Implementations.Updates
/// Gets the application host. /// Gets the application host.
/// </summary> /// </summary>
/// <value>The application host.</value> /// <value>The application host.</value>
private readonly IApplicationHost _applicationHost; private readonly IServerApplicationHost _applicationHost;
private readonly IZipClient _zipClient; private readonly IZipClient _zipClient;
@ -63,7 +62,7 @@ namespace Emby.Server.Implementations.Updates
public InstallationManager( public InstallationManager(
ILogger<InstallationManager> logger, ILogger<InstallationManager> logger,
IApplicationHost appHost, IServerApplicationHost appHost,
IApplicationPaths appPaths, IApplicationPaths appPaths,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
@ -237,7 +236,8 @@ namespace Emby.Server.Implementations.Updates
private IEnumerable<InstallationInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog) private IEnumerable<InstallationInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
{ {
foreach (var plugin in _applicationHost.Plugins) var plugins = _applicationHost.GetLocalPlugins(_appPaths.PluginsPath);
foreach (var plugin in plugins)
{ {
var compatibleVersions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, minVersion: plugin.Version); var compatibleVersions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, minVersion: plugin.Version);
var version = compatibleVersions.FirstOrDefault(y => y.Version > plugin.Version); var version = compatibleVersions.FirstOrDefault(y => y.Version > plugin.Version);

View File

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Globalization;
namespace MediaBrowser.Common.Plugins
{
/// <summary>
/// Local plugin struct.
/// </summary>
public readonly struct LocalPlugin : IEquatable<LocalPlugin>
{
/// <summary>
/// Initializes a new instance of the <see cref="LocalPlugin"/> struct.
/// </summary>
/// <param name="id">The plugin id.</param>
/// <param name="name">The plugin name.</param>
/// <param name="version">The plugin version.</param>
/// <param name="path">The plugin path.</param>
public LocalPlugin(Guid id, string name, Version version, string path)
{
Id = id;
Name = name;
Version = version;
Path = path;
DllFiles = new List<string>();
}
/// <summary>
/// Gets the plugin id.
/// </summary>
public Guid Id { get; }
/// <summary>
/// Gets the plugin name.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the plugin version.
/// </summary>
public Version Version { get; }
/// <summary>
/// Gets the plugin path.
/// </summary>
public string Path { get; }
/// <summary>
/// Gets the list of dll files for this plugin.
/// </summary>
public List<string> DllFiles { get; }
/// <summary>
/// == operator.
/// </summary>
/// <param name="left">Left item.</param>
/// <param name="right">Right item.</param>
/// <returns>Comparison result.</returns>
public static bool operator ==(LocalPlugin left, LocalPlugin right)
{
return left.Equals(right);
}
/// <summary>
/// != operator.
/// </summary>
/// <param name="left">Left item.</param>
/// <param name="right">Right item.</param>
/// <returns>Comparison result.</returns>
public static bool operator !=(LocalPlugin left, LocalPlugin right)
{
return !(left == right);
}
/// <summary>
/// Compare two <see cref="LocalPlugin"/>.
/// </summary>
/// <param name="a">The first item.</param>
/// <param name="b">The second item.</param>
/// <returns>Comparison result.</returns>
public static int Compare(LocalPlugin a, LocalPlugin b)
{
var compare = string.Compare(a.Name, b.Name, true, CultureInfo.InvariantCulture);
// Id is not equal but name is.
if (a.Id != b.Id && compare == 0)
{
compare = a.Id.CompareTo(b.Id);
}
return compare == 0 ? a.Version.CompareTo(b.Version) : compare;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is LocalPlugin other && this.Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
return Name.GetHashCode(StringComparison.OrdinalIgnoreCase);
}
/// <inheritdoc />
public bool Equals(LocalPlugin other)
{
return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase)
&& Id.Equals(other.Id);
}
}
}

View File

@ -6,8 +6,8 @@ using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller namespace MediaBrowser.Controller
{ {
@ -119,5 +119,13 @@ namespace MediaBrowser.Controller
string ExpandVirtualPath(string path); string ExpandVirtualPath(string path);
string ReverseVirtualPath(string path); string ReverseVirtualPath(string path);
/// <summary>
/// Gets the list of local plugins.
/// </summary>
/// <param name="path">Plugin base directory.</param>
/// <param name="cleanup">Cleanup old plugins.</param>
/// <returns>Enumerable of local plugins.</returns>
IEnumerable<LocalPlugin> GetLocalPlugins(string path, bool cleanup = true);
} }
} }