mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-09-04 20:46:54 +00:00
Compare commits
8 Commits
dependabot
...
dev
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c89b79fda6 | ||
![]() |
e08d1c7f16 | ||
![]() |
c3d98d60b5 | ||
![]() |
8aac9e16a6 | ||
![]() |
21f25ee500 | ||
![]() |
9a979942e3 | ||
![]() |
32b6c4052b | ||
![]() |
7732ab52d3 |
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Publish
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>5.0.0.0</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Authors>Zony(real-zony)</Authors>
|
||||
<RepositoryUrl>https://github.com/real-zony/ZonyLrcToolsX</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
@@ -3,26 +3,23 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||
<PackageVersion Include="FluentIcons.Avalonia.Fluent" Version="1.1.244" />
|
||||
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
|
||||
<PackageVersion Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="QRCoder" Version="1.5.1" />
|
||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Async" Version="2.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
|
||||
<PackageVersion Include="QRCoder" Version="1.6.0" />
|
||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
<PackageVersion Include="Polly" Version="8.4.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.7" />
|
||||
<PackageVersion Include="Polly" Version="8.6.2" />
|
||||
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
|
||||
<PackageVersion Include="MusicDecrypto.Library" Version="2.4.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageVersion Include="MusicDecrypto.Library" Version="2.4.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.7" />
|
||||
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.6" />
|
||||
@@ -31,23 +28,18 @@
|
||||
<PackageVersion Include="SuperSocket.WebSocket.Server" Version="2.0.0-beta.11" />
|
||||
<!-- Testing Projects -->
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageVersion Include="Shouldly" Version="4.2.1" />
|
||||
<PackageVersion Include="xunit" Version="2.8.1" />
|
||||
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageVersion>
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.2">
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageVersion>
|
||||
<!-- Avalonia -->
|
||||
<PackageVersion Include="Avalonia" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.11" />
|
||||
<PackageVersion Include="YamlDotNet" Version="15.3.0" />
|
||||
<PackageVersion Include="Ude.NetStandard" Version="1.2.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@@ -13,8 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Tests", "tests
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Common", "src\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj", "{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Desktop", "src\ZonyLrcTools.Desktop\ZonyLrcTools.Desktop.csproj", "{DC92902B-4303-4E43-AFB3-3F93FD3986AD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -33,10 +31,6 @@ Global
|
||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -48,6 +42,5 @@ Global
|
||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||
{FFBD3200-568F-455B-8390-5E76C51D522C} = {AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}
|
||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||
{DC92902B-4303-4E43-AFB3-3F93FD3986AD} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<PackageReference Include="Serilog.Sinks.Console"/>
|
||||
<PackageReference Include="Serilog.Sinks.File"/>
|
||||
<PackageReference Include="System.Text.Encoding.CodePages"/>
|
||||
<PackageReference Include="Ude.NetStandard"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -1,3 +1,5 @@
|
||||
using System.Text;
|
||||
using Ude;
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
|
||||
namespace ZonyLrcTools.Common.MusicScanner;
|
||||
@@ -13,11 +15,15 @@ public class CsvFileMusicScanner : ITransientDependency
|
||||
/// <param name="csvFilePath">CSV 文件的路径。</param>
|
||||
/// <param name="outputDirectory">歌词文件的输出目录。</param>
|
||||
/// <param name="pattern">输出的歌词文件格式,默认是 "{Artist} - {Title}.lrc" 的形式。</param>
|
||||
public async Task<List<MusicInfo>> GetMusicInfoFromCsvFileAsync(string csvFilePath, string outputDirectory, string pattern)
|
||||
public async Task<List<MusicInfo>> GetMusicInfoFromCsvFileAsync(string csvFilePath, string outputDirectory,
|
||||
string pattern)
|
||||
{
|
||||
var csvFileContent = await File.ReadAllTextAsync(csvFilePath);
|
||||
var csvFileLines = csvFileContent.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return csvFileLines.Skip(1).Select(line => GetMusicInfoFromCsvFileLine(line, outputDirectory, pattern)).ToList();
|
||||
var encoding = DetectFileEncoding(csvFilePath);
|
||||
|
||||
var csvFileContent = await File.ReadAllTextAsync(csvFilePath, encoding);
|
||||
var csvFileLines = csvFileContent.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||
return csvFileLines.Skip(1).Select(line => GetMusicInfoFromCsvFileLine(line, outputDirectory, pattern))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private MusicInfo GetMusicInfoFromCsvFileLine(string csvFileLine, string outputDirectory, string pattern)
|
||||
@@ -29,4 +35,19 @@ public class CsvFileMusicScanner : ITransientDependency
|
||||
var musicInfo = new MusicInfo(fakeFilePath, name, artist);
|
||||
return musicInfo;
|
||||
}
|
||||
|
||||
private Encoding DetectFileEncoding(string filePath)
|
||||
{
|
||||
using var fileStream = File.OpenRead(filePath);
|
||||
var detector = new CharsetDetector();
|
||||
detector.Feed(fileStream);
|
||||
detector.DataEnd();
|
||||
|
||||
if (detector.Charset != null)
|
||||
{
|
||||
return Encoding.GetEncoding(detector.Charset);
|
||||
}
|
||||
|
||||
return Encoding.Default;
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
@@ -15,6 +15,7 @@
|
||||
<PackageReference Include="Polly"/>
|
||||
<PackageReference Include="QRCoder"/>
|
||||
<PackageReference Include="TagLibSharp"/>
|
||||
<PackageReference Include="Ude.NetStandard" />
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -1,35 +0,0 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="ZonyLrcTools.Desktop.App"
|
||||
xmlns:local="using:ZonyLrcTools.Desktop"
|
||||
xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia"
|
||||
RequestedThemeVariant="Default"
|
||||
xmlns:ic="using:FluentIcons.Avalonia.Fluent">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator />
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ic:SymbolIconSource x:Key="OpenIcon" Symbol="Open" />
|
||||
<ic:SymbolIconSource x:Key="CodeIcon" Symbol="Code" />
|
||||
<ic:SymbolIconSource x:Key="ChatHelpIcon" Symbol="ChatHelp" />
|
||||
<ic:SymbolIconSource x:Key="SettingsIcon" Symbol="Settings" />
|
||||
<ic:SymbolIconSource x:Key="DownloadIcon" Symbol="ArrowDownload" />
|
||||
<ic:SymbolIconSource x:Key="TagIcon" Symbol="Tag" />
|
||||
|
||||
<FontFamily x:Key="GlobalFontFamily">Microsoft YaHei, Simsun, Arial</FontFamily>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
<styling:FluentAvaloniaTheme />
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{StaticResource GlobalFontFamily}" />
|
||||
</Style>
|
||||
<StyleInclude Source="/Controls/DownloadStatusControl.axaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
@@ -1,67 +0,0 @@
|
||||
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;
|
||||
using ZonyLrcTools.Desktop.Views;
|
||||
|
||||
namespace ZonyLrcTools.Desktop;
|
||||
|
||||
public class App : Application
|
||||
{
|
||||
public new static App Current => (App)Application.Current!;
|
||||
public IServiceProvider Services { get; } = ConfigureServices();
|
||||
|
||||
public IOptions<GlobalOptions> GlobalOptions => Services.GetRequiredService<IOptions<GlobalOptions>>();
|
||||
|
||||
private Lazy<ISerializer> _yamlSerializer = new(() => new SerializerBuilder()
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.WithDefaultScalarStyle(ScalarStyle.DoubleQuoted)
|
||||
.Build());
|
||||
|
||||
private static IServiceProvider ConfigureServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.BeginAutoDependencyInject<Program>();
|
||||
services.BeginAutoDependencyInject<IWarpHttpClient>();
|
||||
services.ConfigureConfiguration();
|
||||
services.ConfigureToolService();
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new HomeViewModel(),
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 172 KiB |
Binary file not shown.
Before Width: | Height: | Size: 142 KiB |
@@ -1,92 +0,0 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:ZonyLrcTools.Desktop.Controls"
|
||||
xmlns:fluent="using:FluentIcons.Avalonia.Fluent">
|
||||
|
||||
<Design.PreviewWith>
|
||||
<controls:DownloadStatusControl Status="Completed" />
|
||||
</Design.PreviewWith>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl">
|
||||
<!-- Set Defaults -->
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<Border Name="IconContainer" Width="20" Height="20">
|
||||
<fluent:SymbolIcon Name="StatusIcon" Symbol="{TemplateBinding Symbol}" />
|
||||
</Border>
|
||||
<TextBlock Text="{TemplateBinding Text}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=NotStarted]">
|
||||
<Setter Property="Symbol" Value="QuestionCircle" />
|
||||
<Setter Property="Text" Value="未下载" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Completed]">
|
||||
<Setter Property="Symbol" Value="CheckmarkCircle" />
|
||||
<Setter Property="Text" Value="已完成" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Failed]">
|
||||
<Setter Property="Symbol" Value="DismissCircle" />
|
||||
<Setter Property="Text" Value="失败" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Completed] /template/ Border#IconContainer">
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:0.5">
|
||||
<KeyFrame Cue="0%">
|
||||
<Setter Property="RotateTransform.Angle" Value="0"/>
|
||||
<Setter Property="ScaleTransform.ScaleX" Value="1"/>
|
||||
<Setter Property="ScaleTransform.ScaleY" Value="1"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="50%">
|
||||
<Setter Property="RotateTransform.Angle" Value="180"/>
|
||||
<Setter Property="ScaleTransform.ScaleX" Value="1.2"/>
|
||||
<Setter Property="ScaleTransform.ScaleY" Value="1.2"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="RotateTransform.Angle" Value="360"/>
|
||||
<Setter Property="ScaleTransform.ScaleX" Value="1"/>
|
||||
<Setter Property="ScaleTransform.ScaleY" Value="1"/>
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Failed] /template/ Border#IconContainer">
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:0.5" IterationCount="3">
|
||||
<KeyFrame Cue="0%">
|
||||
<Setter Property="TranslateTransform.X" Value="0"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="25%">
|
||||
<Setter Property="TranslateTransform.X" Value="-2"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="50%">
|
||||
<Setter Property="TranslateTransform.X" Value="0"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="75%">
|
||||
<Setter Property="TranslateTransform.X" Value="2"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="TranslateTransform.X" Value="0"/>
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl /template/ Border#IconContainer">
|
||||
<Setter Property="RenderTransform">
|
||||
<TransformGroup>
|
||||
<RotateTransform/>
|
||||
<ScaleTransform/>
|
||||
<TranslateTransform/>
|
||||
</TransformGroup>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Styles>
|
@@ -1,36 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using FluentIcons.Common;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Controls;
|
||||
|
||||
public class DownloadStatusControl : TemplatedControl
|
||||
{
|
||||
public static readonly StyledProperty<DownloadStatus> StatusProperty =
|
||||
AvaloniaProperty.Register<DownloadStatusControl, DownloadStatus>(nameof(Status));
|
||||
|
||||
public static readonly StyledProperty<Symbol> SymbolProperty =
|
||||
AvaloniaProperty.Register<DownloadStatusControl, Symbol>(nameof(Symbol));
|
||||
|
||||
public static readonly StyledProperty<string> TextProperty =
|
||||
AvaloniaProperty.Register<DownloadStatusControl, string>(nameof(Text));
|
||||
|
||||
public DownloadStatus Status
|
||||
{
|
||||
get => GetValue(StatusProperty);
|
||||
set => SetValue(StatusProperty, value);
|
||||
}
|
||||
|
||||
public Symbol Symbol
|
||||
{
|
||||
get => GetValue(SymbolProperty);
|
||||
set => SetValue(SymbolProperty, value);
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => GetValue(TextProperty);
|
||||
set => SetValue(TextProperty, value);
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ReactiveUI />
|
||||
</Weavers>
|
@@ -1,33 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Helpers;
|
||||
|
||||
public static class UrlHelper
|
||||
{
|
||||
public static void OpenLink(string url)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
using var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "/bin/sh",
|
||||
Arguments = $"-c \"xdg-open {url.Replace("\"", "\\\"")}\"",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
using var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? url : "open",
|
||||
Arguments = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? url : "",
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
using ZonyLrcTools.Common.Infrastructure.Logging;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Infrastructure.Logging;
|
||||
|
||||
public class SerilogWarpLogger(ILogger<SerilogWarpLogger> logger) : IWarpLogger, ITransientDependency
|
||||
{
|
||||
public Task DebugAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogDebug(message, exception);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task InfoAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogInformation(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task WarnAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogWarning(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ErrorAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogError(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:ZonyLrcTools.Desktop.ViewModels"
|
||||
xmlns:controls="clr-namespace:ZonyLrcTools.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Pages.HomePage"
|
||||
x:DataType="viewModels:HomeViewModel">
|
||||
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Bottom" Margin="10">
|
||||
<TextBlock Text="下载进度:" Margin="0,0,0,5" />
|
||||
<ProgressBar Value="{Binding DownloadProgress}" Maximum="{Binding MaxProgress}" Height="20" />
|
||||
</StackPanel>
|
||||
|
||||
<DataGrid AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
GridLinesVisibility="All"
|
||||
BorderThickness="1"
|
||||
BorderBrush="Gray"
|
||||
ItemsSource="{Binding Songs}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="歌曲名称" Binding="{Binding SongName}" Width="*" />
|
||||
<DataGridTextColumn Header="歌手名称" Binding="{Binding ArtistName}" Width="*" />
|
||||
<DataGridTextColumn Header="文件路径" Binding="{Binding FilePath}" Width="2*" />
|
||||
<DataGridTemplateColumn Header="下载状态" Width="1*">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<controls:DownloadStatusControl Status="{Binding DownloadStatus}" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
|
||||
<DataGrid.Styles>
|
||||
<Style Selector="DataGridColumnHeader">
|
||||
<Setter Property="Background" Value="{DynamicResource ControlFillColorDefault}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimary}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="12,8,12,8" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<Style Selector="DataGridCell">
|
||||
<Setter Property="Padding" Value="12,8" />
|
||||
</Style>
|
||||
</DataGrid.Styles>
|
||||
</DataGrid>
|
||||
</DockPanel>
|
||||
</UserControl>
|
@@ -1,24 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Pages;
|
||||
|
||||
public partial class HomePage : UserControl
|
||||
{
|
||||
public HomePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (DataContext is HomeViewModel vm)
|
||||
{
|
||||
vm.MaxProgress = 100;
|
||||
vm.DownloadProgress = 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,190 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Pages.SettingsPage"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:settings="clr-namespace:ZonyLrcTools.Desktop.ViewModels.Settings"
|
||||
x:DataType="settings:SettingsViewModel">
|
||||
|
||||
<UserControl.Resources>
|
||||
<settings:NullToDefaultValueConverter x:Key="NullToDefaultValueConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Orientation="Vertical" Margin="24" Spacing="8">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto">
|
||||
<Image Width="64" Height="64" HorizontalAlignment="Left"
|
||||
Source="/Assets/logo.ico"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality" />
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
Margin="12,0,0,0" VerticalAlignment="Center">
|
||||
<TextBlock Text="ZonyLrcToolsX" />
|
||||
<TextBlock Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Version, StringFormat='Version {0}'}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Margin="0,20,0,0"
|
||||
Text="软件设置" />
|
||||
<!-- 全局设置 -->
|
||||
<ui:SettingsExpander Header="全局设置" Description="配置软件的全局设置" IconSource="{StaticResource SettingsIcon}">
|
||||
<ui:SettingsExpanderItem Content="是否合并歌词为一行">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsOneLine}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="换行符">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ComboBox ItemsSource="{Binding Config.LineBreakOptions}"
|
||||
SelectedItem="{Binding Config.SelectedLineBreak}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="启用翻译歌词">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsEnableTranslation}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="跳过已经存在的歌词文件(不覆盖)">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsSkipExistLyricFiles}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="文件编码">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ComboBox ItemsSource="{Binding Config.FileEncodingOptions}"
|
||||
SelectedItem="{Binding Config.SelectedFileEncoding}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="只输出翻译歌词">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsOnlyOutputTranslation}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<!-- 歌词下载器配置 -->
|
||||
<ui:SettingsExpander Header="歌词下载器设置" Description="配置每个单独的歌词下载器参数" IconSource="{StaticResource DownloadIcon}">
|
||||
<ItemsRepeater ItemsSource="{Binding LyricsProviders}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ui:SettingsExpander Margin="0,0,0,10">
|
||||
<ui:SettingsExpander.Header>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</ui:SettingsExpander.Header>
|
||||
|
||||
<ui:SettingsExpanderItem Content="下载器优先级">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<NumericUpDown Value="{Binding Priority, Converter={StaticResource NullToDefaultValueConverter}}" Minimum="-1" Maximum="100" Increment="1" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="搜索深度">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<NumericUpDown Value="{Binding Depth, Converter={StaticResource NullToDefaultValueConverter}}" Minimum="1" Maximum="100" Increment="1" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<!-- 标签设置 -->
|
||||
<ui:SettingsExpander Header="标签读取设置" Description="配置读取音乐文件标签时的一些参数" IconSource="{StaticResource TagIcon}">
|
||||
<!-- Block Word Options -->
|
||||
<ui:SettingsExpanderItem Content="是否启用屏蔽字典">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Tag.BlockWord.IsEnable}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="屏蔽字典的路径:">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBox Text="{Binding Tag.BlockWord.FilePath}" Width="200" />
|
||||
<Button Content="浏览" Command="{Binding BrowseBlockWordFileCommand}" CommandParameter="{Binding $parent[Window]}" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpander Header="标签扫描器" Description="配置标签扫描器">
|
||||
<ItemsRepeater ItemsSource="{Binding Tag.TagInfoProviders}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ui:SettingsExpander Margin="0,0,0,10">
|
||||
<ui:SettingsExpander.Header>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</ui:SettingsExpander.Header>
|
||||
|
||||
<ui:SettingsExpanderItem Content="优先级">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<NumericUpDown Value="{Binding Priority , Converter={StaticResource NullToDefaultValueConverter}}" Minimum="-1" Maximum="100" Increment="1" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="扩展配置">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ItemsRepeater ItemsSource="{Binding Extensions}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid ColumnDefinitions="Auto,*" Margin="0,5">
|
||||
<TextBlock Grid.Column="0" Text="{Binding Key}" Margin="0,0,10,0" />
|
||||
<TextBox Grid.Column="1" Text="{Binding Value}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ui:SettingsExpander>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<TextBlock Margin="0,20,0,0"
|
||||
Text="关于" />
|
||||
|
||||
<ui:SettingsExpander Description="获得帮助与支持信息" Header="帮助与支持"
|
||||
IconSource="{StaticResource ChatHelpIcon}">
|
||||
<ui:SettingsExpanderItem>
|
||||
<!-- QQ 群 -->
|
||||
<StackPanel Orientation="Vertical" Spacing="10">
|
||||
<TextBlock Text="QQ 群: 337656932" />
|
||||
<ui:HyperlinkButton NavigateUri="https://docs.myzony.com/">
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<ui:SymbolIcon Symbol="Link" />
|
||||
<TextBlock Text="使用文档" />
|
||||
</StackPanel>
|
||||
</ui:HyperlinkButton>
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<ui:SettingsExpander ActionIconSource="{StaticResource OpenIcon}" Click="OnGitHubClick"
|
||||
Description="获取软件的源代码" Header="GitHub 仓库"
|
||||
IconSource="{StaticResource CodeIcon}" IsClickEnabled="True" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
@@ -1,20 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Desktop.Helpers;
|
||||
using ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Pages;
|
||||
|
||||
public partial class SettingsPage : UserControl
|
||||
{
|
||||
public SettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new SettingsViewModel(App.Current.Services.GetRequiredService<IOptions<GlobalOptions>>().Value);
|
||||
}
|
||||
|
||||
private void OnGitHubClick(object? sender, RoutedEventArgs? eventArgs) => UrlHelper.OpenLink("https://github.com/real-zony/ZonyLrcToolsX");
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace ZonyLrcTools.Desktop;
|
||||
|
||||
class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.WithInterFont()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI();
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Templates;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop;
|
||||
|
||||
public class ViewLocator : IDataTemplate
|
||||
{
|
||||
public Control Build(object data)
|
||||
{
|
||||
var name = data.GetType().FullName!.Replace("ViewModel", "View");
|
||||
var type = Type.GetType(name);
|
||||
|
||||
if (type != null)
|
||||
{
|
||||
return (Control)Activator.CreateInstance(type)!;
|
||||
}
|
||||
|
||||
return new TextBlock { Text = "Not Found: " + name };
|
||||
}
|
||||
|
||||
public bool Match(object data)
|
||||
{
|
||||
return data is ViewModelBase;
|
||||
}
|
||||
}
|
@@ -1,77 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using ReactiveUI;
|
||||
using ZonyLrcTools.Common;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
public class HomeViewModel : ViewModelBase
|
||||
{
|
||||
private ObservableCollection<SongInfoViewModel> _songs = new();
|
||||
|
||||
public ObservableCollection<SongInfoViewModel> Songs
|
||||
{
|
||||
get => _songs;
|
||||
set => this.RaiseAndSetIfChanged(ref _songs, value);
|
||||
}
|
||||
|
||||
private int _downloadProgress;
|
||||
|
||||
public int DownloadProgress
|
||||
{
|
||||
get => _downloadProgress;
|
||||
set => this.RaiseAndSetIfChanged(ref _downloadProgress, value);
|
||||
}
|
||||
|
||||
private int _maxProgress;
|
||||
|
||||
public int MaxProgress
|
||||
{
|
||||
get => _maxProgress;
|
||||
set => this.RaiseAndSetIfChanged(ref _maxProgress, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class SongInfoViewModel : ReactiveObject
|
||||
{
|
||||
public SongInfoViewModel(MusicInfo info)
|
||||
{
|
||||
_info = info;
|
||||
DownloadStatus = DownloadStatus.NotStarted;
|
||||
|
||||
this.WhenAnyValue(x => x.Info)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(SongName));
|
||||
this.RaisePropertyChanged(nameof(ArtistName));
|
||||
this.RaisePropertyChanged(nameof(FilePath));
|
||||
});
|
||||
}
|
||||
|
||||
private MusicInfo _info;
|
||||
|
||||
public MusicInfo Info
|
||||
{
|
||||
get => _info;
|
||||
set => this.RaiseAndSetIfChanged(ref _info, value);
|
||||
}
|
||||
|
||||
public string SongName => Info.Name;
|
||||
public string ArtistName => Info.Artist;
|
||||
public string FilePath => Info.FilePath;
|
||||
|
||||
private DownloadStatus _downloadStatus;
|
||||
|
||||
public DownloadStatus DownloadStatus
|
||||
{
|
||||
get => _downloadStatus;
|
||||
set => this.RaiseAndSetIfChanged(ref _downloadStatus, value);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DownloadStatus
|
||||
{
|
||||
NotStarted = 1,
|
||||
Completed,
|
||||
Failed
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class BlockWordViewModel : ViewModelBase
|
||||
{
|
||||
public BlockWordViewModel(GlobalOptions options)
|
||||
{
|
||||
IsEnable = options.Provider.Tag.BlockWord.IsEnable;
|
||||
FilePath = options.Provider.Tag.BlockWord.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; }
|
||||
|
||||
[Reactive] public string? FilePath { get; set; }
|
||||
}
|
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class GlobalConfigurationViewModel : ViewModelBase
|
||||
{
|
||||
public GlobalConfigurationViewModel(GlobalOptions config)
|
||||
{
|
||||
var globalConfig = config.Provider.Lyric.Config;
|
||||
|
||||
InitializeLineBreakComboBoxItem(globalConfig);
|
||||
|
||||
IsOneLine = globalConfig.IsOneLine;
|
||||
IsEnableTranslation = globalConfig.IsEnableTranslation;
|
||||
IsSkipExistLyricFiles = globalConfig.IsSkipExistLyricFiles;
|
||||
IsOnlyOutputTranslation = globalConfig.IsOnlyOutputTranslation;
|
||||
|
||||
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; }
|
||||
|
||||
[Reactive] public bool IsEnableTranslation { get; set; }
|
||||
|
||||
[Reactive] public bool IsSkipExistLyricFiles { get; set; }
|
||||
|
||||
[Reactive] public bool IsOnlyOutputTranslation { get; set; }
|
||||
|
||||
private void InitializeLineBreakComboBoxItem(GlobalLyricsConfigOptions config)
|
||||
{
|
||||
LineBreakOptions =
|
||||
[
|
||||
new TextComboboxItem { Name = "Windows", Value = "\r\n" },
|
||||
new TextComboboxItem { Name = "Unix", Value = "\n" },
|
||||
new TextComboboxItem { Name = "Mac", Value = "\r" }
|
||||
];
|
||||
SelectedLineBreak = LineBreakOptions.FirstOrDefault(x => x.Value == config.LineBreak)
|
||||
?? LineBreakOptions.First();
|
||||
|
||||
FileEncodingOptions = Encoding.GetEncodings()
|
||||
.Select(x => new TextComboboxItem { Name = x.DisplayName, Value = x.Name })
|
||||
.ToList();
|
||||
FileEncodingOptions.Insert(0, new TextComboboxItem { Name = "UTF-8-BOM", Value = "utf-8-bom" });
|
||||
SelectedFileEncoding = FileEncodingOptions.FirstOrDefault(x => x.Value == config.FileEncoding)
|
||||
?? FileEncodingOptions.First();
|
||||
|
||||
this.WhenAnyValue(x => x.SelectedLineBreak)
|
||||
.Subscribe(x => config.LineBreak = x.Value);
|
||||
this.WhenAnyValue(x => x.SelectedFileEncoding)
|
||||
.Subscribe(x => config.FileEncoding = x.Value);
|
||||
}
|
||||
|
||||
public List<TextComboboxItem> LineBreakOptions { get; private set; }
|
||||
|
||||
[Reactive] public TextComboboxItem SelectedLineBreak { get; set; }
|
||||
|
||||
public List<TextComboboxItem> FileEncodingOptions { get; private set; }
|
||||
|
||||
[Reactive] public TextComboboxItem SelectedFileEncoding { get; set; }
|
||||
|
||||
public class TextComboboxItem
|
||||
{
|
||||
public string Name { get; set; } = default!;
|
||||
public string Value { get; set; } = default!;
|
||||
}
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class LyricsProviderViewModel : ViewModelBase
|
||||
{
|
||||
public LyricsProviderViewModel(LyricsProviderOptions options)
|
||||
{
|
||||
var globalOptions = App.Current.Services.GetRequiredService<IOptions<GlobalOptions>>().Value;
|
||||
|
||||
Name = options.Name;
|
||||
Priority = options.Priority;
|
||||
Depth = options.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; }
|
||||
|
||||
[Reactive] public int Priority { get; set; }
|
||||
|
||||
[Reactive] public int Depth { get; set; }
|
||||
}
|
||||
|
||||
internal class NullToDefaultValueConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null ? System.Convert.ToDecimal(value) : 0;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is decimal dValue)
|
||||
{
|
||||
return System.Convert.ToInt32(dValue);
|
||||
}
|
||||
|
||||
return BindingOperations.DoNothing;
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using ReactiveUI;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
public SettingsViewModel(GlobalOptions globalOptions)
|
||||
{
|
||||
Config = new GlobalConfigurationViewModel(globalOptions);
|
||||
LyricsProviders = new ObservableCollection<LyricsProviderViewModel>(globalOptions.Provider.Lyric.Plugin.Select(p => new LyricsProviderViewModel(p)));
|
||||
Tag = new TagInfoViewModel(globalOptions);
|
||||
BrowseBlockWordFileCommand = ReactiveCommand.CreateFromTask<Window>(BrowseBlockWordFile);
|
||||
}
|
||||
|
||||
public static string Version => typeof(Program).Assembly.GetName().Version!.ToString();
|
||||
|
||||
public TagInfoViewModel Tag { get; }
|
||||
|
||||
public ReactiveCommand<Window, Unit> BrowseBlockWordFileCommand { get; }
|
||||
|
||||
public GlobalConfigurationViewModel Config { get; }
|
||||
|
||||
public ObservableCollection<LyricsProviderViewModel> LyricsProviders { get; }
|
||||
|
||||
private async Task BrowseBlockWordFile(Window parentWindow)
|
||||
{
|
||||
var storage = parentWindow.StorageProvider;
|
||||
if (storage.CanOpen)
|
||||
{
|
||||
var options = new FilePickerOpenOptions
|
||||
{
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = new[]
|
||||
{
|
||||
new FilePickerFileType("JSON")
|
||||
{
|
||||
Patterns = new[] { "*.json" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var files = await storage.OpenFilePickerAsync(options);
|
||||
if (files.Count > 0)
|
||||
{
|
||||
Tag.BlockWord.FilePath = files[0].Path.LocalPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class TagInfoProviderViewModel : ViewModelBase
|
||||
{
|
||||
public TagInfoProviderViewModel(TagInfoProviderOptions options)
|
||||
{
|
||||
var globalOptions = App.Current.Services.GetRequiredService<IOptions<GlobalOptions>>().Value;
|
||||
|
||||
Name = options.Name;
|
||||
Priority = options.Priority;
|
||||
Extensions = options.Extensions == null ? [] : new ObservableCollection<ObservableKeyValuePair>(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; }
|
||||
|
||||
[Reactive] public ObservableCollection<ObservableKeyValuePair> 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;
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class TagInfoViewModel : ViewModelBase
|
||||
{
|
||||
public TagInfoViewModel(GlobalOptions options)
|
||||
{
|
||||
BlockWord = new BlockWordViewModel(options);
|
||||
TagInfoProviders = new ObservableCollection<TagInfoProviderViewModel>(options.Provider.Tag.Plugin.Select(p => new TagInfoProviderViewModel(p)));
|
||||
}
|
||||
|
||||
public BlockWordViewModel BlockWord { get; }
|
||||
|
||||
public ObservableCollection<TagInfoProviderViewModel> TagInfoProviders { get; }
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
public abstract class ViewModelBase : ReactiveObject
|
||||
{
|
||||
protected void SubscribeToProperty<TSender, TRet>(TSender sender, Expression<Func<TSender, TRet>> property, Action<TRet> updateGlobalConfigAction)
|
||||
{
|
||||
sender.WhenAnyValue(property)
|
||||
.Subscribe(x =>
|
||||
{
|
||||
updateGlobalConfigAction(x);
|
||||
App.Current.UpdateConfiguration();
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,74 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:fluent="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Views.MainView">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid Name="TitleBarHost"
|
||||
Background="Transparent"
|
||||
ColumnDefinitions="Auto,Auto,*,Auto,0">
|
||||
<Grid.Styles>
|
||||
<Style Selector="Button.AppBarButton">
|
||||
<Setter Property="Padding" Value="8" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Theme" Value="{StaticResource TransparentButton}" />
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<Image Width="20" Height="20" Margin="14"
|
||||
Source="/Assets/logo.ico" IsHitTestVisible="False"
|
||||
IsVisible="{Binding !#FrameView.CanGoBack}"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality" />
|
||||
|
||||
<Button Grid.Column="0" Margin="6"
|
||||
Classes="AppBarButton" Command="{Binding #FrameView.GoBack}"
|
||||
IsEnabled="{Binding #FrameView.CanGoBack}"
|
||||
IsVisible="{Binding #FrameView.CanGoBack}">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="ArrowLeft" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>Back</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="ZonyLrcToolsX" IsHitTestVisible="False" />
|
||||
|
||||
<StackPanel Grid.Column="3"
|
||||
Orientation="Horizontal" Margin="6" Spacing="6">
|
||||
<Button Name="OpenFolderButton" Classes="AppBarButton">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="FolderAdd" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>扫描文件夹</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
<Button Name="DownloadButton" Classes="AppBarButton">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="ArrowDownload" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>下载歌词</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
<Button Name="SettingsButton" Classes="AppBarButton">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="Settings" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>软件设置</ToolTip.Tip>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="1" Grid.ColumnSpan="3"
|
||||
Background="{DynamicResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
CornerRadius="8 8 0 0">
|
||||
<ui:Frame Name="FrameView" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -1,102 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using DynamicData;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ZonyLrcTools.Common;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
using ZonyLrcTools.Desktop.Pages;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Views;
|
||||
|
||||
public partial class MainView : UserControl
|
||||
{
|
||||
private Window? _window;
|
||||
|
||||
private Frame? _frameView;
|
||||
private Button? _settingsButton;
|
||||
private Button? _openFolderButton;
|
||||
private Button? _downloadButton;
|
||||
|
||||
public MainView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
_window = e.Root as Window;
|
||||
|
||||
_frameView = this.FindControl<Frame>("FrameView");
|
||||
_frameView?.Navigate(typeof(HomePage));
|
||||
|
||||
_settingsButton = this.FindControl<Button>("SettingsButton");
|
||||
if (_settingsButton != null) _settingsButton.Click += OnSettingsButtonClick;
|
||||
|
||||
_openFolderButton = this.FindControl<Button>("OpenFolderButton");
|
||||
if (_openFolderButton != null) _openFolderButton.Click += OnOpenFolderButtonClick;
|
||||
_downloadButton = this.FindControl<Button>("DownloadButton");
|
||||
if (_downloadButton != null) _downloadButton.Click += OnDownloadButtonClick;
|
||||
}
|
||||
|
||||
private async void OnDownloadButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var downloader = App.Current.Services.GetRequiredService<ILyricsDownloader>();
|
||||
if (DataContext is HomeViewModel vm)
|
||||
{
|
||||
var needDownloadMusicInfos = vm.Songs
|
||||
.Where(x => x.DownloadStatus == DownloadStatus.NotStarted)
|
||||
.Select(x => x.Info)
|
||||
.ToList();
|
||||
|
||||
vm.DownloadProgress = 0;
|
||||
vm.MaxProgress = needDownloadMusicInfos.Count;
|
||||
|
||||
await downloader.DownloadAsync(needDownloadMusicInfos, callback: info =>
|
||||
{
|
||||
var song = vm.Songs.FirstOrDefault(x => x.Info == info);
|
||||
if (song != null)
|
||||
{
|
||||
song.DownloadStatus = DownloadStatus.Completed;
|
||||
}
|
||||
|
||||
vm.DownloadProgress++;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnOpenFolderButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var storage = _window?.StorageProvider;
|
||||
var musicInfoLoader = App.Current.Services.GetRequiredService<IMusicInfoLoader>();
|
||||
|
||||
if (storage?.CanOpen == true && DataContext is HomeViewModel vm)
|
||||
{
|
||||
var options = new FolderPickerOpenOptions
|
||||
{
|
||||
SuggestedStartLocation = await storage.TryGetWellKnownFolderAsync(WellKnownFolder.Music)
|
||||
};
|
||||
var folders = await storage.OpenFolderPickerAsync(options);
|
||||
if (folders.Count == 0) return;
|
||||
var folderPath = folders[0].Path.LocalPath;
|
||||
var musicInfos = await musicInfoLoader.LoadAsync(folderPath);
|
||||
|
||||
vm.Songs.Clear();
|
||||
vm.Songs.AddRange(musicInfos.Select(x => new SongInfoViewModel(x!)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSettingsButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_frameView?.CurrentSourcePageType != typeof(SettingsPage))
|
||||
_frameView?.Navigate(typeof(SettingsPage));
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:views="clr-namespace:ZonyLrcTools.Desktop.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Views.MainWindow"
|
||||
Background="{DynamicResource AcrylicBackgroundFillColorDefaultBrush}"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
Title="ZonyLrcTools.Desktop">
|
||||
|
||||
<Panel>
|
||||
<views:MainView />
|
||||
</Panel>
|
||||
|
||||
</Window>
|
@@ -1,13 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
DataContext = new HomeViewModel();
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia"/>
|
||||
<PackageReference Include="Avalonia.Desktop"/>
|
||||
<PackageReference Include="Avalonia.Themes.Fluent"/>
|
||||
<PackageReference Include="Avalonia.Fonts.Inter"/>
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI"/>
|
||||
<PackageReference Include="FluentAvaloniaUI" />
|
||||
<PackageReference Include="FluentIcons.Avalonia.Fluent" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting"/>
|
||||
<PackageReference Include="ReactiveUI.Fody" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="config.yaml"/>
|
||||
<Content Include="config.yaml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<!-- This manifest is used on Windows only.
|
||||
Don't remove it as it might cause problems with window transparency and embeded controls.
|
||||
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||
<assemblyIdentity version="1.0.0.0" name="ZonyLrcTools.Desktop.Desktop"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on
|
||||
and is designed to work with. Uncomment the appropriate elements
|
||||
and Windows will automatically select the most compatible environment. -->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
@@ -1,61 +0,0 @@
|
||||
# 允许扫描的歌曲文件后缀名。
|
||||
supportFileExtensions:
|
||||
- '*.mp3'
|
||||
- '*.flac'
|
||||
- '*.wav'
|
||||
- '*.m4a'
|
||||
- '*.ogg'
|
||||
- '*.opus'
|
||||
|
||||
# 网络代理服务设置,仅支持 HTTP 代理。
|
||||
networkOptions:
|
||||
isEnable: false # 是否启用代理。
|
||||
ip: 127.0.0.1 # 代理服务 IP 地址。
|
||||
port: 4780 # 代理服务端口号。
|
||||
updateUrl: https://api.myzony.com/lrc-tools/update # 更新检查地址。
|
||||
|
||||
# 下载器的相关参数配置。
|
||||
provider:
|
||||
# 标签扫描器的相关参数配置。
|
||||
tag:
|
||||
# 支持的标签扫描器。
|
||||
plugin:
|
||||
- name: Taglib # 基于 Taglib 库的标签扫描器。
|
||||
priority: 1 # 优先级,升序排列。
|
||||
- name: FileName # 基于文件名的标签扫描器。
|
||||
priority: 2
|
||||
# 基于文件名扫描器的扩展参数。
|
||||
extensions:
|
||||
# 正则表达式,用于匹配文件名中的作者信息和歌曲信息,可根据
|
||||
# 自己的需求进行调整。
|
||||
regularExpressions: "(?'artist'.+)\\s-\\s(?'name'.+)"
|
||||
# 歌曲标签屏蔽字典替换功能。
|
||||
blockWord:
|
||||
isEnable: false # 是否启用屏蔽字典。
|
||||
filePath: 'BlockWords.json' # 屏蔽字典的路径。
|
||||
# 歌词下载器的相关参数配置。
|
||||
lyric:
|
||||
# 支持的歌词下载器。
|
||||
plugin:
|
||||
- name: NetEase # 基于网易云音乐的歌词下载器。
|
||||
priority: 1 # 优先级,升序排列,改为 -1 时禁用。
|
||||
depth: 10 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
|
||||
additional:
|
||||
isEnableRomanOutput: false # 是否启用罗马音输出,本参数仅当对应歌曲有罗马音歌词时有效。
|
||||
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
||||
priority: 2
|
||||
# depth: 10 # 暂时不支持。
|
||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||
priority: 3
|
||||
depth: 10
|
||||
- name: KuWo # 基于酷我音乐的歌词下载器。
|
||||
priority: 4
|
||||
depth: 10
|
||||
# 歌词下载的一些共有配置参数。
|
||||
config:
|
||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||
isEnableTranslation: true # 是否启用翻译歌词。
|
||||
isOnlyOutputTranslation: false # 是否只输出翻译歌词。
|
||||
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||
fileEncoding: 'utf-8' # 歌词文件的编码格式。
|
@@ -33,7 +33,7 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyrics
|
||||
await Should.ThrowAsync<ErrorCodeException>(_lyricsProvider.DownloadAsync("天ノ弱", "漆柚").AsTask);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact(Skip = "This music already exists KuGou's database.")]
|
||||
public async Task DownloadAsync_Index_Exception_Test()
|
||||
{
|
||||
await Should.ThrowAsync<ErrorCodeException>(async () => await _lyricsProvider.DownloadAsync("40'z", "ZOOLY"));
|
||||
|
@@ -17,7 +17,7 @@ public class KuWoLyricsProviderTests : TestBase
|
||||
.FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.KuWo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact(Skip = "酷我音乐的 API 不稳定,可能导致测试失败,暂跳过")]
|
||||
[Trait("LyricsProvider ", "KuGou")]
|
||||
public async Task DownloadAsync_Test()
|
||||
{
|
||||
|
@@ -113,17 +113,11 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyrics
|
||||
lyric.IsPruneMusic.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DownloadAsync_NullException_Test()
|
||||
{
|
||||
var result = await Should.ThrowAsync<ErrorCodeException>(_lyricsProvider.DownloadAsync("創世記", "りりィ").AsTask);
|
||||
result.ErrorCode.ShouldBe(ErrorCodes.NoMatchingSong);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DownloadAsync_Source_Null_Test()
|
||||
{
|
||||
var lyric = await _lyricsProvider.DownloadAsync("Concerto for Piano and Orchestra No. 12 in A major, K414 - 1. Allegro",
|
||||
var lyric = await _lyricsProvider.DownloadAsync(
|
||||
"Concerto for Piano and Orchestra No. 12 in A major, K414 - 1. Allegro",
|
||||
"Wolfgang Amadeus Mozart");
|
||||
|
||||
lyric.IsPruneMusic.ShouldBeTrue();
|
||||
@@ -150,5 +144,12 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyrics
|
||||
var lyric = await _lyricsProvider.DownloadAsync("Your Heart Is A Muscle (2012)", "Carly Rae Jepsen");
|
||||
lyric.IsPruneMusic.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DownloadAsync_Issue_156_Test()
|
||||
{
|
||||
var lyric = await _lyricsProvider.DownloadAsync("你说(demo)", "枯木逢春");
|
||||
lyric.IsPruneMusic.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ using Shouldly;
|
||||
using Xunit;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Common.Infrastructure.Network;
|
||||
using ZonyLrcTools.Common.Updater;
|
||||
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Network
|
||||
{
|
||||
@@ -25,9 +26,9 @@ namespace ZonyLrcTools.Tests.Infrastructure.Network
|
||||
{
|
||||
var client = ServiceProvider.GetRequiredService<IWarpHttpClient>();
|
||||
|
||||
var response = await client.GetAsync(@"https://www.baidu.com");
|
||||
var response = await client.GetAsync(DefaultUpdater.UpdateUrl);
|
||||
response.ShouldNotBeNull();
|
||||
response.ShouldContain("百度");
|
||||
response.ShouldContain("NewVersion");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -39,10 +40,10 @@ namespace ZonyLrcTools.Tests.Infrastructure.Network
|
||||
|
||||
var client = ServiceProvider.GetRequiredService<IWarpHttpClient>();
|
||||
|
||||
var response = await client.GetAsync(@"https://www.baidu.com");
|
||||
var response = await client.GetAsync(DefaultUpdater.UpdateUrl);
|
||||
|
||||
response.ShouldNotBeNull();
|
||||
response.ShouldContain("百度");
|
||||
response.ShouldContain("NewVersion");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using Shouldly;
|
||||
using System.IO;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZonyLrcTools.Common;
|
||||
|
||||
@@ -9,7 +10,10 @@ public class MusicInfoTests
|
||||
[Fact]
|
||||
public void InvalidFilePathTest()
|
||||
{
|
||||
var musicInfo = new MusicInfo("C:\\Users\\Zony\\Music\\[ZonyLrcTools]:? - 01. 你好.mp3", "你好", "Zony");
|
||||
musicInfo.FilePath.ShouldBe(@"C:\Users\Zony\Music\[ZonyLrcTools] - 01. 你好.mp3");
|
||||
var tempFilePath = Path.GetTempFileName();
|
||||
var errorFilePath = $"{tempFilePath}" + "?:";
|
||||
|
||||
var musicInfo = new MusicInfo(errorFilePath, "你好", "Zony");
|
||||
musicInfo.FilePath.ShouldBe(tempFilePath);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
@@ -1,8 +0,0 @@
|
||||
New Features: NONE
|
||||
|
||||
Breaking Changes: None
|
||||
Enhancement:
|
||||
- 支持 UTF-8 BOM 编码,以解决部分设备乱码的问题。
|
||||
|
||||
Fixed Bugs:
|
||||
- None
|
@@ -4,4 +4,4 @@ Enhancement: None
|
||||
|
||||
Fixed Bugs:
|
||||
|
||||
- 修复了酷我音乐下载歌词失败的问题。- [fea5e11](https://github.com/real-zony/ZonyLrcToolsX/commit/fea5e1124ffde5f6cfe0b993e96fdfe04e1b7892)
|
||||
- 修复了 #156 的问题。
|
Reference in New Issue
Block a user