aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Plugins/PluginManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Plugins/PluginManager.cs')
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs74
1 files changed, 69 insertions, 5 deletions
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index 0a7c144ed..c6a7f4546 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -32,6 +32,8 @@ namespace Emby.Server.Implementations.Plugins
/// </summary>
public class PluginManager : IPluginManager
{
+ private const string MetafileName = "meta.json";
+
private readonly string _pluginsPath;
private readonly Version _appVersion;
private readonly List<AssemblyLoadContext> _assemblyLoadContexts;
@@ -374,7 +376,7 @@ namespace Emby.Server.Implementations.Plugins
try
{
var data = JsonSerializer.Serialize(manifest, _jsonOptions);
- File.WriteAllText(Path.Combine(path, "meta.json"), data);
+ File.WriteAllText(Path.Combine(path, MetafileName), data);
return true;
}
catch (ArgumentException e)
@@ -385,7 +387,7 @@ namespace Emby.Server.Implementations.Plugins
}
/// <inheritdoc/>
- public async Task<bool> GenerateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status)
+ public async Task<bool> PopulateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status)
{
var versionInfo = packageInfo.Versions.First(v => v.Version == version.ToString());
var imagePath = string.Empty;
@@ -427,14 +429,76 @@ namespace Emby.Server.Implementations.Plugins
Version = versionInfo.Version,
Status = status == PluginStatus.Disabled ? PluginStatus.Disabled : PluginStatus.Active, // Keep disabled state.
AutoUpdate = true,
- ImagePath = imagePath,
- Assemblies = versionInfo.Assemblies
+ ImagePath = imagePath
};
+ var metafile = Path.Combine(Path.Combine(path, MetafileName));
+ if (File.Exists(metafile))
+ {
+ var data = File.ReadAllBytes(metafile);
+ var localManifest = JsonSerializer.Deserialize<PluginManifest>(data, _jsonOptions) ?? new PluginManifest();
+
+ // Plugin installation is the typical cause for populating a manifest. Activate.
+ localManifest.Status = status == PluginStatus.Disabled ? PluginStatus.Disabled : PluginStatus.Active;
+
+ if (!Equals(localManifest.Id, manifest.Id))
+ {
+ _logger.LogError("The manifest ID {LocalUUID} did not match the package info ID {PackageUUID}.", localManifest.Id, manifest.Id);
+ localManifest.Status = PluginStatus.Malfunctioned;
+ }
+
+ if (localManifest.Version != manifest.Version)
+ {
+ _logger.LogWarning("The version of the local manifest was {LocalVersion}, but {PackageVersion} was expected. The value will be replaced.", localManifest.Version, manifest.Version);
+
+ // Correct the local version.
+ localManifest.Version = manifest.Version;
+ }
+
+ // Reconcile missing data against repository manifest.
+ ReconcileManifest(localManifest, manifest);
+
+ manifest = localManifest;
+ }
+ else
+ {
+ _logger.LogInformation("No local manifest exists for plugin {Plugin}. Populating from repository manifest.", manifest.Name);
+ }
+
return SaveManifest(manifest, path);
}
/// <summary>
+ /// Resolve the target plugin manifest against the source. Values are mapped onto the
+ /// target only if they are default values or empty strings. ID and status fields are ignored.
+ /// </summary>
+ /// <param name="baseManifest">The base <see cref="PluginManifest"/> to be reconciled.</param>
+ /// <param name="projector">The <see cref="PluginManifest"/> to reconcile against.</param>
+ private void ReconcileManifest(PluginManifest baseManifest, PluginManifest projector)
+ {
+ var ignoredFields = new string[]
+ {
+ nameof(baseManifest.Id),
+ nameof(baseManifest.Status)
+ };
+
+ foreach (var property in baseManifest.GetType().GetProperties())
+ {
+ var localValue = property.GetValue(baseManifest);
+
+ if (property.PropertyType == typeof(bool) || ignoredFields.Any(s => Equals(s, property.Name)))
+ {
+ continue;
+ }
+
+ if (property.PropertyType.IsNullOrDefault(localValue) || (property.PropertyType == typeof(string) && (string)localValue! == string.Empty))
+ {
+ property.SetValue(baseManifest, property.GetValue(projector));
+ }
+ }
+ }
+
+ /// <summary>
/// Changes a plugin's load status.
/// </summary>
/// <param name="plugin">The <see cref="LocalPlugin"/> instance.</param>
@@ -598,7 +662,7 @@ namespace Emby.Server.Implementations.Plugins
{
Version? version;
PluginManifest? manifest = null;
- var metafile = Path.Combine(dir, "meta.json");
+ var metafile = Path.Combine(dir, MetafileName);
if (File.Exists(metafile))
{
// Only path where this stays null is when File.ReadAllBytes throws an IOException