1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 13:28:33 +00:00

Profile service - Migrate profiles before importing them

This commit is contained in:
RobertBeekman 2024-02-22 21:23:47 +01:00
parent 39d7d8132f
commit bfd4a436de
5 changed files with 129 additions and 4 deletions

View File

@ -36,6 +36,7 @@ public static class ContainerExtensions
// Bind migrations
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<IProfileMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<ILayoutProvider>(), Reuse.Singleton);
container.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);

View File

@ -0,0 +1,9 @@
using Newtonsoft.Json.Linq;
namespace Artemis.Core.Services;
internal interface IProfileMigration
{
int Version { get; }
void Migrate(JObject profileJson);
}

View File

@ -0,0 +1,90 @@
using Newtonsoft.Json.Linq;
namespace Artemis.Core.Services.ProfileMigrators;
/// <summary>
/// Migrates nodes to be provider-based.
/// This requires giving them a ProviderId and updating the their namespaces to match the namespace of the new plugin.
/// </summary>
internal class M0001NodeProviders : IProfileMigration
{
/// <inheritdoc />
public int Version => 1;
/// <inheritdoc />
public void Migrate(JObject profileJson)
{
JArray? folders = (JArray?) profileJson["Folders"]?["$values"];
JArray? layers = (JArray?) profileJson["Layers"]?["$values"];
if (folders != null)
{
foreach (JToken folder in folders)
{
MigrateProfileElement(folder);
}
}
if (layers != null)
{
foreach (JToken layer in layers)
{
MigrateProfileElement(layer);
MigratePropertyGroup(layer["GeneralPropertyGroup"]);
MigratePropertyGroup(layer["TransformPropertyGroup"]);
MigratePropertyGroup(layer["LayerBrush"]?["PropertyGroup"]);
}
}
}
private void MigrateProfileElement(JToken profileElement)
{
JArray? layerEffects = (JArray?) profileElement["LayerEffects"]?["$values"];
if (layerEffects != null)
{
foreach (JToken layerEffect in layerEffects)
MigratePropertyGroup(layerEffect["PropertyGroup"]);
}
JToken? displayCondition = profileElement["DisplayCondition"];
if (displayCondition != null)
MigrateNodeScript(displayCondition["Script"]);
}
private void MigratePropertyGroup(JToken? propertyGroup)
{
if (propertyGroup == null || !propertyGroup.HasValues)
return;
JArray? properties = (JArray?) propertyGroup["Properties"]?["$values"];
JArray? propertyGroups = (JArray?) propertyGroup["PropertyGroups"]?["$values"];
if (properties != null)
{
foreach (JToken property in properties)
MigrateNodeScript(property["DataBinding"]?["NodeScript"]);
}
if (propertyGroups != null)
{
foreach (JToken childPropertyGroup in propertyGroups)
MigratePropertyGroup(childPropertyGroup);
}
}
private void MigrateNodeScript(JToken? nodeScript)
{
if (nodeScript == null || !nodeScript.HasValues)
return;
JArray? nodes = (JArray?) nodeScript["Nodes"]?["$values"];
if (nodes == null)
return;
foreach (JToken node in nodes)
{
node["Type"] = node["Type"]?.Value<string>()?.Replace("Artemis.VisualScripting.Nodes", "Artemis.Plugins.Nodes.General.Nodes");
node["ProviderId"] = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78";
}
}
}

View File

@ -10,6 +10,7 @@ using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serilog;
using SkiaSharp;
@ -24,9 +25,10 @@ internal class ProfileService : IProfileService
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
private readonly List<ProfileCategory> _profileCategories;
private readonly IProfileRepository _profileRepository;
private readonly List<IProfileMigration> _profileMigrators;
private readonly List<Exception> _renderExceptions = new();
private readonly List<Exception> _updateExceptions = new();
private DateTime _lastRenderExceptionLog;
private DateTime _lastUpdateExceptionLog;
@ -35,13 +37,15 @@ internal class ProfileService : IProfileService
IPluginManagementService pluginManagementService,
IInputService inputService,
IDeviceService deviceService,
IProfileRepository profileRepository)
IProfileRepository profileRepository,
List<IProfileMigration> profileMigrators)
{
_logger = logger;
_profileCategoryRepository = profileCategoryRepository;
_pluginManagementService = pluginManagementService;
_deviceService = deviceService;
_profileRepository = profileRepository;
_profileMigrators = profileMigrators;
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
_deviceService.LedsChanged += DeviceServiceOnLedsChanged;
@ -58,7 +62,7 @@ internal class ProfileService : IProfileService
public ProfileConfiguration? FocusProfile { get; set; }
public ProfileElement? FocusProfileElement { get; set; }
public bool UpdateFocusProfile { get; set; }
public bool ProfileRenderingDisabled { get; set; }
/// <inheritdoc />
@ -461,7 +465,12 @@ internal class ProfileService : IProfileService
await using Stream profileStream = profileEntry.Open();
using StreamReader profileReader = new(profileStream);
ProfileEntity? profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(await profileReader.ReadToEndAsync(), IProfileService.ExportSettings);
JObject? profileJson = JsonConvert.DeserializeObject<JObject>(await profileReader.ReadToEndAsync(), IProfileService.ExportSettings);
// Before deserializing, apply any pending migrations
MigrateProfile(configurationEntity, profileJson);
ProfileEntity? profileEntity = profileJson?.ToObject<ProfileEntity>(JsonSerializer.Create(IProfileService.ExportSettings));
if (profileEntity == null)
throw new ArtemisCoreException("Could not import profile, failed to deserialize profile.json");
@ -545,6 +554,21 @@ internal class ProfileService : IProfileService
}
}
private void MigrateProfile(ProfileConfigurationEntity configurationEntity, JObject? profileJson)
{
if (profileJson == null)
return;
foreach (IProfileMigration profileMigrator in _profileMigrators.OrderBy(m => m.Version))
{
if (profileMigrator.Version <= configurationEntity.Version)
continue;
profileMigrator.Migrate(profileJson);
configurationEntity.Version = profileMigrator.Version;
}
}
/// <summary>
/// Populates all missing LEDs on all currently active profiles
/// </summary>

View File

@ -26,4 +26,5 @@ public class ProfileConfigurationEntity
public Guid ProfileId { get; set; }
public bool FadeInAndOut { get; set; }
public int Version { get; set; }
}