diff --git a/src/ZonyLrcTools.Common/Configuration/TagInfoOptions.cs b/src/ZonyLrcTools.Common/Configuration/TagInfoOptions.cs index e2600ae..c211003 100644 --- a/src/ZonyLrcTools.Common/Configuration/TagInfoOptions.cs +++ b/src/ZonyLrcTools.Common/Configuration/TagInfoOptions.cs @@ -8,4 +8,9 @@ public class TagInfoOptions /// 屏蔽词功能相关配置。 /// public BlockWordOptions BlockWord { get; set; } = null!; + + public TagInfoProviderOptions GetTagProviderOption(string name) + { + return Plugin.FirstOrDefault(x => x.Name == name)!; + } } \ No newline at end of file diff --git a/src/ZonyLrcTools.Desktop/App.axaml.cs b/src/ZonyLrcTools.Desktop/App.axaml.cs index 4d81481..b555928 100644 --- a/src/ZonyLrcTools.Desktop/App.axaml.cs +++ b/src/ZonyLrcTools.Desktop/App.axaml.cs @@ -1,8 +1,14 @@ using System; +using System.IO; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using YamlDotNet.Core; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; +using ZonyLrcTools.Common.Configuration; using ZonyLrcTools.Common.Infrastructure.DependencyInject; using ZonyLrcTools.Common.Infrastructure.Network; using ZonyLrcTools.Desktop.ViewModels; @@ -15,6 +21,13 @@ public class App : Application public new static App Current => (App)Application.Current!; public IServiceProvider Services { get; } = ConfigureServices(); + public IOptions GlobalOptions => Services.GetRequiredService>(); + + private Lazy _yamlSerializer = new(() => new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .WithDefaultScalarStyle(ScalarStyle.DoubleQuoted) + .Build()); + private static IServiceProvider ConfigureServices() { var services = new ServiceCollection(); @@ -44,4 +57,11 @@ public class App : Application base.OnFrameworkInitializationCompleted(); } + + public void UpdateConfiguration() + { + var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.yaml"); + var yamlString = _yamlSerializer.Value.Serialize(GlobalOptions.Value); + File.WriteAllText(configPath, yamlString); + } } \ No newline at end of file diff --git a/src/ZonyLrcTools.Desktop/ViewModels/Settings/BlockWordViewModel.cs b/src/ZonyLrcTools.Desktop/ViewModels/Settings/BlockWordViewModel.cs index 0fb5215..200dfa8 100644 --- a/src/ZonyLrcTools.Desktop/ViewModels/Settings/BlockWordViewModel.cs +++ b/src/ZonyLrcTools.Desktop/ViewModels/Settings/BlockWordViewModel.cs @@ -1,5 +1,3 @@ -using System; -using ReactiveUI; using ReactiveUI.Fody.Helpers; using ZonyLrcTools.Common.Configuration; @@ -12,12 +10,8 @@ public class BlockWordViewModel : ViewModelBase IsEnable = options.Provider.Tag.BlockWord.IsEnable; FilePath = options.Provider.Tag.BlockWord.FilePath; - this.WhenAnyValue(x => x.IsEnable, x => x.FilePath) - .Subscribe(_ => - { - options.Provider.Tag.BlockWord.IsEnable = IsEnable; - options.Provider.Tag.BlockWord.FilePath = FilePath; - }); + SubscribeToProperty(this, x => x.IsEnable, x => options.Provider.Tag.BlockWord.IsEnable = x); + SubscribeToProperty(this, x => x.FilePath, x => options.Provider.Tag.BlockWord.FilePath = x!); } [Reactive] public bool IsEnable { get; set; } diff --git a/src/ZonyLrcTools.Desktop/ViewModels/Settings/GlobalConfigurationViewModel.cs b/src/ZonyLrcTools.Desktop/ViewModels/Settings/GlobalConfigurationViewModel.cs index ce30281..1020fc8 100644 --- a/src/ZonyLrcTools.Desktop/ViewModels/Settings/GlobalConfigurationViewModel.cs +++ b/src/ZonyLrcTools.Desktop/ViewModels/Settings/GlobalConfigurationViewModel.cs @@ -21,14 +21,10 @@ public class GlobalConfigurationViewModel : ViewModelBase IsSkipExistLyricFiles = globalConfig.IsSkipExistLyricFiles; IsOnlyOutputTranslation = globalConfig.IsOnlyOutputTranslation; - this.WhenAnyValue(x => x.IsOneLine) - .Subscribe(x => globalConfig.IsOneLine = x); - this.WhenAnyValue(x => x.IsEnableTranslation) - .Subscribe(x => globalConfig.IsEnableTranslation = x); - this.WhenAnyValue(x => x.IsSkipExistLyricFiles) - .Subscribe(x => globalConfig.IsSkipExistLyricFiles = x); - this.WhenAnyValue(x => x.IsOnlyOutputTranslation) - .Subscribe(x => globalConfig.IsOnlyOutputTranslation = x); + SubscribeToProperty(this, x => x.IsOneLine, x => globalConfig.IsOneLine = x); + SubscribeToProperty(this, x => x.IsEnableTranslation, x => globalConfig.IsEnableTranslation = x); + SubscribeToProperty(this, x => x.IsSkipExistLyricFiles, x => globalConfig.IsSkipExistLyricFiles = x); + SubscribeToProperty(this, x => x.IsOnlyOutputTranslation, x => globalConfig.IsOnlyOutputTranslation = x); } [Reactive] public bool IsOneLine { get; set; } diff --git a/src/ZonyLrcTools.Desktop/ViewModels/Settings/LyricsProviderViewModel.cs b/src/ZonyLrcTools.Desktop/ViewModels/Settings/LyricsProviderViewModel.cs index 1e2176f..27824e4 100644 --- a/src/ZonyLrcTools.Desktop/ViewModels/Settings/LyricsProviderViewModel.cs +++ b/src/ZonyLrcTools.Desktop/ViewModels/Settings/LyricsProviderViewModel.cs @@ -4,7 +4,6 @@ using Avalonia.Data; using Avalonia.Data.Converters; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using ReactiveUI; using ReactiveUI.Fody.Helpers; using ZonyLrcTools.Common.Configuration; @@ -20,10 +19,8 @@ public class LyricsProviderViewModel : ViewModelBase Priority = options.Priority; Depth = options.Depth; - this.WhenAnyValue(x => x.Priority) - .Subscribe(priority => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Priority = priority); - this.WhenAnyValue(x => x.Depth) - .Subscribe(depth => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Depth = depth); + SubscribeToProperty(this, x => x.Priority, x => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Priority = x); + SubscribeToProperty(this, x => x.Depth, x => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Depth = x); } public string Name { get; set; } diff --git a/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoProviderViewModel.cs b/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoProviderViewModel.cs index 19dcbf5..5bf9177 100644 --- a/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoProviderViewModel.cs +++ b/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoProviderViewModel.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using ReactiveUI.Fody.Helpers; using ZonyLrcTools.Common.Configuration; @@ -9,14 +11,32 @@ public class TagInfoProviderViewModel : ViewModelBase { public TagInfoProviderViewModel(TagInfoProviderOptions options) { + var globalOptions = App.Current.Services.GetRequiredService>().Value; + Name = options.Name; Priority = options.Priority; - Extensions = new ObservableCollection>(options.Extensions ?? new Dictionary()); + Extensions = options.Extensions == null ? [] : new ObservableCollection(options.Extensions.Select(x => new ObservableKeyValuePair(x.Key, x.Value))); + + SubscribeToProperty(this, x => x.Priority, x => globalOptions.Provider.Tag.GetTagProviderOption(Name).Priority = x); + SubscribeToProperty(this, x => x.Extensions, x => globalOptions.Provider.Tag.GetTagProviderOption(Name).Extensions = x.ToDictionary(pair => pair.Key, pair => pair.Value)); } public string? Name { get; set; } [Reactive] public int Priority { get; set; } - public ObservableCollection> Extensions { get; } + [Reactive] public ObservableCollection Extensions { get; set; } +} + +public class ObservableKeyValuePair : ViewModelBase +{ + [Reactive] public string Key { get; set; } + + [Reactive] public string Value { get; set; } + + public ObservableKeyValuePair(string key, string value) + { + Key = key; + Value = value; + } } \ No newline at end of file diff --git a/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoViewModel.cs b/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoViewModel.cs index 0ab2ef5..20031d3 100644 --- a/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoViewModel.cs +++ b/src/ZonyLrcTools.Desktop/ViewModels/Settings/TagInfoViewModel.cs @@ -6,11 +6,8 @@ namespace ZonyLrcTools.Desktop.ViewModels.Settings; public class TagInfoViewModel : ViewModelBase { - private readonly GlobalOptions _options; - public TagInfoViewModel(GlobalOptions options) { - _options = options; BlockWord = new BlockWordViewModel(options); TagInfoProviders = new ObservableCollection(options.Provider.Tag.Plugin.Select(p => new TagInfoProviderViewModel(p))); } diff --git a/src/ZonyLrcTools.Desktop/ViewModels/ViewModelBase.cs b/src/ZonyLrcTools.Desktop/ViewModels/ViewModelBase.cs index de8358e..470004d 100644 --- a/src/ZonyLrcTools.Desktop/ViewModels/ViewModelBase.cs +++ b/src/ZonyLrcTools.Desktop/ViewModels/ViewModelBase.cs @@ -1,5 +1,18 @@ -using ReactiveUI; +using System; +using System.Linq.Expressions; +using ReactiveUI; namespace ZonyLrcTools.Desktop.ViewModels; -public abstract class ViewModelBase : ReactiveObject; \ No newline at end of file +public abstract class ViewModelBase : ReactiveObject +{ + protected void SubscribeToProperty(TSender sender, Expression> property, Action updateGlobalConfigAction) + { + sender.WhenAnyValue(property) + .Subscribe(x => + { + updateGlobalConfigAction(x); + App.Current.UpdateConfiguration(); + }); + } +} \ No newline at end of file diff --git a/src/ZonyLrcTools.Desktop/ZonyLrcTools.Desktop.csproj b/src/ZonyLrcTools.Desktop/ZonyLrcTools.Desktop.csproj index 681e69e..8342c39 100644 --- a/src/ZonyLrcTools.Desktop/ZonyLrcTools.Desktop.csproj +++ b/src/ZonyLrcTools.Desktop/ZonyLrcTools.Desktop.csproj @@ -33,7 +33,7 @@ - Always + PreserveNewest diff --git a/tests/ZonyLrcTools.Tests/Infrastructure/Album/QQMusicAlbumDownloaderTests.cs b/tests/ZonyLrcTools.Tests/Infrastructure/Album/QQMusicAlbumDownloaderTests.cs index ce8d551..9ae9b7b 100644 --- a/tests/ZonyLrcTools.Tests/Infrastructure/Album/QQMusicAlbumDownloaderTests.cs +++ b/tests/ZonyLrcTools.Tests/Infrastructure/Album/QQMusicAlbumDownloaderTests.cs @@ -5,12 +5,14 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; +using Xunit; using ZonyLrcTools.Common.Album; namespace ZonyLrcTools.Tests.Infrastructure.Album { public class QQMusicAlbumDownloaderTests : TestBase { + [Fact] public async Task DownloadDataAsync_Test() { var downloader = ServiceProvider.GetRequiredService>()