mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Prerequisites - Finalized UI logic surrounding install/remove
This commit is contained in:
1 parent
21700aaad5
commit
77be79dde5
11 files changed
+260
-191
No files matched your search
@@ -7,32 +7,24 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public class ArtemisPluginPrerequisiteException : Exception
|
||||
{
|
||||
internal ArtemisPluginPrerequisiteException(Plugin plugin, PluginPrerequisite? pluginPrerequisite)
|
||||
internal ArtemisPluginPrerequisiteException(IPrerequisitesSubject subject)
|
||||
{
|
||||
Plugin = plugin;
|
||||
PluginPrerequisite = pluginPrerequisite;
|
||||
Subject = subject;
|
||||
}
|
||||
|
||||
internal ArtemisPluginPrerequisiteException(Plugin plugin, PluginPrerequisite? pluginPrerequisite, string message) : base(message)
|
||||
internal ArtemisPluginPrerequisiteException(IPrerequisitesSubject subject, string message) : base(message)
|
||||
{
|
||||
Plugin = plugin;
|
||||
PluginPrerequisite = pluginPrerequisite;
|
||||
Subject = subject;
|
||||
}
|
||||
|
||||
internal ArtemisPluginPrerequisiteException(Plugin plugin, PluginPrerequisite? pluginPrerequisite, string message, Exception inner) : base(message, inner)
|
||||
internal ArtemisPluginPrerequisiteException(IPrerequisitesSubject subject, string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
Plugin = plugin;
|
||||
PluginPrerequisite = pluginPrerequisite;
|
||||
Subject = subject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin the error is related to
|
||||
/// Gets the subject the error is related to
|
||||
/// </summary>
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin prerequisite the error is related to
|
||||
/// </summary>
|
||||
public PluginPrerequisite? PluginPrerequisite { get; }
|
||||
public IPrerequisitesSubject Subject { get; }
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ namespace Artemis.Core
|
||||
Info = info;
|
||||
Directory = directory;
|
||||
Entity = pluginEntity ?? new PluginEntity {Id = Guid, IsEnabled = true};
|
||||
Info.Plugin = this;
|
||||
|
||||
_features = new List<PluginFeatureInfo>();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Core.LayerEffects;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Storage.Entities.Plugins;
|
||||
using Humanizer;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@@ -22,10 +23,11 @@ namespace Artemis.Core
|
||||
private PluginFeature? _instance;
|
||||
private string _name = null!;
|
||||
|
||||
internal PluginFeatureInfo(Plugin plugin, Type featureType, PluginFeatureAttribute? attribute)
|
||||
internal PluginFeatureInfo(Plugin plugin, Type featureType, PluginFeatureEntity pluginFeatureEntity, PluginFeatureAttribute? attribute)
|
||||
{
|
||||
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
|
||||
FeatureType = featureType ?? throw new ArgumentNullException(nameof(featureType));
|
||||
Entity = pluginFeatureEntity;
|
||||
|
||||
Name = attribute?.Name ?? featureType.Name.Humanize(LetterCasing.Title);
|
||||
Description = attribute?.Description;
|
||||
@@ -48,7 +50,7 @@ namespace Artemis.Core
|
||||
else
|
||||
Icon = "Plugin";
|
||||
}
|
||||
|
||||
|
||||
internal PluginFeatureInfo(Plugin plugin, PluginFeatureAttribute? attribute, PluginFeature instance)
|
||||
{
|
||||
if (instance == null) throw new ArgumentNullException(nameof(instance));
|
||||
@@ -121,6 +123,11 @@ namespace Artemis.Core
|
||||
[JsonProperty]
|
||||
public bool AlwaysEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the feature is enabled in persistent storage
|
||||
/// </summary>
|
||||
public bool EnabledInStorage => Entity.IsEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the feature this info is associated with
|
||||
/// </summary>
|
||||
@@ -136,6 +143,8 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public bool ArePrerequisitesMet() => Prerequisites.All(p => p.IsMet());
|
||||
|
||||
internal PluginFeatureEntity Entity { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Humanizer;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a plugin prerequisite action that downloads a file
|
||||
/// </summary>
|
||||
public class DownloadFileAction : PluginPrerequisiteAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of a copy folder action
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action</param>
|
||||
/// <param name="source">The source URL to download</param>
|
||||
/// <param name="target">The target file to save as (will be created if needed)</param>
|
||||
public DownloadFileAction(string name, string source, string target) : base(name)
|
||||
{
|
||||
Source = source ?? throw new ArgumentNullException(nameof(source));
|
||||
Target = target ?? throw new ArgumentNullException(nameof(target));
|
||||
|
||||
ShowProgressBar = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source URL to download
|
||||
/// </summary>
|
||||
public string Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target file to save as (will be created if needed)
|
||||
/// </summary>
|
||||
public string Target { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Execute(CancellationToken cancellationToken)
|
||||
{
|
||||
using HttpClient client = new();
|
||||
await using FileStream destinationStream = File.Create(Target);
|
||||
|
||||
void ProgressOnProgressReported(object? sender, EventArgs e)
|
||||
{
|
||||
if (Progress.ProgressPerSecond != 0)
|
||||
Status = $"Downloading {Target} - {Progress.ProgressPerSecond.Bytes().Humanize("#.##")}/sec";
|
||||
else
|
||||
Status = $"Downloading {Target}";
|
||||
}
|
||||
|
||||
Progress.ProgressReported += ProgressOnProgressReported;
|
||||
|
||||
// Get the http headers first to examine the content length
|
||||
using HttpResponseMessage response = await client.GetAsync(Target, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||
await using Stream download = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
long? contentLength = response.Content.Headers.ContentLength;
|
||||
|
||||
// Ignore progress reporting when no progress reporter was
|
||||
// passed or when the content length is unknown
|
||||
if (!contentLength.HasValue)
|
||||
{
|
||||
ProgressIndeterminate = true;
|
||||
await download.CopyToAsync(destinationStream, Progress, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgressIndeterminate = false;
|
||||
await download.CopyToAsync(contentLength.Value, destinationStream, Progress, cancellationToken);
|
||||
}
|
||||
|
||||
Progress.ProgressReported -= ProgressOnProgressReported;
|
||||
|
||||
Progress.Report((1, 1));
|
||||
Status = "Finished downloading";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,7 +340,12 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
|
||||
foreach (Type featureType in featureTypes)
|
||||
plugin.AddFeature(new PluginFeatureInfo(plugin, featureType, (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute))));
|
||||
{
|
||||
// Load the enabled state and if not found, default to true
|
||||
PluginFeatureEntity featureEntity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ??
|
||||
new PluginFeatureEntity { IsEnabled = plugin.Info.AutoEnableFeatures, Type = featureType.FullName! };
|
||||
plugin.AddFeature(new PluginFeatureInfo(plugin, featureType, featureEntity, (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute))));
|
||||
}
|
||||
|
||||
if (!featureTypes.Any())
|
||||
_logger.Warning("Plugin {plugin} contains no features", plugin);
|
||||
@@ -382,7 +387,7 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
|
||||
if (!plugin.Info.ArePrerequisitesMet())
|
||||
throw new ArtemisPluginPrerequisiteException(plugin, null, "Cannot enable a plugin whose prerequisites aren't all met");
|
||||
throw new ArtemisPluginPrerequisiteException(plugin.Info, "Cannot enable a plugin whose prerequisites aren't all met");
|
||||
|
||||
// Create the Ninject child kernel and load the module
|
||||
plugin.Kernel = new ChildKernel(_kernel, new PluginModule(plugin));
|
||||
@@ -406,10 +411,7 @@ namespace Artemis.Core.Services
|
||||
featureInfo.Instance = instance;
|
||||
instance.Info = featureInfo;
|
||||
instance.Plugin = plugin;
|
||||
|
||||
// Load the enabled state and if not found, default to true
|
||||
instance.Entity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureInfo.FeatureType.FullName) ??
|
||||
new PluginFeatureEntity {IsEnabled = plugin.Info.AutoEnableFeatures, Type = featureInfo.FeatureType.FullName!};
|
||||
instance.Entity = featureInfo.Entity;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -418,17 +420,8 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
|
||||
// Activate features after they are all loaded
|
||||
foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && (f.Instance.Entity.IsEnabled || f.AlwaysEnabled)))
|
||||
{
|
||||
try
|
||||
{
|
||||
EnablePluginFeature(pluginFeature.Instance!, false, !ignorePluginLock);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored, logged in EnablePluginFeature
|
||||
}
|
||||
}
|
||||
foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && (f.EnabledInStorage || f.AlwaysEnabled)))
|
||||
EnablePluginFeature(pluginFeature.Instance!, false, !ignorePluginLock);
|
||||
|
||||
if (saveState)
|
||||
{
|
||||
@@ -585,7 +578,10 @@ namespace Artemis.Core.Services
|
||||
if (pluginFeature.Plugin.Info.RequiresAdmin && !_isElevated)
|
||||
{
|
||||
if (!saveState)
|
||||
{
|
||||
OnPluginFeatureEnableFailed(new PluginFeatureEventArgs(pluginFeature));
|
||||
throw new ArtemisCoreException("Cannot enable a feature that requires elevation without saving it's state.");
|
||||
}
|
||||
|
||||
pluginFeature.Entity.IsEnabled = true;
|
||||
pluginFeature.Plugin.Entity.IsEnabled = true;
|
||||
@@ -596,6 +592,12 @@ namespace Artemis.Core.Services
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pluginFeature.Info.ArePrerequisitesMet())
|
||||
{
|
||||
OnPluginFeatureEnableFailed(new PluginFeatureEventArgs(pluginFeature));
|
||||
throw new ArtemisPluginPrerequisiteException(pluginFeature.Info, "Cannot enable a plugin feature whose prerequisites aren't all met");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
pluginFeature.SetEnabled(true, isAutoEnable);
|
||||
@@ -608,7 +610,6 @@ namespace Artemis.Core.Services
|
||||
new ArtemisPluginException(pluginFeature.Plugin, $"Exception during SetEnabled(true) on {pluginFeature}", e),
|
||||
"Failed to enable plugin"
|
||||
);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user