mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2026-02-03 18:08:25 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39dce9a7a0 |
98
.github/workflows/dotnet.yml
vendored
98
.github/workflows/dotnet.yml
vendored
@@ -9,101 +9,55 @@ on:
|
|||||||
branches: [ dev ]
|
branches: [ dev ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Windows 和 Linux 构建(保持在 Ubuntu 上)
|
build:
|
||||||
build-linux-windows:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Get build version
|
- name: Get build version
|
||||||
id: date
|
id: date
|
||||||
run: echo "date=$(date +'%Y%m%d')${{github.run_number}}" >> $GITHUB_OUTPUT
|
run: echo "::set-output name=date::$(date +'%Y%m%d')${{github.run_number}}"
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 10.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Publish Windows and Linux
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
- name: Publish
|
||||||
working-directory: ./src/ZonyLrcTools.Cli
|
working-directory: ./src/ZonyLrcTools.Cli
|
||||||
run: |
|
run: |
|
||||||
for platform in win-x64 linux-x64 win-arm64 linux-arm64; do
|
ls -a
|
||||||
dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:UseAppHost=true -p:DebugType=none --self-contained true
|
chmod +x ./publish.sh
|
||||||
cd ./bin/Release/net10.0/"$platform"/publish/
|
./publish.sh
|
||||||
zip -r ./ZonyLrcTools_"$platform"_"${{ steps.date.outputs.date }}".zip ./
|
mv ./TempFiles ../../
|
||||||
cd ../../../../../
|
|
||||||
mkdir -p ./TempFiles
|
|
||||||
mv ./bin/Release/net10.0/"$platform"/publish/*.zip ./TempFiles/
|
|
||||||
done
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
env:
|
||||||
|
PUBLISH_VERSION: ${{ steps.date.outputs.date }}
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
retention-days: 90
|
retention-days: 90
|
||||||
name: release-files-linux-windows
|
name: release-files
|
||||||
path: ./src/ZonyLrcTools.Cli/TempFiles
|
path: |
|
||||||
|
./TempFiles
|
||||||
|
./versions/release.md
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.date.outputs.date }}
|
version: ${{ steps.date.outputs.date }}
|
||||||
|
|
||||||
# macOS 构建(在 macOS runner 上,支持 ad-hoc 签名)
|
|
||||||
build-macos:
|
|
||||||
runs-on: macos-latest
|
|
||||||
steps:
|
|
||||||
- name: Get build version
|
|
||||||
id: date
|
|
||||||
run: echo "date=$(date +'%Y%m%d')${{github.run_number}}" >> $GITHUB_OUTPUT
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: 10.0.x
|
|
||||||
- name: Publish macOS
|
|
||||||
working-directory: ./src/ZonyLrcTools.Cli
|
|
||||||
run: |
|
|
||||||
for platform in osx-x64 osx-arm64; do
|
|
||||||
dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:DebugType=none --self-contained true
|
|
||||||
|
|
||||||
# Ad-hoc 签名,解决 macOS Gatekeeper/XProtect 问题
|
|
||||||
codesign -s - --force --deep ./bin/Release/net10.0/"$platform"/publish/ZonyLrcTools.Cli
|
|
||||||
|
|
||||||
cd ./bin/Release/net10.0/"$platform"/publish/
|
|
||||||
zip -r ./ZonyLrcTools_"$platform"_"${{ steps.date.outputs.date }}".zip ./
|
|
||||||
cd ../../../../../
|
|
||||||
mkdir -p ./TempFiles
|
|
||||||
mv ./bin/Release/net10.0/"$platform"/publish/*.zip ./TempFiles/
|
|
||||||
done
|
|
||||||
shell: bash
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
retention-days: 90
|
|
||||||
name: release-files-macos
|
|
||||||
path: ./src/ZonyLrcTools.Cli/TempFiles
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.date.outputs.date }}
|
|
||||||
|
|
||||||
# Release Job - 合并所有平台的构建产物
|
|
||||||
release:
|
release:
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
needs: [build-linux-windows, build-macos]
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout for release notes
|
- name: Download artifact
|
||||||
uses: actions/checkout@v4
|
uses: actions/download-artifact@v3
|
||||||
- name: Download Linux/Windows artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: release-files-linux-windows
|
name: release-files
|
||||||
path: ./TempFiles
|
path: .
|
||||||
- name: Download macOS artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: release-files-macos
|
|
||||||
path: ./TempFiles
|
|
||||||
- name: Upload Release
|
- name: Upload Release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
artifacts: "./TempFiles/*.zip"
|
artifacts: "./TempFiles/*.zip"
|
||||||
token: ${{ secrets.GITHUBACTIONS }}
|
token: ${{ secrets.GITHUBACTIONS }}
|
||||||
tag: ZonyLrcToolsX_Alpha.${{ needs.build-linux-windows.outputs.version }}
|
tag: ZonyLrcToolsX_Alpha.${{ needs.build.outputs.version }}
|
||||||
commit: dev
|
commit: dev
|
||||||
bodyFile: ./versions/release.md
|
bodyFile: ./versions/release.md
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>4.0.3</Version>
|
<Version>4.0.2</Version>
|
||||||
<Authors>Zony(real-zony)</Authors>
|
<Authors>Zony(real-zony)</Authors>
|
||||||
<RepositoryUrl>https://github.com/real-zony/ZonyLrcToolsX</RepositoryUrl>
|
<RepositoryUrl>https://github.com/real-zony/ZonyLrcToolsX</RepositoryUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -3,23 +3,28 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageVersion Include="FastEndpoints" Version="7.0.1" />
|
||||||
|
<PackageVersion Include="FastEndpoints.Swagger" Version="7.0.1" />
|
||||||
|
<PackageVersion Include="FreeSql" Version="3.5.211" />
|
||||||
|
<PackageVersion Include="FreeSql.Provider.SqliteCore" Version="3.5.211" />
|
||||||
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
|
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
|
||||||
<PackageVersion Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.1" />
|
<PackageVersion Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.1" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
|
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.7" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.1" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
|
||||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="10.0.0" />
|
<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.Async" Version="2.1.0" />
|
||||||
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
|
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
|
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.1" />
|
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.7" />
|
||||||
<PackageVersion Include="Polly" Version="8.6.5" />
|
<PackageVersion Include="Polly" Version="8.6.2" />
|
||||||
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
|
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
|
||||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageVersion Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
|
<PackageVersion Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
|
||||||
<PackageVersion Include="MusicDecrypto.Library" Version="2.4.1" />
|
<PackageVersion Include="MusicDecrypto.Library" Version="2.4.1" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.1" />
|
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.7" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.1" />
|
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.7" />
|
||||||
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.6" />
|
<PackageVersion Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.6" />
|
||||||
@@ -27,10 +32,10 @@
|
|||||||
<PackageVersion Include="SuperSocket.WebSocket" Version="2.0.0-beta.11" />
|
<PackageVersion Include="SuperSocket.WebSocket" Version="2.0.0-beta.11" />
|
||||||
<PackageVersion Include="SuperSocket.WebSocket.Server" Version="2.0.0-beta.11" />
|
<PackageVersion Include="SuperSocket.WebSocket.Server" Version="2.0.0-beta.11" />
|
||||||
<!-- Testing Projects -->
|
<!-- Testing Projects -->
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5">
|
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageVersion>
|
</PackageVersion>
|
||||||
@@ -38,19 +43,8 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageVersion>
|
</PackageVersion>
|
||||||
<!-- Misc -->
|
<!-- Avalonia -->
|
||||||
<PackageVersion Include="Ude.NetStandard" Version="1.2.0" />
|
<PackageVersion Include="Ude.NetStandard" Version="1.2.0" />
|
||||||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||||
<!-- Avalonia -->
|
|
||||||
<PackageVersion Include="Avalonia" Version="11.2.3" />
|
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.2.3" />
|
|
||||||
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.2.3" />
|
|
||||||
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.2.3" />
|
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.3" />
|
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.3" />
|
|
||||||
<!-- MVVM -->
|
|
||||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
|
||||||
<!-- Localization -->
|
|
||||||
<PackageVersion Include="Microsoft.Extensions.Localization" Version="10.0.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -13,77 +13,44 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Tests", "tests
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Common", "src\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj", "{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Common", "src\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj", "{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Desktop", "src\ZonyLrcTools.Desktop\ZonyLrcTools.Desktop.csproj", "{90718541-0E84-4A2B-8FEF-7210C28A1FE1}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "frontend", "frontend", "{07691E21-94C7-4C9F-970D-B0C77F02B878}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Api", "src\ZonyLrcTools.Api\ZonyLrcTools.Api.csproj", "{698A1F4D-86F1-4251-B708-9B15648D7245}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|Any CPU.Build.0 = Release|Any CPU
|
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|Any CPU.Build.0 = Release|Any CPU
|
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|x86.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.ActiveCfg = Release|Any CPU
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.Build.0 = Release|Any CPU
|
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|x64.ActiveCfg = Release|Any CPU
|
{698A1F4D-86F1-4251-B708-9B15648D7245}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|x64.Build.0 = Release|Any CPU
|
{698A1F4D-86F1-4251-B708-9B15648D7245}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|x86.ActiveCfg = Release|Any CPU
|
{698A1F4D-86F1-4251-B708-9B15648D7245}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|x86.Build.0 = Release|Any CPU
|
{698A1F4D-86F1-4251-B708-9B15648D7245}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {7A6191C3-CC25-4732-885C-F4DD32F9E412}
|
||||||
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||||
{FFBD3200-568F-455B-8390-5E76C51D522C} = {AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}
|
{FFBD3200-568F-455B-8390-5E76C51D522C} = {AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}
|
||||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||||
{90718541-0E84-4A2B-8FEF-7210C28A1FE1} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
{07691E21-94C7-4C9F-970D-B0C77F02B878} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||||
EndGlobalSection
|
{698A1F4D-86F1-4251-B708-9B15648D7245} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {7A6191C3-CC25-4732-885C-F4DD32F9E412}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
19
src/ZonyLrcTools.Api/Database/DatabaseServiceExtensions.cs
Normal file
19
src/ZonyLrcTools.Api/Database/DatabaseServiceExtensions.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using FreeSql;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace ZonyLrcTools.Api.Database;
|
||||||
|
|
||||||
|
public static class DatabaseServiceExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection RegisterDatabaseServices(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
var freeSqlBuilder = new FreeSqlBuilder()
|
||||||
|
.UseConnectionString(DataType.Sqlite, "Data Source=ZonyLrcTools.db;Pooling=true;Max Pool Size=10;")
|
||||||
|
.UseAutoSyncStructure(true);
|
||||||
|
|
||||||
|
var freeSql = freeSqlBuilder.Build();
|
||||||
|
services.AddSingleton(freeSql);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/ZonyLrcTools.Api/Domain/Settings/Setting.cs
Normal file
30
src/ZonyLrcTools.Api/Domain/Settings/Setting.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using FreeSql.DataAnnotations;
|
||||||
|
|
||||||
|
namespace ZonyLrcTools.Api.Domain.Settings;
|
||||||
|
|
||||||
|
[Table(Name = "Settings")]
|
||||||
|
public class Setting
|
||||||
|
{
|
||||||
|
[Column(IsPrimary = true, IsIdentity = true)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
public string Key { get; protected set; } = null!;
|
||||||
|
|
||||||
|
public string? Value { get; set; }
|
||||||
|
|
||||||
|
public SettingValueType ValueType { get; protected set; }
|
||||||
|
|
||||||
|
public Setting(string key, SettingValueType valueType)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
ValueType = valueType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SettingValueType
|
||||||
|
{
|
||||||
|
String = 1,
|
||||||
|
Int = 2,
|
||||||
|
Bool = 3,
|
||||||
|
Double = 4
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ZonyLrcTools.Api.Endpoints.Settings.DTOs;
|
||||||
|
|
||||||
|
public class GetAllSettingsResponse
|
||||||
|
{
|
||||||
|
public bool IsAutoCheckUpdate { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using ZonyLrcTools.Api.Domain.Settings;
|
||||||
|
using ZonyLrcTools.Api.Endpoints.Settings.DTOs;
|
||||||
|
|
||||||
|
namespace ZonyLrcTools.Api.Endpoints.Settings;
|
||||||
|
|
||||||
|
public class GetAllSettingsEndpoint(IFreeSql freeSql) : EndpointWithoutRequest<GetAllSettingsResponse>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Get("/api/settings");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
var setting = await freeSql.Select<Setting>()
|
||||||
|
.ToListAsync(ct);
|
||||||
|
|
||||||
|
if (setting.Count == 0)
|
||||||
|
{
|
||||||
|
await Send.NotFoundAsync(ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Send.OkAsync(new GetAllSettingsResponse
|
||||||
|
{
|
||||||
|
IsAutoCheckUpdate = true
|
||||||
|
}, ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/ZonyLrcTools.Api/Program.cs
Normal file
23
src/ZonyLrcTools.Api/Program.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using ZonyLrcTools.Api.Database;
|
||||||
|
|
||||||
|
namespace ZonyLrcTools.Api;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.WebHost.ConfigureKestrel(k => k.ListenAnyIP(10086));
|
||||||
|
|
||||||
|
builder.Services.AddFastEndpoints();
|
||||||
|
builder.Services.RegisterDatabaseServices();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseFastEndpoints();
|
||||||
|
await app.RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/ZonyLrcTools.Api/ZonyLrcTools.Api.csproj
Normal file
18
src/ZonyLrcTools.Api/ZonyLrcTools.Api.csproj
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FastEndpoints" />
|
||||||
|
<PackageReference Include="FastEndpoints.Swagger" />
|
||||||
|
<PackageReference Include="FreeSql" />
|
||||||
|
<PackageReference Include="FreeSql.Provider.SqliteCore" />
|
||||||
|
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Cli.Infrastructure.Localization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides localization services for CLI commands and options.
|
|
||||||
/// </summary>
|
|
||||||
public interface ICliLocalizationService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a localized string by key.
|
|
||||||
/// </summary>
|
|
||||||
string this[string key] { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implementation of CLI localization service.
|
|
||||||
/// </summary>
|
|
||||||
public class CliLocalizationService : ICliLocalizationService, ISingletonDependency
|
|
||||||
{
|
|
||||||
private readonly IStringLocalizer<Cli.CommandStrings> _localizer;
|
|
||||||
|
|
||||||
public CliLocalizationService(IStringLocalizer<Cli.CommandStrings> localizer)
|
|
||||||
{
|
|
||||||
_localizer = localizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var value = _localizer[key];
|
|
||||||
return value.ResourceNotFound ? $"[{key}]" : value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -48,7 +48,7 @@ namespace ZonyLrcTools.Cli
|
|||||||
|
|
||||||
#region > 程序初始化配置 <
|
#region > 程序初始化配置 <
|
||||||
|
|
||||||
private static void ConfigureErrorMessage() => ErrorCodeHelperStatic.LoadErrorMessage();
|
private static void ConfigureErrorMessage() => ErrorCodeHelper.LoadErrorMessage();
|
||||||
|
|
||||||
private static void ConfigureLogger()
|
private static void ConfigureLogger()
|
||||||
{
|
{
|
||||||
@@ -90,7 +90,6 @@ namespace ZonyLrcTools.Cli
|
|||||||
services.BeginAutoDependencyInject<Program>();
|
services.BeginAutoDependencyInject<Program>();
|
||||||
services.BeginAutoDependencyInject<IWarpHttpClient>();
|
services.BeginAutoDependencyInject<IWarpHttpClient>();
|
||||||
services.ConfigureConfiguration();
|
services.ConfigureConfiguration();
|
||||||
services.ConfigureLocalization();
|
|
||||||
services.ConfigureToolService();
|
services.ConfigureToolService();
|
||||||
services.AddHostedService<UpdaterHostedService>();
|
services.AddHostedService<UpdaterHostedService>();
|
||||||
})
|
})
|
||||||
@@ -103,7 +102,7 @@ namespace ZonyLrcTools.Cli
|
|||||||
{
|
{
|
||||||
case ErrorCodeException exception:
|
case ErrorCodeException exception:
|
||||||
Log.Logger.Error(
|
Log.Logger.Error(
|
||||||
$"出现了未处理的异常。\n错误代码: {exception.ErrorCode}\n错误信息: {ErrorCodeHelperStatic.GetMessage(exception.ErrorCode)}\n原始信息:{exception.Message}\n调用栈:{exception.StackTrace}");
|
$"出现了未处理的异常。\n错误代码: {exception.ErrorCode}\n错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n原始信息:{exception.Message}\n调用栈:{exception.StackTrace}");
|
||||||
Environment.Exit(exception.ErrorCode);
|
Environment.Exit(exception.ErrorCode);
|
||||||
return exception.ErrorCode;
|
return exception.ErrorCode;
|
||||||
case { } unknownException:
|
case { } unknownException:
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace ZonyLrcTools.Cli;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Marker class for CLI command localization resources.
|
|
||||||
/// This class is used by IStringLocalizer<CommandStrings> to locate the resource files.
|
|
||||||
/// The namespace must be the root namespace so that IStringLocalizer looks for Resources/CommandStrings.resx
|
|
||||||
/// </summary>
|
|
||||||
public class CommandStrings
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Main Program -->
|
|
||||||
<data name="Program_Description" xml:space="preserve">
|
|
||||||
<value>ZonyLrcTools - Lyrics download command-line tool</value>
|
|
||||||
</data>
|
|
||||||
<!-- Download Command -->
|
|
||||||
<data name="Download_Description" xml:space="preserve">
|
|
||||||
<value>Download lyrics or album covers</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_Dir_Description" xml:space="preserve">
|
|
||||||
<value>Specify the folder path containing music files</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_Lyrics_Description" xml:space="preserve">
|
|
||||||
<value>Download lyrics files</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_Album_Description" xml:space="preserve">
|
|
||||||
<value>Download album covers</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_CSV_Description" xml:space="preserve">
|
|
||||||
<value>Import song list from CSV file for download</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_SongList_Description" xml:space="preserve">
|
|
||||||
<value>Import song list from NetEase playlist ID for download</value>
|
|
||||||
</data>
|
|
||||||
<!-- Utility Command -->
|
|
||||||
<data name="Utility_Description" xml:space="preserve">
|
|
||||||
<value>Utility commands</value>
|
|
||||||
</data>
|
|
||||||
<data name="Utility_Convert_Description" xml:space="preserve">
|
|
||||||
<value>Convert encrypted music files to normal format</value>
|
|
||||||
</data>
|
|
||||||
<data name="Utility_Source_Description" xml:space="preserve">
|
|
||||||
<value>Specify the folder path to convert</value>
|
|
||||||
</data>
|
|
||||||
<!-- Search Command -->
|
|
||||||
<data name="Search_Description" xml:space="preserve">
|
|
||||||
<value>Search for lyrics</value>
|
|
||||||
</data>
|
|
||||||
<data name="Search_Name_Description" xml:space="preserve">
|
|
||||||
<value>Song name</value>
|
|
||||||
</data>
|
|
||||||
<data name="Search_Artist_Description" xml:space="preserve">
|
|
||||||
<value>Artist name</value>
|
|
||||||
</data>
|
|
||||||
<!-- Common Options -->
|
|
||||||
<data name="Option_Help" xml:space="preserve">
|
|
||||||
<value>Show help information</value>
|
|
||||||
</data>
|
|
||||||
<data name="Option_Version" xml:space="preserve">
|
|
||||||
<value>Show version information</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Main Program -->
|
|
||||||
<data name="Program_Description" xml:space="preserve">
|
|
||||||
<value>ZonyLrcTools - 歌词下载命令行工具</value>
|
|
||||||
</data>
|
|
||||||
<!-- Download Command -->
|
|
||||||
<data name="Download_Description" xml:space="preserve">
|
|
||||||
<value>下载歌词或专辑封面</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_Dir_Description" xml:space="preserve">
|
|
||||||
<value>指定音乐文件所在的文件夹路径</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_Lyrics_Description" xml:space="preserve">
|
|
||||||
<value>下载歌词文件</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_Album_Description" xml:space="preserve">
|
|
||||||
<value>下载专辑封面</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_CSV_Description" xml:space="preserve">
|
|
||||||
<value>从 CSV 文件导入歌曲列表进行下载</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download_SongList_Description" xml:space="preserve">
|
|
||||||
<value>从网易云歌单 ID 导入歌曲列表进行下载</value>
|
|
||||||
</data>
|
|
||||||
<!-- Utility Command -->
|
|
||||||
<data name="Utility_Description" xml:space="preserve">
|
|
||||||
<value>工具类命令</value>
|
|
||||||
</data>
|
|
||||||
<data name="Utility_Convert_Description" xml:space="preserve">
|
|
||||||
<value>转换加密音乐文件为普通格式</value>
|
|
||||||
</data>
|
|
||||||
<data name="Utility_Source_Description" xml:space="preserve">
|
|
||||||
<value>指定需要转换的文件夹路径</value>
|
|
||||||
</data>
|
|
||||||
<!-- Search Command -->
|
|
||||||
<data name="Search_Description" xml:space="preserve">
|
|
||||||
<value>搜索歌词</value>
|
|
||||||
</data>
|
|
||||||
<data name="Search_Name_Description" xml:space="preserve">
|
|
||||||
<value>歌曲名称</value>
|
|
||||||
</data>
|
|
||||||
<data name="Search_Artist_Description" xml:space="preserve">
|
|
||||||
<value>艺术家名称</value>
|
|
||||||
</data>
|
|
||||||
<!-- Common Options -->
|
|
||||||
<data name="Option_Help" xml:space="preserve">
|
|
||||||
<value>显示帮助信息</value>
|
|
||||||
</data>
|
|
||||||
<data name="Option_Version" xml:space="preserve">
|
|
||||||
<value>显示版本信息</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils"/>
|
<PackageReference Include="McMaster.Extensions.CommandLineUtils"/>
|
||||||
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine"/>
|
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting"/>
|
<PackageReference Include="Microsoft.Extensions.Hosting"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization"/>
|
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting"/>
|
<PackageReference Include="Serilog.Extensions.Hosting"/>
|
||||||
<PackageReference Include="Serilog.Sinks.Async"/>
|
<PackageReference Include="Serilog.Sinks.Async"/>
|
||||||
<PackageReference Include="Serilog.Sinks.Console"/>
|
<PackageReference Include="Serilog.Sinks.Console"/>
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ for platform in "${Platforms[@]}"
|
|||||||
do
|
do
|
||||||
dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:DebugType=none --self-contained true || exit 1
|
dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:DebugType=none --self-contained true || exit 1
|
||||||
|
|
||||||
cd ./bin/Release/net10.0/"$platform"/publish/ || exit 1
|
cd ./bin/Release/net8.0/"$platform"/publish/ || exit 1
|
||||||
zip -r ./ZonyLrcTools_"$platform"_"${PUBLISH_VERSION}".zip ./ || exit 1
|
zip -r ./ZonyLrcTools_"$platform"_"${PUBLISH_VERSION}".zip ./ || exit 1
|
||||||
cd ../../../../../ || exit 1
|
cd ../../../../../ || exit 1
|
||||||
|
|
||||||
mv ./bin/Release/net10.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$PUBLISH_VERSION".zip ./TempFiles
|
mv ./bin/Release/net8.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$PUBLISH_VERSION".zip ./TempFiles
|
||||||
done
|
done
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -52,23 +51,5 @@ namespace ZonyLrcTools.Common.Infrastructure.DependencyInject
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 配置本地化服务。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="services">服务集合。</param>
|
|
||||||
/// <param name="defaultCulture">默认语言,默认为 zh-CN。</param>
|
|
||||||
public static IServiceCollection ConfigureLocalization(this IServiceCollection services, string defaultCulture = "zh-CN")
|
|
||||||
{
|
|
||||||
// Note: Don't set ResourcesPath because the embedded resource names are based on
|
|
||||||
// the marker class namespace (e.g., ZonyLrcTools.Common.Messages), not folder path
|
|
||||||
services.AddLocalization();
|
|
||||||
|
|
||||||
var culture = new CultureInfo(defaultCulture);
|
|
||||||
CultureInfo.DefaultThreadCurrentCulture = culture;
|
|
||||||
CultureInfo.DefaultThreadCurrentUICulture = culture;
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,132 +1,41 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
|
||||||
using ZonyLrcTools.Common.Infrastructure.Localization;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Common.Infrastructure.Exceptions;
|
namespace ZonyLrcTools.Common.Infrastructure.Exceptions
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 错误码相关的帮助类,支持国际化。
|
|
||||||
/// </summary>
|
|
||||||
public class ErrorCodeHelper : IErrorCodeHelper, ISingletonDependency
|
|
||||||
{
|
{
|
||||||
private readonly ILocalizationService _localizationService;
|
|
||||||
private readonly Dictionary<int, string> _fallbackMessages;
|
|
||||||
|
|
||||||
public ErrorCodeHelper(ILocalizationService localizationService)
|
|
||||||
{
|
|
||||||
_localizationService = localizationService;
|
|
||||||
_fallbackMessages = new Dictionary<int, string>();
|
|
||||||
LoadFallbackMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetMessage(int errorCode)
|
|
||||||
{
|
|
||||||
var localizedMessage = _localizationService.GetErrorMessage(errorCode);
|
|
||||||
|
|
||||||
// 如果本地化消息不是默认的 "Unknown error" 格式,则返回本地化消息
|
|
||||||
if (!localizedMessage.StartsWith("Unknown error:"))
|
|
||||||
{
|
|
||||||
return localizedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回退到 JSON 文件的消息
|
|
||||||
return _fallbackMessages.TryGetValue(errorCode, out var message)
|
|
||||||
? message
|
|
||||||
: $"未知错误: {errorCode}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetWarningMessage(int warningCode)
|
|
||||||
{
|
|
||||||
var localizedMessage = _localizationService.GetWarningMessage(warningCode);
|
|
||||||
|
|
||||||
// 如果本地化消息不是默认的 "Unknown warning" 格式,则返回本地化消息
|
|
||||||
if (!localizedMessage.StartsWith("Unknown warning:"))
|
|
||||||
{
|
|
||||||
return localizedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回退到 JSON 文件的消息
|
|
||||||
return _fallbackMessages.TryGetValue(warningCode, out var message)
|
|
||||||
? message
|
|
||||||
: $"未知警告: {warningCode}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从 error_msg.json 加载回退消息(用于兼容旧系统)。
|
/// 错误码相关的帮助类。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void LoadFallbackMessages()
|
public static class ErrorCodeHelper
|
||||||
{
|
{
|
||||||
try
|
public static Dictionary<int, string> ErrorMessages { get; }
|
||||||
|
|
||||||
|
static ErrorCodeHelper()
|
||||||
{
|
{
|
||||||
var jsonPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "error_msg.json");
|
ErrorMessages = new Dictionary<int, string>();
|
||||||
if (!File.Exists(jsonPath))
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 err_msg.json 文件加载错误信息。
|
||||||
|
/// </summary>
|
||||||
|
public static void LoadErrorMessage()
|
||||||
|
{
|
||||||
|
// 防止重复加载。
|
||||||
|
if (ErrorMessages.Count != 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var jsonPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "error_msg.json");
|
||||||
using var jsonReader = new JsonTextReader(File.OpenText(jsonPath));
|
using var jsonReader = new JsonTextReader(File.OpenText(jsonPath));
|
||||||
var jsonObj = JObject.Load(jsonReader);
|
var jsonObj = JObject.Load(jsonReader);
|
||||||
|
|
||||||
var errors = jsonObj.SelectTokens("$.Error.*");
|
var errors = jsonObj.SelectTokens("$.Error.*");
|
||||||
var warnings = jsonObj.SelectTokens("$.Warning.*");
|
var warnings = jsonObj.SelectTokens("$.Warning.*");
|
||||||
errors.Union(warnings).Select(m => m.Parent).OfType<JProperty>().ToList()
|
errors.Union(warnings).Select(m => m.Parent).OfType<JProperty>().ToList()
|
||||||
.ForEach(m => _fallbackMessages[int.Parse(m.Name)] = m.Value.Value<string>() ?? string.Empty);
|
.ForEach(m => ErrorMessages.Add(int.Parse(m.Name), m.Value.Value<string>() ?? string.Empty));
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// 忽略加载失败,使用本地化消息
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetMessage(int errorCode) => ErrorMessages[errorCode];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 静态错误码帮助类(用于不支持 DI 的场景)。
|
|
||||||
/// </summary>
|
|
||||||
public static class ErrorCodeHelperStatic
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<int, string> ErrorMessages = new();
|
|
||||||
private static bool _isLoaded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从 error_msg.json 文件加载错误信息。
|
|
||||||
/// </summary>
|
|
||||||
public static void LoadErrorMessage()
|
|
||||||
{
|
|
||||||
if (_isLoaded)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var jsonPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "error_msg.json");
|
|
||||||
if (!File.Exists(jsonPath))
|
|
||||||
{
|
|
||||||
_isLoaded = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var jsonReader = new JsonTextReader(File.OpenText(jsonPath));
|
|
||||||
var jsonObj = JObject.Load(jsonReader);
|
|
||||||
|
|
||||||
var errors = jsonObj.SelectTokens("$.Error.*");
|
|
||||||
var warnings = jsonObj.SelectTokens("$.Warning.*");
|
|
||||||
errors.Union(warnings).Select(m => m.Parent).OfType<JProperty>().ToList()
|
|
||||||
.ForEach(m => ErrorMessages[int.Parse(m.Name)] = m.Value.Value<string>() ?? string.Empty);
|
|
||||||
|
|
||||||
_isLoaded = true;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_isLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetMessage(int errorCode)
|
|
||||||
{
|
|
||||||
return ErrorMessages.TryGetValue(errorCode, out var message)
|
|
||||||
? message
|
|
||||||
: $"未知错误: {errorCode}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
namespace ZonyLrcTools.Common.Infrastructure.Exceptions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 错误码帮助类接口。
|
|
||||||
/// </summary>
|
|
||||||
public interface IErrorCodeHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取错误消息。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="errorCode">错误代码。</param>
|
|
||||||
/// <returns>对应的错误消息。</returns>
|
|
||||||
string GetMessage(int errorCode);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取警告消息。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="warningCode">警告代码。</param>
|
|
||||||
/// <returns>对应的警告消息。</returns>
|
|
||||||
string GetWarningMessage(int warningCode);
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,7 @@ namespace ZonyLrcTools.Common.Infrastructure.Extensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append($"错误代码: {exception.ErrorCode},信息: {ErrorCodeHelperStatic.GetMessage(exception.ErrorCode)}");
|
sb.Append($"错误代码: {exception.ErrorCode},信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}");
|
||||||
sb.Append($"\n附加信息:\n {JsonConvert.SerializeObject(exception.AttachObject)}");
|
sb.Append($"\n附加信息:\n {JsonConvert.SerializeObject(exception.AttachObject)}");
|
||||||
logger.WarnAsync(sb.ToString()).GetAwaiter().GetResult();
|
logger.WarnAsync(sb.ToString()).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Common.Infrastructure.Localization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides localization services for the application.
|
|
||||||
/// </summary>
|
|
||||||
public interface ILocalizationService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a localized string by key.
|
|
||||||
/// </summary>
|
|
||||||
string this[string key] { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a localized string by key with format arguments.
|
|
||||||
/// </summary>
|
|
||||||
string this[string key, params object[] args] { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an error message by error code.
|
|
||||||
/// </summary>
|
|
||||||
string GetErrorMessage(int errorCode);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a warning message by warning code.
|
|
||||||
/// </summary>
|
|
||||||
string GetWarningMessage(int warningCode);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the current culture.
|
|
||||||
/// </summary>
|
|
||||||
void SetCulture(string cultureName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current culture name.
|
|
||||||
/// </summary>
|
|
||||||
string CurrentCulture { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the list of supported cultures.
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyList<CultureInfo> SupportedCultures { get; }
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Common.Infrastructure.Localization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implementation of <see cref="ILocalizationService"/> using .NET localization.
|
|
||||||
/// </summary>
|
|
||||||
public class LocalizationService : ILocalizationService, ISingletonDependency
|
|
||||||
{
|
|
||||||
private readonly IStringLocalizer<Common.Messages> _messagesLocalizer;
|
|
||||||
private readonly IStringLocalizer<Common.ErrorMessages> _errorLocalizer;
|
|
||||||
|
|
||||||
private static readonly CultureInfo[] _supportedCultures =
|
|
||||||
{
|
|
||||||
new("zh-CN"),
|
|
||||||
new("en-US")
|
|
||||||
};
|
|
||||||
|
|
||||||
public LocalizationService(
|
|
||||||
IStringLocalizer<Common.Messages> messagesLocalizer,
|
|
||||||
IStringLocalizer<Common.ErrorMessages> errorLocalizer)
|
|
||||||
{
|
|
||||||
_messagesLocalizer = messagesLocalizer;
|
|
||||||
_errorLocalizer = errorLocalizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var value = _messagesLocalizer[key];
|
|
||||||
return value.ResourceNotFound ? $"[{key}]" : value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string key, params object[] args]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var value = _messagesLocalizer[key];
|
|
||||||
if (value.ResourceNotFound) return $"[{key}]";
|
|
||||||
return string.Format(value.Value, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetErrorMessage(int errorCode)
|
|
||||||
{
|
|
||||||
var key = $"Error_{errorCode}";
|
|
||||||
var value = _errorLocalizer[key];
|
|
||||||
return value.ResourceNotFound ? $"Unknown error: {errorCode}" : value.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetWarningMessage(int warningCode)
|
|
||||||
{
|
|
||||||
var key = $"Warning_{warningCode}";
|
|
||||||
var value = _errorLocalizer[key];
|
|
||||||
return value.ResourceNotFound ? $"Unknown warning: {warningCode}" : value.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CurrentCulture => CultureInfo.CurrentUICulture.Name;
|
|
||||||
|
|
||||||
public IReadOnlyList<CultureInfo> SupportedCultures => _supportedCultures;
|
|
||||||
|
|
||||||
public void SetCulture(string cultureName)
|
|
||||||
{
|
|
||||||
var culture = new CultureInfo(cultureName);
|
|
||||||
CultureInfo.CurrentCulture = culture;
|
|
||||||
CultureInfo.CurrentUICulture = culture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace ZonyLrcTools.Common;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Marker class for error message localization resources.
|
|
||||||
/// This class is used by IStringLocalizer<ErrorMessages> to locate the resource files.
|
|
||||||
/// The namespace must be the root namespace so that IStringLocalizer looks for Resources/ErrorMessages.resx
|
|
||||||
/// </summary>
|
|
||||||
public class ErrorMessages
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Error Messages -->
|
|
||||||
<data name="Error_10001" xml:space="preserve">
|
|
||||||
<value>The file extension to search cannot be empty.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10002" xml:space="preserve">
|
|
||||||
<value>The directory to scan does not exist, please verify the path.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10003" xml:space="preserve">
|
|
||||||
<value>Unable to get the file extension information.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10004" xml:space="preserve">
|
|
||||||
<value>No music files were found.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10005" xml:space="preserve">
|
|
||||||
<value>The specified encoding is not supported, please check the configuration.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10006" xml:space="preserve">
|
|
||||||
<value>Unable to get the song list from NetEase Music.</value>
|
|
||||||
</data>
|
|
||||||
<!-- Warning Messages -->
|
|
||||||
<data name="Warning_50001" xml:space="preserve">
|
|
||||||
<value>An error occurred while scanning files.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50002" xml:space="preserve">
|
|
||||||
<value>Both song name and artist name are empty, unable to search.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50003" xml:space="preserve">
|
|
||||||
<value>Song name cannot be empty, unable to search.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50004" xml:space="preserve">
|
|
||||||
<value>The downloader did not find the corresponding song information.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50005" xml:space="preserve">
|
|
||||||
<value>The download request returned an invalid response, possibly a server error.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50006" xml:space="preserve">
|
|
||||||
<value>Tag info reader is empty, unable to parse music tag information.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50007" xml:space="preserve">
|
|
||||||
<value>TagLib tag reader encountered an unexpected exception.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50008" xml:space="preserve">
|
|
||||||
<value>Service interface restricted, unable to make request, please try using a proxy server.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50009" xml:space="preserve">
|
|
||||||
<value>HTTP request to target server failed.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50010" xml:space="preserve">
|
|
||||||
<value>Failed to deserialize HTTP response to JSON.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50011" xml:space="preserve">
|
|
||||||
<value>Currently only NCM format song conversion is supported.</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Error Messages -->
|
|
||||||
<data name="Error_10001" xml:space="preserve">
|
|
||||||
<value>待搜索的后缀不能为空。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10002" xml:space="preserve">
|
|
||||||
<value>需要扫描的目录不存在,请确认路径是否正确。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10003" xml:space="preserve">
|
|
||||||
<value>不能获取文件的后缀信息。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10004" xml:space="preserve">
|
|
||||||
<value>没有扫描到任何音乐文件。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10005" xml:space="preserve">
|
|
||||||
<value>指定的编码不受支持,请检查配置。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error_10006" xml:space="preserve">
|
|
||||||
<value>无法从网易云音乐获取歌曲列表。</value>
|
|
||||||
</data>
|
|
||||||
<!-- Warning Messages -->
|
|
||||||
<data name="Warning_50001" xml:space="preserve">
|
|
||||||
<value>扫描文件时出现了错误。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50002" xml:space="preserve">
|
|
||||||
<value>歌曲名称或歌手名称均为空,无法进行搜索。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50003" xml:space="preserve">
|
|
||||||
<value>歌曲名称不能为空,无法进行搜索。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50004" xml:space="preserve">
|
|
||||||
<value>下载器没有搜索到对应的歌曲信息。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50005" xml:space="preserve">
|
|
||||||
<value>下载请求的返回值不合法,可能是服务端故障。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50006" xml:space="preserve">
|
|
||||||
<value>标签信息读取器为空,无法解析音乐 Tag 信息。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50007" xml:space="preserve">
|
|
||||||
<value>TagLib 标签读取器出现了预期之外的异常。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50008" xml:space="preserve">
|
|
||||||
<value>服务接口限制,无法进行请求,请尝试使用代理服务器。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50009" xml:space="preserve">
|
|
||||||
<value>对目标服务器执行 HTTP 请求失败。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50010" xml:space="preserve">
|
|
||||||
<value>HTTP 请求的结果反序列化为 JSON 失败。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Warning_50011" xml:space="preserve">
|
|
||||||
<value>目前仅支持 NCM 格式的歌曲转换操作。</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace ZonyLrcTools.Common;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Marker class for general message localization resources.
|
|
||||||
/// This class is used by IStringLocalizer<Messages> to locate the resource files.
|
|
||||||
/// The namespace must be the root namespace so that IStringLocalizer looks for Resources/Messages.resx
|
|
||||||
/// </summary>
|
|
||||||
public class Messages
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Lyrics Download -->
|
|
||||||
<data name="Lyrics_DownloadStart" xml:space="preserve">
|
|
||||||
<value>Starting lyrics file download...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_DownloadComplete" xml:space="preserve">
|
|
||||||
<value>Lyrics download complete, success: {0} skipped (instrumental): {1} failed: {2}.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_SongDownloadSuccess" xml:space="preserve">
|
|
||||||
<value>Song: {0}, Artist: {1}, download successful.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_SongDownloadFailed" xml:space="preserve">
|
|
||||||
<value>Song: {0}, Artist: {1}, download failed.</value>
|
|
||||||
</data>
|
|
||||||
<!-- Album Download -->
|
|
||||||
<data name="Album_DownloadStart" xml:space="preserve">
|
|
||||||
<value>Starting album cover download...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_DownloadComplete" xml:space="preserve">
|
|
||||||
<value>Album cover download complete, success: {0} failed: {1}.</value>
|
|
||||||
</data>
|
|
||||||
<!-- File Scanner -->
|
|
||||||
<data name="Scanner_Start" xml:space="preserve">
|
|
||||||
<value>Starting folder scan, please wait...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Scanner_Complete" xml:space="preserve">
|
|
||||||
<value>Scan complete, {0} files found.</value>
|
|
||||||
</data>
|
|
||||||
<!-- General -->
|
|
||||||
<data name="General_Starting" xml:space="preserve">
|
|
||||||
<value>Starting...</value>
|
|
||||||
</data>
|
|
||||||
<data name="General_UnhandledException" xml:space="preserve">
|
|
||||||
<value>An unhandled exception occurred.</value>
|
|
||||||
</data>
|
|
||||||
<data name="General_ErrorCode" xml:space="preserve">
|
|
||||||
<value>Error code: {0}</value>
|
|
||||||
</data>
|
|
||||||
<data name="General_ErrorMessage" xml:space="preserve">
|
|
||||||
<value>Error message: {0}</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Lyrics Download -->
|
|
||||||
<data name="Lyrics_DownloadStart" xml:space="preserve">
|
|
||||||
<value>开始下载歌词文件数据...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_DownloadComplete" xml:space="preserve">
|
|
||||||
<value>歌词数据下载完成,成功: {0} 跳过(纯音乐): {1} 失败: {2}。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_SongDownloadSuccess" xml:space="preserve">
|
|
||||||
<value>歌曲名: {0}, 艺术家: {1}, 下载成功。</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_SongDownloadFailed" xml:space="preserve">
|
|
||||||
<value>歌曲名: {0}, 艺术家: {1}, 下载失败。</value>
|
|
||||||
</data>
|
|
||||||
<!-- Album Download -->
|
|
||||||
<data name="Album_DownloadStart" xml:space="preserve">
|
|
||||||
<value>开始下载专辑封面...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_DownloadComplete" xml:space="preserve">
|
|
||||||
<value>专辑封面下载完成,成功: {0} 失败: {1}。</value>
|
|
||||||
</data>
|
|
||||||
<!-- File Scanner -->
|
|
||||||
<data name="Scanner_Start" xml:space="preserve">
|
|
||||||
<value>开始扫描文件夹,请稍等...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Scanner_Complete" xml:space="preserve">
|
|
||||||
<value>扫描完成,共 {0} 个文件。</value>
|
|
||||||
</data>
|
|
||||||
<!-- General -->
|
|
||||||
<data name="General_Starting" xml:space="preserve">
|
|
||||||
<value>正在启动...</value>
|
|
||||||
</data>
|
|
||||||
<data name="General_UnhandledException" xml:space="preserve">
|
|
||||||
<value>出现了未处理的异常。</value>
|
|
||||||
</data>
|
|
||||||
<data name="General_ErrorCode" xml:space="preserve">
|
|
||||||
<value>错误代码: {0}</value>
|
|
||||||
</data>
|
|
||||||
<data name="General_ErrorMessage" xml:space="preserve">
|
|
||||||
<value>错误信息: {0}</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Http"/>
|
<PackageReference Include="Microsoft.Extensions.Http"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization"/>
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
|
||||||
<PackageReference Include="MusicDecrypto.Library"/>
|
<PackageReference Include="MusicDecrypto.Library"/>
|
||||||
<PackageReference Include="Newtonsoft.Json"/>
|
<PackageReference Include="Newtonsoft.Json"/>
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
<Application xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
x:Class="ZonyLrcTools.Desktop.App"
|
|
||||||
RequestedThemeVariant="Default">
|
|
||||||
|
|
||||||
<Application.Styles>
|
|
||||||
<FluentTheme />
|
|
||||||
</Application.Styles>
|
|
||||||
|
|
||||||
<Application.Resources>
|
|
||||||
<ResourceDictionary>
|
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
|
||||||
<ResourceDictionary x:Key="Light">
|
|
||||||
<SolidColorBrush x:Key="AppBackgroundBrush">#FFFFFF</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="AppAccentBrush">#0078D4</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="NavigationBackgroundBrush">#F3F3F3</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="SuccessColorBrush">#107C10</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="ErrorColorBrush">#D13438</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="WarningColorBrush">#FFB900</SolidColorBrush>
|
|
||||||
</ResourceDictionary>
|
|
||||||
<ResourceDictionary x:Key="Dark">
|
|
||||||
<SolidColorBrush x:Key="AppBackgroundBrush">#1F1F1F</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="AppAccentBrush">#60CDFF</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="NavigationBackgroundBrush">#2D2D2D</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="SuccessColorBrush">#6CCB5F</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="ErrorColorBrush">#FF99A4</SolidColorBrush>
|
|
||||||
<SolidColorBrush x:Key="WarningColorBrush">#FCE100</SolidColorBrush>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</ResourceDictionary.ThemeDictionaries>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</Application.Resources>
|
|
||||||
|
|
||||||
</Application>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
|
||||||
using Avalonia.Data.Core.Plugins;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using ZonyLrcTools.Desktop.Services;
|
|
||||||
using ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
using ZonyLrcTools.Desktop.Views;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop;
|
|
||||||
|
|
||||||
public partial class App : Application
|
|
||||||
{
|
|
||||||
public static ServiceProvider? Services { get; private set; }
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
// Configure DI container BEFORE loading XAML
|
|
||||||
// This is required because LocalizeExtension needs App.Services during XAML loading
|
|
||||||
var services = new ServiceCollection();
|
|
||||||
services.AddDesktopServices();
|
|
||||||
Services = services.BuildServiceProvider();
|
|
||||||
|
|
||||||
// Initialize language settings before loading XAML
|
|
||||||
ServiceCollectionExtensions.InitializeLanguage();
|
|
||||||
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
|
||||||
{
|
|
||||||
// Avoid duplicate validations from both Avalonia and CommunityToolkit
|
|
||||||
BindingPlugins.DataValidators.RemoveAt(0);
|
|
||||||
|
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
{
|
|
||||||
var mainViewModel = Services!.GetRequiredService<MainWindowViewModel>();
|
|
||||||
|
|
||||||
desktop.MainWindow = new MainWindow
|
|
||||||
{
|
|
||||||
DataContext = mainViewModel
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnFrameworkInitializationCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
using Avalonia.Data;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Markup.Xaml.MarkupExtensions;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Infrastructure.Localization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Avalonia markup extension for localization.
|
|
||||||
/// Usage: Text="{loc:Localize Key=Nav_Home}"
|
|
||||||
/// </summary>
|
|
||||||
public class LocalizeExtension : MarkupExtension
|
|
||||||
{
|
|
||||||
public LocalizeExtension()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalizeExtension(string key)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The resource key to look up.
|
|
||||||
/// </summary>
|
|
||||||
public string? Key { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value if the key is not found.
|
|
||||||
/// </summary>
|
|
||||||
public string? Default { get; set; }
|
|
||||||
|
|
||||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(Key))
|
|
||||||
{
|
|
||||||
return Default ?? "[NO_KEY]";
|
|
||||||
}
|
|
||||||
|
|
||||||
var localizer = App.Services?.GetService<IStringLocalizer<Desktop.UIStrings>>();
|
|
||||||
if (localizer == null)
|
|
||||||
{
|
|
||||||
return Default ?? $"[{Key}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
var localizedValue = localizer[Key];
|
|
||||||
if (localizedValue.ResourceNotFound)
|
|
||||||
{
|
|
||||||
return Default ?? $"[{Key}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return localizedValue.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UI localization service for dynamic language switching.
|
|
||||||
/// </summary>
|
|
||||||
public interface IUILocalizationService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a localized string by key.
|
|
||||||
/// </summary>
|
|
||||||
string this[string key] { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a localized string by key with format arguments.
|
|
||||||
/// </summary>
|
|
||||||
string this[string key, params object[] args] { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current culture name.
|
|
||||||
/// </summary>
|
|
||||||
string CurrentCulture { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the current UI language.
|
|
||||||
/// </summary>
|
|
||||||
void SetLanguage(string cultureName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether the language follows system settings.
|
|
||||||
/// </summary>
|
|
||||||
bool FollowSystem { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event raised when the language changes.
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler? LanguageChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implementation of UI localization service.
|
|
||||||
/// </summary>
|
|
||||||
public class UILocalizationService : IUILocalizationService
|
|
||||||
{
|
|
||||||
private readonly IStringLocalizer<Desktop.UIStrings> _localizer;
|
|
||||||
private bool _followSystem = true;
|
|
||||||
|
|
||||||
public UILocalizationService(IStringLocalizer<Desktop.UIStrings> localizer)
|
|
||||||
{
|
|
||||||
_localizer = localizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var value = _localizer[key];
|
|
||||||
return value.ResourceNotFound ? $"[{key}]" : value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string key, params object[] args]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var value = _localizer[key];
|
|
||||||
if (value.ResourceNotFound) return $"[{key}]";
|
|
||||||
return string.Format(value.Value, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CurrentCulture => System.Globalization.CultureInfo.CurrentUICulture.Name;
|
|
||||||
|
|
||||||
public bool FollowSystem
|
|
||||||
{
|
|
||||||
get => _followSystem;
|
|
||||||
set => _followSystem = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler? LanguageChanged;
|
|
||||||
|
|
||||||
public void SetLanguage(string cultureName)
|
|
||||||
{
|
|
||||||
var culture = new System.Globalization.CultureInfo(cultureName);
|
|
||||||
System.Globalization.CultureInfo.CurrentCulture = culture;
|
|
||||||
System.Globalization.CultureInfo.CurrentUICulture = culture;
|
|
||||||
LanguageChanged?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using Avalonia;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop;
|
|
||||||
|
|
||||||
internal static class Program
|
|
||||||
{
|
|
||||||
[STAThread]
|
|
||||||
public static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
|
||||||
|
|
||||||
BuildAvaloniaApp()
|
|
||||||
.StartWithClassicDesktopLifetime(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AppBuilder BuildAvaloniaApp()
|
|
||||||
=> AppBuilder.Configure<App>()
|
|
||||||
.UsePlatformDetect()
|
|
||||||
.WithInterFont()
|
|
||||||
.LogToTrace();
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace ZonyLrcTools.Desktop;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Marker class for UI localization resources.
|
|
||||||
/// This class is used by IStringLocalizer<UIStrings> to locate the resource files.
|
|
||||||
/// The namespace must be the root namespace so that IStringLocalizer looks for Resources/UIStrings.resx
|
|
||||||
/// </summary>
|
|
||||||
public class UIStrings
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Navigation -->
|
|
||||||
<data name="Nav_Home" xml:space="preserve">
|
|
||||||
<value>Home</value>
|
|
||||||
</data>
|
|
||||||
<data name="Nav_LyricsDownload" xml:space="preserve">
|
|
||||||
<value>Lyrics Download</value>
|
|
||||||
</data>
|
|
||||||
<data name="Nav_AlbumDownload" xml:space="preserve">
|
|
||||||
<value>Album Cover</value>
|
|
||||||
</data>
|
|
||||||
<data name="Nav_Settings" xml:space="preserve">
|
|
||||||
<value>Settings</value>
|
|
||||||
</data>
|
|
||||||
<!-- Home Page -->
|
|
||||||
<data name="Home_Welcome" xml:space="preserve">
|
|
||||||
<value>Welcome to ZonyLrcTools</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_Description" xml:space="preserve">
|
|
||||||
<value>An open-source lyrics download tool</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_QuickStart" xml:space="preserve">
|
|
||||||
<value>Quick Start</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_DownloadLyrics" xml:space="preserve">
|
|
||||||
<value>Download Lyrics</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_DownloadAlbumCover" xml:space="preserve">
|
|
||||||
<value>Download Album Covers</value>
|
|
||||||
</data>
|
|
||||||
<!-- Lyrics Download Page -->
|
|
||||||
<data name="Lyrics_Title" xml:space="preserve">
|
|
||||||
<value>Lyrics Download</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Description" xml:space="preserve">
|
|
||||||
<value>Batch download lyrics for your music files</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_SelectFolder" xml:space="preserve">
|
|
||||||
<value>Select music folder...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Browse" xml:space="preserve">
|
|
||||||
<value>Browse...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Parallel" xml:space="preserve">
|
|
||||||
<value>Parallel:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_StartDownload" xml:space="preserve">
|
|
||||||
<value>Start Download</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_StopDownload" xml:space="preserve">
|
|
||||||
<value>Stop Download</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Progress" xml:space="preserve">
|
|
||||||
<value>Download Progress</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Idle" xml:space="preserve">
|
|
||||||
<value>Idle</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Scanning" xml:space="preserve">
|
|
||||||
<value>Scanning files...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Downloading" xml:space="preserve">
|
|
||||||
<value>Downloading lyrics...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Complete" xml:space="preserve">
|
|
||||||
<value>Download Complete</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Total" xml:space="preserve">
|
|
||||||
<value>Total:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Files" xml:space="preserve">
|
|
||||||
<value>files</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Success" xml:space="preserve">
|
|
||||||
<value>Success:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Failed" xml:space="preserve">
|
|
||||||
<value>Failed:</value>
|
|
||||||
</data>
|
|
||||||
<!-- Album Download Page -->
|
|
||||||
<data name="Album_Title" xml:space="preserve">
|
|
||||||
<value>Album Cover Download</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_Description" xml:space="preserve">
|
|
||||||
<value>Download album artwork for your music collection</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_SelectFolder" xml:space="preserve">
|
|
||||||
<value>Select music folder...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_Browse" xml:space="preserve">
|
|
||||||
<value>Browse...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_Parallel" xml:space="preserve">
|
|
||||||
<value>Parallel:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_StartDownload" xml:space="preserve">
|
|
||||||
<value>Start Download</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_StopDownload" xml:space="preserve">
|
|
||||||
<value>Stop Download</value>
|
|
||||||
</data>
|
|
||||||
<!-- Settings Page -->
|
|
||||||
<data name="Settings_Title" xml:space="preserve">
|
|
||||||
<value>Settings</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Language" xml:space="preserve">
|
|
||||||
<value>Language</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_FollowSystem" xml:space="preserve">
|
|
||||||
<value>Follow System</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Chinese" xml:space="preserve">
|
|
||||||
<value>Simplified Chinese</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_English" xml:space="preserve">
|
|
||||||
<value>English</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Theme" xml:space="preserve">
|
|
||||||
<value>Theme</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ThemeLight" xml:space="preserve">
|
|
||||||
<value>Light</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ThemeDark" xml:space="preserve">
|
|
||||||
<value>Dark</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ThemeSystem" xml:space="preserve">
|
|
||||||
<value>Follow System</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_LyricsProvider" xml:space="preserve">
|
|
||||||
<value>Lyrics Provider</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Proxy" xml:space="preserve">
|
|
||||||
<value>Proxy Settings</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ProxyEnable" xml:space="preserve">
|
|
||||||
<value>Enable Proxy</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ProxyAddress" xml:space="preserve">
|
|
||||||
<value>Proxy Address</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ProxyPort" xml:space="preserve">
|
|
||||||
<value>Proxy Port</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Save" xml:space="preserve">
|
|
||||||
<value>Save Settings</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Reset" xml:space="preserve">
|
|
||||||
<value>Reset</value>
|
|
||||||
</data>
|
|
||||||
<!-- DataGrid Columns -->
|
|
||||||
<data name="Column_SongName" xml:space="preserve">
|
|
||||||
<value>Song Name</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_Artist" xml:space="preserve">
|
|
||||||
<value>Artist</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_Album" xml:space="preserve">
|
|
||||||
<value>Album</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_Status" xml:space="preserve">
|
|
||||||
<value>Status</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_FilePath" xml:space="preserve">
|
|
||||||
<value>File Path</value>
|
|
||||||
</data>
|
|
||||||
<!-- Status Messages -->
|
|
||||||
<data name="Status_Success" xml:space="preserve">
|
|
||||||
<value>Success</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Failed" xml:space="preserve">
|
|
||||||
<value>Failed</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Skipped" xml:space="preserve">
|
|
||||||
<value>Skipped</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Pending" xml:space="preserve">
|
|
||||||
<value>Pending</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Processing" xml:space="preserve">
|
|
||||||
<value>Processing</value>
|
|
||||||
</data>
|
|
||||||
<!-- Common -->
|
|
||||||
<data name="Common_OK" xml:space="preserve">
|
|
||||||
<value>OK</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Cancel" xml:space="preserve">
|
|
||||||
<value>Cancel</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Yes" xml:space="preserve">
|
|
||||||
<value>Yes</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_No" xml:space="preserve">
|
|
||||||
<value>No</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Error" xml:space="preserve">
|
|
||||||
<value>Error</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Warning" xml:space="preserve">
|
|
||||||
<value>Warning</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Info" xml:space="preserve">
|
|
||||||
<value>Info</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<!-- Navigation -->
|
|
||||||
<data name="Nav_Home" xml:space="preserve">
|
|
||||||
<value>首页</value>
|
|
||||||
</data>
|
|
||||||
<data name="Nav_LyricsDownload" xml:space="preserve">
|
|
||||||
<value>歌词下载</value>
|
|
||||||
</data>
|
|
||||||
<data name="Nav_AlbumDownload" xml:space="preserve">
|
|
||||||
<value>专辑封面</value>
|
|
||||||
</data>
|
|
||||||
<data name="Nav_Settings" xml:space="preserve">
|
|
||||||
<value>设置</value>
|
|
||||||
</data>
|
|
||||||
<!-- Home Page -->
|
|
||||||
<data name="Home_Welcome" xml:space="preserve">
|
|
||||||
<value>欢迎使用 ZonyLrcTools</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_Description" xml:space="preserve">
|
|
||||||
<value>一款开源的歌词下载工具</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_QuickStart" xml:space="preserve">
|
|
||||||
<value>快速开始</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_DownloadLyrics" xml:space="preserve">
|
|
||||||
<value>下载歌词</value>
|
|
||||||
</data>
|
|
||||||
<data name="Home_DownloadAlbumCover" xml:space="preserve">
|
|
||||||
<value>下载专辑封面</value>
|
|
||||||
</data>
|
|
||||||
<!-- Lyrics Download Page -->
|
|
||||||
<data name="Lyrics_Title" xml:space="preserve">
|
|
||||||
<value>歌词下载</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Description" xml:space="preserve">
|
|
||||||
<value>批量下载音乐文件的歌词</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_SelectFolder" xml:space="preserve">
|
|
||||||
<value>选择音乐文件夹...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Browse" xml:space="preserve">
|
|
||||||
<value>浏览...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Parallel" xml:space="preserve">
|
|
||||||
<value>并行:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_StartDownload" xml:space="preserve">
|
|
||||||
<value>开始下载</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_StopDownload" xml:space="preserve">
|
|
||||||
<value>停止下载</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Progress" xml:space="preserve">
|
|
||||||
<value>下载进度</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Idle" xml:space="preserve">
|
|
||||||
<value>等待中</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Scanning" xml:space="preserve">
|
|
||||||
<value>正在扫描文件...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Downloading" xml:space="preserve">
|
|
||||||
<value>正在下载歌词...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Lyrics_Status_Complete" xml:space="preserve">
|
|
||||||
<value>下载完成</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Total" xml:space="preserve">
|
|
||||||
<value>总计:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Files" xml:space="preserve">
|
|
||||||
<value>个文件</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Success" xml:space="preserve">
|
|
||||||
<value>成功:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Failed" xml:space="preserve">
|
|
||||||
<value>失败:</value>
|
|
||||||
</data>
|
|
||||||
<!-- Album Download Page -->
|
|
||||||
<data name="Album_Title" xml:space="preserve">
|
|
||||||
<value>专辑封面下载</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_Description" xml:space="preserve">
|
|
||||||
<value>批量下载音乐文件的专辑封面</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_SelectFolder" xml:space="preserve">
|
|
||||||
<value>选择音乐文件夹...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_Browse" xml:space="preserve">
|
|
||||||
<value>浏览...</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_Parallel" xml:space="preserve">
|
|
||||||
<value>并行:</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_StartDownload" xml:space="preserve">
|
|
||||||
<value>开始下载</value>
|
|
||||||
</data>
|
|
||||||
<data name="Album_StopDownload" xml:space="preserve">
|
|
||||||
<value>停止下载</value>
|
|
||||||
</data>
|
|
||||||
<!-- Settings Page -->
|
|
||||||
<data name="Settings_Title" xml:space="preserve">
|
|
||||||
<value>设置</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Language" xml:space="preserve">
|
|
||||||
<value>语言</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_FollowSystem" xml:space="preserve">
|
|
||||||
<value>跟随系统</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Chinese" xml:space="preserve">
|
|
||||||
<value>简体中文</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_English" xml:space="preserve">
|
|
||||||
<value>English</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Theme" xml:space="preserve">
|
|
||||||
<value>主题</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ThemeLight" xml:space="preserve">
|
|
||||||
<value>浅色</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ThemeDark" xml:space="preserve">
|
|
||||||
<value>深色</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ThemeSystem" xml:space="preserve">
|
|
||||||
<value>跟随系统</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_LyricsProvider" xml:space="preserve">
|
|
||||||
<value>歌词源</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Proxy" xml:space="preserve">
|
|
||||||
<value>代理设置</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ProxyEnable" xml:space="preserve">
|
|
||||||
<value>启用代理</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ProxyAddress" xml:space="preserve">
|
|
||||||
<value>代理地址</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_ProxyPort" xml:space="preserve">
|
|
||||||
<value>代理端口</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Save" xml:space="preserve">
|
|
||||||
<value>保存设置</value>
|
|
||||||
</data>
|
|
||||||
<data name="Settings_Reset" xml:space="preserve">
|
|
||||||
<value>重置</value>
|
|
||||||
</data>
|
|
||||||
<!-- DataGrid Columns -->
|
|
||||||
<data name="Column_SongName" xml:space="preserve">
|
|
||||||
<value>歌曲名</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_Artist" xml:space="preserve">
|
|
||||||
<value>艺术家</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_Album" xml:space="preserve">
|
|
||||||
<value>专辑</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_Status" xml:space="preserve">
|
|
||||||
<value>状态</value>
|
|
||||||
</data>
|
|
||||||
<data name="Column_FilePath" xml:space="preserve">
|
|
||||||
<value>文件路径</value>
|
|
||||||
</data>
|
|
||||||
<!-- Status Messages -->
|
|
||||||
<data name="Status_Success" xml:space="preserve">
|
|
||||||
<value>成功</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Failed" xml:space="preserve">
|
|
||||||
<value>失败</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Skipped" xml:space="preserve">
|
|
||||||
<value>已跳过</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Pending" xml:space="preserve">
|
|
||||||
<value>等待中</value>
|
|
||||||
</data>
|
|
||||||
<data name="Status_Processing" xml:space="preserve">
|
|
||||||
<value>处理中</value>
|
|
||||||
</data>
|
|
||||||
<!-- Common -->
|
|
||||||
<data name="Common_OK" xml:space="preserve">
|
|
||||||
<value>确定</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Cancel" xml:space="preserve">
|
|
||||||
<value>取消</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Yes" xml:space="preserve">
|
|
||||||
<value>是</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_No" xml:space="preserve">
|
|
||||||
<value>否</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Error" xml:space="preserve">
|
|
||||||
<value>错误</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Warning" xml:space="preserve">
|
|
||||||
<value>警告</value>
|
|
||||||
</data>
|
|
||||||
<data name="Common_Info" xml:space="preserve">
|
|
||||||
<value>提示</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using ZonyLrcTools.Common.Infrastructure.Logging;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Avalonia implementation of IWarpLogger that supports UI binding.
|
|
||||||
/// </summary>
|
|
||||||
public class AvaloniaWarpLogger : IWarpLogger
|
|
||||||
{
|
|
||||||
private const int MaxLogEntries = 1000;
|
|
||||||
|
|
||||||
public ObservableCollection<LogEntry> LogEntries { get; } = new();
|
|
||||||
|
|
||||||
public event EventHandler<LogEntry>? LogAdded;
|
|
||||||
|
|
||||||
public Task DebugAsync(string message, Exception? exception = null)
|
|
||||||
{
|
|
||||||
return AddLogAsync(LogLevel.Debug, message, exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task InfoAsync(string message, Exception? exception = null)
|
|
||||||
{
|
|
||||||
return AddLogAsync(LogLevel.Info, message, exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task WarnAsync(string message, Exception? exception = null)
|
|
||||||
{
|
|
||||||
return AddLogAsync(LogLevel.Warning, message, exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task ErrorAsync(string message, Exception? exception = null)
|
|
||||||
{
|
|
||||||
return AddLogAsync(LogLevel.Error, message, exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AddLogAsync(LogLevel level, string message, Exception? exception)
|
|
||||||
{
|
|
||||||
var entry = new LogEntry(level, message, exception);
|
|
||||||
|
|
||||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
LogEntries.Add(entry);
|
|
||||||
|
|
||||||
while (LogEntries.Count > MaxLogEntries)
|
|
||||||
{
|
|
||||||
LogEntries.RemoveAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
LogAdded?.Invoke(this, entry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record LogEntry(LogLevel Level, string Message, Exception? Exception)
|
|
||||||
{
|
|
||||||
public DateTime Timestamp { get; } = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum LogLevel
|
|
||||||
{
|
|
||||||
Debug,
|
|
||||||
Info,
|
|
||||||
Warning,
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
|
||||||
using Avalonia.Platform.Storage;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
public class DialogService : IDialogService
|
|
||||||
{
|
|
||||||
private Window? MainWindow =>
|
|
||||||
(Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?
|
|
||||||
.MainWindow;
|
|
||||||
|
|
||||||
public async Task<string?> ShowFolderPickerAsync(string title)
|
|
||||||
{
|
|
||||||
if (MainWindow == null) return null;
|
|
||||||
|
|
||||||
var folders = await MainWindow.StorageProvider.OpenFolderPickerAsync(
|
|
||||||
new FolderPickerOpenOptions
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
AllowMultiple = false
|
|
||||||
});
|
|
||||||
|
|
||||||
return folders.FirstOrDefault()?.Path.LocalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string?> ShowFilePickerAsync(string title, string[]? filters = null)
|
|
||||||
{
|
|
||||||
if (MainWindow == null) return null;
|
|
||||||
|
|
||||||
var fileTypes = filters?.Select(f => new FilePickerFileType(f)
|
|
||||||
{
|
|
||||||
Patterns = new[] { f }
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
var files = await MainWindow.StorageProvider.OpenFilePickerAsync(
|
|
||||||
new FilePickerOpenOptions
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
AllowMultiple = false,
|
|
||||||
FileTypeFilter = fileTypes
|
|
||||||
});
|
|
||||||
|
|
||||||
return files.FirstOrDefault()?.Path.LocalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> ShowConfirmDialogAsync(string title, string message)
|
|
||||||
{
|
|
||||||
// TODO: Implement custom confirm dialog
|
|
||||||
await Task.CompletedTask;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ShowMessageAsync(string title, string message)
|
|
||||||
{
|
|
||||||
// TODO: Implement custom message dialog
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
public interface IDialogService
|
|
||||||
{
|
|
||||||
Task<string?> ShowFolderPickerAsync(string title);
|
|
||||||
Task<string?> ShowFilePickerAsync(string title, string[]? filters = null);
|
|
||||||
Task<bool> ShowConfirmDialogAsync(string title, string message);
|
|
||||||
Task ShowMessageAsync(string title, string message);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
public interface INavigationService
|
|
||||||
{
|
|
||||||
ViewModelBase? CurrentViewModel { get; }
|
|
||||||
event EventHandler<ViewModelBase>? NavigationChanged;
|
|
||||||
|
|
||||||
TViewModel NavigateTo<TViewModel>() where TViewModel : ViewModelBase;
|
|
||||||
void GoBack();
|
|
||||||
bool CanGoBack { get; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using Avalonia.Styling;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
public interface IThemeService
|
|
||||||
{
|
|
||||||
ThemeVariant CurrentTheme { get; }
|
|
||||||
event EventHandler<ThemeVariant>? ThemeChanged;
|
|
||||||
|
|
||||||
void SetTheme(ThemeVariant theme);
|
|
||||||
void ToggleTheme();
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
public class NavigationService : INavigationService
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
private readonly Stack<ViewModelBase> _navigationStack = new();
|
|
||||||
|
|
||||||
public ViewModelBase? CurrentViewModel => _navigationStack.TryPeek(out var vm) ? vm : null;
|
|
||||||
public bool CanGoBack => _navigationStack.Count > 1;
|
|
||||||
|
|
||||||
public event EventHandler<ViewModelBase>? NavigationChanged;
|
|
||||||
|
|
||||||
public NavigationService(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
_serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TViewModel NavigateTo<TViewModel>() where TViewModel : ViewModelBase
|
|
||||||
{
|
|
||||||
var viewModel = _serviceProvider.GetRequiredService<TViewModel>();
|
|
||||||
|
|
||||||
if (_navigationStack.TryPeek(out var current))
|
|
||||||
{
|
|
||||||
_ = current.OnDeactivatedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
_navigationStack.Push(viewModel);
|
|
||||||
_ = viewModel.OnActivatedAsync();
|
|
||||||
|
|
||||||
NavigationChanged?.Invoke(this, viewModel);
|
|
||||||
return viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GoBack()
|
|
||||||
{
|
|
||||||
if (!CanGoBack) return;
|
|
||||||
|
|
||||||
var current = _navigationStack.Pop();
|
|
||||||
_ = current.OnDeactivatedAsync();
|
|
||||||
|
|
||||||
if (_navigationStack.TryPeek(out var previous))
|
|
||||||
{
|
|
||||||
_ = previous.OnActivatedAsync();
|
|
||||||
NavigationChanged?.Invoke(this, previous);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
|
||||||
using ZonyLrcTools.Common.Infrastructure.Logging;
|
|
||||||
using ZonyLrcTools.Common.Infrastructure.Network;
|
|
||||||
using ZonyLrcTools.Desktop.Infrastructure.Localization;
|
|
||||||
using ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Register all services for the desktop application.
|
|
||||||
/// </summary>
|
|
||||||
public static IServiceCollection AddDesktopServices(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
// Register Common project's auto dependency injection
|
|
||||||
services.BeginAutoDependencyInject<AvaloniaWarpLogger>();
|
|
||||||
services.BeginAutoDependencyInject<IWarpHttpClient>();
|
|
||||||
|
|
||||||
// Configure Common services
|
|
||||||
services.ConfigureConfiguration();
|
|
||||||
services.ConfigureLocalization();
|
|
||||||
services.ConfigureToolService();
|
|
||||||
|
|
||||||
// Configure Desktop localization
|
|
||||||
// Note: Don't set ResourcesPath here because the embedded resource name is
|
|
||||||
// ZonyLrcTools.Desktop.UIStrings (not ZonyLrcTools.Desktop.Resources.UIStrings)
|
|
||||||
services.AddLocalization();
|
|
||||||
services.AddSingleton<IUILocalizationService, UILocalizationService>();
|
|
||||||
|
|
||||||
// Register GUI-specific IWarpLogger implementation
|
|
||||||
services.AddSingleton<AvaloniaWarpLogger>();
|
|
||||||
services.AddSingleton<IWarpLogger>(sp => sp.GetRequiredService<AvaloniaWarpLogger>());
|
|
||||||
|
|
||||||
// Register navigation and dialog services
|
|
||||||
services.AddSingleton<INavigationService, NavigationService>();
|
|
||||||
services.AddSingleton<IDialogService, DialogService>();
|
|
||||||
services.AddSingleton<IThemeService, ThemeService>();
|
|
||||||
|
|
||||||
// Register ViewModels
|
|
||||||
services.AddTransient<MainWindowViewModel>();
|
|
||||||
services.AddTransient<HomeViewModel>();
|
|
||||||
services.AddTransient<LyricsDownloadViewModel>();
|
|
||||||
services.AddTransient<AlbumDownloadViewModel>();
|
|
||||||
services.AddTransient<SettingsViewModel>();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize language settings based on system culture or user preference.
|
|
||||||
/// </summary>
|
|
||||||
public static void InitializeLanguage(string? preferredCulture = null)
|
|
||||||
{
|
|
||||||
CultureInfo culture;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(preferredCulture))
|
|
||||||
{
|
|
||||||
// Use user's preferred culture
|
|
||||||
culture = new CultureInfo(preferredCulture);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use system culture, fallback to zh-CN if not supported
|
|
||||||
var systemCulture = CultureInfo.CurrentUICulture.Name;
|
|
||||||
culture = systemCulture.StartsWith("zh") ? new CultureInfo("zh-CN") :
|
|
||||||
systemCulture.StartsWith("en") ? new CultureInfo("en-US") :
|
|
||||||
new CultureInfo("zh-CN");
|
|
||||||
}
|
|
||||||
|
|
||||||
CultureInfo.CurrentCulture = culture;
|
|
||||||
CultureInfo.CurrentUICulture = culture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Styling;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
public class ThemeService : IThemeService
|
|
||||||
{
|
|
||||||
public ThemeVariant CurrentTheme => Application.Current?.ActualThemeVariant ?? ThemeVariant.Default;
|
|
||||||
|
|
||||||
public event EventHandler<ThemeVariant>? ThemeChanged;
|
|
||||||
|
|
||||||
public void SetTheme(ThemeVariant theme)
|
|
||||||
{
|
|
||||||
if (Application.Current == null) return;
|
|
||||||
|
|
||||||
Application.Current.RequestedThemeVariant = theme;
|
|
||||||
ThemeChanged?.Invoke(this, theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleTheme()
|
|
||||||
{
|
|
||||||
var newTheme = CurrentTheme == ThemeVariant.Dark
|
|
||||||
? ThemeVariant.Light
|
|
||||||
: ThemeVariant.Dark;
|
|
||||||
|
|
||||||
SetTheme(newTheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using ZonyLrcTools.Common.Album;
|
|
||||||
using ZonyLrcTools.Desktop.Infrastructure.Localization;
|
|
||||||
using ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
public partial class AlbumDownloadViewModel : ViewModelBase
|
|
||||||
{
|
|
||||||
private readonly IAlbumDownloader _albumDownloader;
|
|
||||||
private readonly IDialogService _dialogService;
|
|
||||||
private readonly IUILocalizationService? _localization;
|
|
||||||
|
|
||||||
private CancellationTokenSource? _downloadCts;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _selectedFolderPath;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _parallelCount = 2;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _totalCount;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _completedCount;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _failedCount;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private double _progressPercentage;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[NotifyPropertyChangedFor(nameof(CanStartDownload))]
|
|
||||||
private bool _isDownloading;
|
|
||||||
|
|
||||||
// Localized strings
|
|
||||||
public string AlbumTitle => _localization?["Album_Title"] ?? "Album Cover Download";
|
|
||||||
public string AlbumDescription => _localization?["Album_Description"] ?? "Download album artwork for your music collection";
|
|
||||||
public string AlbumSelectFolder => _localization?["Album_SelectFolder"] ?? "Select music folder...";
|
|
||||||
public string AlbumBrowse => _localization?["Album_Browse"] ?? "Browse...";
|
|
||||||
public string AlbumParallel => _localization?["Album_Parallel"] ?? "Parallel:";
|
|
||||||
public string AlbumStartDownload => _localization?["Album_StartDownload"] ?? "Start Download";
|
|
||||||
public string AlbumStopDownload => _localization?["Album_StopDownload"] ?? "Stop Download";
|
|
||||||
public string CommonTotal => _localization?["Common_Total"] ?? "Total:";
|
|
||||||
public string CommonFiles => _localization?["Common_Files"] ?? "files";
|
|
||||||
public string CommonSuccess => _localization?["Common_Success"] ?? "Success:";
|
|
||||||
public string CommonFailed => _localization?["Common_Failed"] ?? "Failed:";
|
|
||||||
public string ColumnSongName => _localization?["Column_SongName"] ?? "Song Name";
|
|
||||||
public string ColumnArtist => _localization?["Column_Artist"] ?? "Artist";
|
|
||||||
public string ColumnStatus => _localization?["Column_Status"] ?? "Status";
|
|
||||||
|
|
||||||
public bool CanStartDownload => !IsDownloading && !string.IsNullOrEmpty(SelectedFolderPath);
|
|
||||||
|
|
||||||
public ObservableCollection<MusicFileViewModel> MusicFiles { get; } = new();
|
|
||||||
|
|
||||||
public AlbumDownloadViewModel(
|
|
||||||
IAlbumDownloader albumDownloader,
|
|
||||||
IDialogService dialogService,
|
|
||||||
IUILocalizationService? localization = null)
|
|
||||||
{
|
|
||||||
_albumDownloader = albumDownloader;
|
|
||||||
_dialogService = dialogService;
|
|
||||||
_localization = localization;
|
|
||||||
|
|
||||||
if (_localization != null)
|
|
||||||
{
|
|
||||||
_localization.LanguageChanged += OnLanguageChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLanguageChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
OnPropertyChanged(nameof(AlbumTitle));
|
|
||||||
OnPropertyChanged(nameof(AlbumDescription));
|
|
||||||
OnPropertyChanged(nameof(AlbumSelectFolder));
|
|
||||||
OnPropertyChanged(nameof(AlbumBrowse));
|
|
||||||
OnPropertyChanged(nameof(AlbumParallel));
|
|
||||||
OnPropertyChanged(nameof(AlbumStartDownload));
|
|
||||||
OnPropertyChanged(nameof(AlbumStopDownload));
|
|
||||||
OnPropertyChanged(nameof(CommonTotal));
|
|
||||||
OnPropertyChanged(nameof(CommonFiles));
|
|
||||||
OnPropertyChanged(nameof(CommonSuccess));
|
|
||||||
OnPropertyChanged(nameof(CommonFailed));
|
|
||||||
OnPropertyChanged(nameof(ColumnSongName));
|
|
||||||
OnPropertyChanged(nameof(ColumnArtist));
|
|
||||||
OnPropertyChanged(nameof(ColumnStatus));
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task SelectFolderAsync()
|
|
||||||
{
|
|
||||||
var folder = await _dialogService.ShowFolderPickerAsync("Select Music Folder");
|
|
||||||
if (string.IsNullOrEmpty(folder)) return;
|
|
||||||
|
|
||||||
SelectedFolderPath = folder;
|
|
||||||
OnPropertyChanged(nameof(CanStartDownload));
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task StartDownloadAsync()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(SelectedFolderPath)) return;
|
|
||||||
|
|
||||||
IsDownloading = true;
|
|
||||||
CompletedCount = 0;
|
|
||||||
FailedCount = 0;
|
|
||||||
ProgressPercentage = 0;
|
|
||||||
|
|
||||||
_downloadCts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO: Implement actual download logic using _albumDownloader
|
|
||||||
await Task.Delay(1000, _downloadCts.Token); // Placeholder
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
// User cancelled
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
IsDownloading = false;
|
|
||||||
_downloadCts?.Dispose();
|
|
||||||
_downloadCts = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void CancelDownload()
|
|
||||||
{
|
|
||||||
_downloadCts?.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
public partial class HomeViewModel : ViewModelBase
|
|
||||||
{
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _welcomeMessage = "Welcome to ZonyLrcTools X";
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _description = "A cross-platform tool for downloading lyrics and album covers for your music collection.";
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _version = "4.0.3";
|
|
||||||
}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using ZonyLrcTools.Common.Lyrics;
|
|
||||||
using ZonyLrcTools.Desktop.Infrastructure.Localization;
|
|
||||||
using ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
public partial class LyricsDownloadViewModel : ViewModelBase
|
|
||||||
{
|
|
||||||
private readonly ILyricsDownloader _lyricsDownloader;
|
|
||||||
private readonly IDialogService _dialogService;
|
|
||||||
private readonly IUILocalizationService? _localization;
|
|
||||||
|
|
||||||
private CancellationTokenSource? _downloadCts;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _selectedFolderPath;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _parallelCount = 2;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _totalCount;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _completedCount;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _failedCount;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private double _progressPercentage;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[NotifyCanExecuteChangedFor(nameof(StartDownloadCommand))]
|
|
||||||
[NotifyPropertyChangedFor(nameof(CanStartDownload))]
|
|
||||||
private bool _isDownloading;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _currentProcessingFile;
|
|
||||||
|
|
||||||
// Localized strings
|
|
||||||
public string LyricsTitle => _localization?["Lyrics_Title"] ?? "Lyrics Download";
|
|
||||||
public string LyricsDescription => _localization?["Lyrics_Description"] ?? "Batch download lyrics for your music files";
|
|
||||||
public string LyricsSelectFolder => _localization?["Lyrics_SelectFolder"] ?? "Select music folder...";
|
|
||||||
public string LyricsBrowse => _localization?["Lyrics_Browse"] ?? "Browse...";
|
|
||||||
public string LyricsParallel => _localization?["Lyrics_Parallel"] ?? "Parallel:";
|
|
||||||
public string LyricsStartDownload => _localization?["Lyrics_StartDownload"] ?? "Start Download";
|
|
||||||
public string LyricsStopDownload => _localization?["Lyrics_StopDownload"] ?? "Stop Download";
|
|
||||||
public string CommonTotal => _localization?["Common_Total"] ?? "Total:";
|
|
||||||
public string CommonFiles => _localization?["Common_Files"] ?? "files";
|
|
||||||
public string CommonSuccess => _localization?["Common_Success"] ?? "Success:";
|
|
||||||
public string CommonFailed => _localization?["Common_Failed"] ?? "Failed:";
|
|
||||||
public string ColumnSongName => _localization?["Column_SongName"] ?? "Song Name";
|
|
||||||
public string ColumnArtist => _localization?["Column_Artist"] ?? "Artist";
|
|
||||||
public string ColumnFilePath => _localization?["Column_FilePath"] ?? "File Path";
|
|
||||||
public string ColumnStatus => _localization?["Column_Status"] ?? "Status";
|
|
||||||
|
|
||||||
public bool CanStartDownload => !IsDownloading && !string.IsNullOrEmpty(SelectedFolderPath);
|
|
||||||
|
|
||||||
public ObservableCollection<MusicFileViewModel> MusicFiles { get; } = new();
|
|
||||||
|
|
||||||
public LyricsDownloadViewModel(
|
|
||||||
ILyricsDownloader lyricsDownloader,
|
|
||||||
IDialogService dialogService,
|
|
||||||
IUILocalizationService? localization = null)
|
|
||||||
{
|
|
||||||
_lyricsDownloader = lyricsDownloader;
|
|
||||||
_dialogService = dialogService;
|
|
||||||
_localization = localization;
|
|
||||||
|
|
||||||
if (_localization != null)
|
|
||||||
{
|
|
||||||
_localization.LanguageChanged += OnLanguageChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLanguageChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
OnPropertyChanged(nameof(LyricsTitle));
|
|
||||||
OnPropertyChanged(nameof(LyricsDescription));
|
|
||||||
OnPropertyChanged(nameof(LyricsSelectFolder));
|
|
||||||
OnPropertyChanged(nameof(LyricsBrowse));
|
|
||||||
OnPropertyChanged(nameof(LyricsParallel));
|
|
||||||
OnPropertyChanged(nameof(LyricsStartDownload));
|
|
||||||
OnPropertyChanged(nameof(LyricsStopDownload));
|
|
||||||
OnPropertyChanged(nameof(CommonTotal));
|
|
||||||
OnPropertyChanged(nameof(CommonFiles));
|
|
||||||
OnPropertyChanged(nameof(CommonSuccess));
|
|
||||||
OnPropertyChanged(nameof(CommonFailed));
|
|
||||||
OnPropertyChanged(nameof(ColumnSongName));
|
|
||||||
OnPropertyChanged(nameof(ColumnArtist));
|
|
||||||
OnPropertyChanged(nameof(ColumnFilePath));
|
|
||||||
OnPropertyChanged(nameof(ColumnStatus));
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task SelectFolderAsync()
|
|
||||||
{
|
|
||||||
var folder = await _dialogService.ShowFolderPickerAsync("Select Music Folder");
|
|
||||||
if (string.IsNullOrEmpty(folder)) return;
|
|
||||||
|
|
||||||
SelectedFolderPath = folder;
|
|
||||||
OnPropertyChanged(nameof(CanStartDownload));
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanStartDownload))]
|
|
||||||
private async Task StartDownloadAsync()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(SelectedFolderPath)) return;
|
|
||||||
|
|
||||||
IsDownloading = true;
|
|
||||||
CompletedCount = 0;
|
|
||||||
FailedCount = 0;
|
|
||||||
ProgressPercentage = 0;
|
|
||||||
|
|
||||||
_downloadCts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO: Implement actual download logic using _lyricsDownloader
|
|
||||||
await Task.Delay(1000, _downloadCts.Token); // Placeholder
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
// User cancelled
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
IsDownloading = false;
|
|
||||||
_downloadCts?.Dispose();
|
|
||||||
_downloadCts = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void CancelDownload()
|
|
||||||
{
|
|
||||||
_downloadCts?.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class MusicFileViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _filePath = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _name = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _artist = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isProcessed;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isSuccessful;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _statusMessage;
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using ZonyLrcTools.Desktop.Infrastructure.Localization;
|
|
||||||
using ZonyLrcTools.Desktop.Services;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
public partial class MainWindowViewModel : ViewModelBase
|
|
||||||
{
|
|
||||||
private readonly IThemeService _themeService;
|
|
||||||
private readonly IUILocalizationService _localization;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private ViewModelBase? _currentPage;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _selectedNavigationIndex;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[NotifyPropertyChangedFor(nameof(ThemeButtonText))]
|
|
||||||
private bool _isDarkTheme;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _title = "ZonyLrcTools X";
|
|
||||||
|
|
||||||
// Navigation localized strings
|
|
||||||
public string NavHome => _localization["Nav_Home"];
|
|
||||||
public string NavLyricsDownload => _localization["Nav_LyricsDownload"];
|
|
||||||
public string NavAlbumDownload => _localization["Nav_AlbumDownload"];
|
|
||||||
public string NavSettings => _localization["Nav_Settings"];
|
|
||||||
public string HomeDescription => _localization["Home_Description"];
|
|
||||||
|
|
||||||
public string ThemeButtonText => IsDarkTheme
|
|
||||||
? _localization["Settings_ThemeLight"]
|
|
||||||
: _localization["Settings_ThemeDark"];
|
|
||||||
|
|
||||||
public MainWindowViewModel(
|
|
||||||
IThemeService themeService,
|
|
||||||
IUILocalizationService localization,
|
|
||||||
HomeViewModel homeViewModel)
|
|
||||||
{
|
|
||||||
_themeService = themeService;
|
|
||||||
_localization = localization;
|
|
||||||
|
|
||||||
// Subscribe to language changes
|
|
||||||
_localization.LanguageChanged += OnLanguageChanged;
|
|
||||||
|
|
||||||
// Set initial page
|
|
||||||
CurrentPage = homeViewModel;
|
|
||||||
SelectedNavigationIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLanguageChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
// Notify all localized properties have changed
|
|
||||||
OnPropertyChanged(nameof(NavHome));
|
|
||||||
OnPropertyChanged(nameof(NavLyricsDownload));
|
|
||||||
OnPropertyChanged(nameof(NavAlbumDownload));
|
|
||||||
OnPropertyChanged(nameof(NavSettings));
|
|
||||||
OnPropertyChanged(nameof(HomeDescription));
|
|
||||||
OnPropertyChanged(nameof(ThemeButtonText));
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void ToggleTheme()
|
|
||||||
{
|
|
||||||
IsDarkTheme = !IsDarkTheme;
|
|
||||||
_themeService.SetTheme(IsDarkTheme
|
|
||||||
? Avalonia.Styling.ThemeVariant.Dark
|
|
||||||
: Avalonia.Styling.ThemeVariant.Light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Globalization;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using YamlDotNet.Serialization;
|
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
|
||||||
using ZonyLrcTools.Common.Configuration;
|
|
||||||
using ZonyLrcTools.Desktop.Infrastructure.Localization;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
public partial class SettingsViewModel : ViewModelBase
|
|
||||||
{
|
|
||||||
private readonly GlobalOptions _globalOptions;
|
|
||||||
private readonly IUILocalizationService? _localizationService;
|
|
||||||
private bool _isLoading = true; // Prevent saving during initial load
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _followSystemLanguage = true;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isChineseSelected = true;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isEnglishSelected;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isProxyEnabled;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _proxyIp = "127.0.0.1";
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _proxyPort = 7890;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isTranslationEnabled;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isOneLineMode;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _skipExistingLyrics;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _selectedEncoding = "utf-8";
|
|
||||||
|
|
||||||
// Localized strings
|
|
||||||
public string SettingsTitle => _localizationService?["Settings_Title"] ?? "Settings";
|
|
||||||
public string SettingsLanguage => _localizationService?["Settings_Language"] ?? "Language";
|
|
||||||
public string SettingsFollowSystem => _localizationService?["Settings_FollowSystem"] ?? "Follow System";
|
|
||||||
public string SettingsChinese => _localizationService?["Settings_Chinese"] ?? "Chinese";
|
|
||||||
public string SettingsEnglish => _localizationService?["Settings_English"] ?? "English";
|
|
||||||
public string SettingsProxy => _localizationService?["Settings_Proxy"] ?? "Proxy";
|
|
||||||
public string SettingsProxyEnable => _localizationService?["Settings_ProxyEnable"] ?? "Enable Proxy";
|
|
||||||
public string SettingsProxyAddress => _localizationService?["Settings_ProxyAddress"] ?? "Proxy Address";
|
|
||||||
public string SettingsLyricsProvider => _localizationService?["Settings_LyricsProvider"] ?? "Lyrics Provider";
|
|
||||||
public string SettingsReset => _localizationService?["Settings_Reset"] ?? "Reset";
|
|
||||||
|
|
||||||
public ObservableCollection<string> AvailableEncodings { get; } = new()
|
|
||||||
{
|
|
||||||
"utf-8",
|
|
||||||
"utf-8-bom",
|
|
||||||
"gb2312",
|
|
||||||
"gbk"
|
|
||||||
};
|
|
||||||
|
|
||||||
public ObservableCollection<LyricsProviderSettingViewModel> LyricsProviders { get; } = new();
|
|
||||||
|
|
||||||
public SettingsViewModel(IOptions<GlobalOptions> options, IUILocalizationService? localizationService = null)
|
|
||||||
{
|
|
||||||
_globalOptions = options.Value;
|
|
||||||
_localizationService = localizationService;
|
|
||||||
|
|
||||||
// Subscribe to language changes
|
|
||||||
if (_localizationService != null)
|
|
||||||
{
|
|
||||||
_localizationService.LanguageChanged += OnLanguageChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadSettings();
|
|
||||||
LoadLanguageSettings();
|
|
||||||
_isLoading = false; // Allow saving after initial load
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLanguageChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
OnPropertyChanged(nameof(SettingsTitle));
|
|
||||||
OnPropertyChanged(nameof(SettingsLanguage));
|
|
||||||
OnPropertyChanged(nameof(SettingsFollowSystem));
|
|
||||||
OnPropertyChanged(nameof(SettingsChinese));
|
|
||||||
OnPropertyChanged(nameof(SettingsEnglish));
|
|
||||||
OnPropertyChanged(nameof(SettingsProxy));
|
|
||||||
OnPropertyChanged(nameof(SettingsProxyEnable));
|
|
||||||
OnPropertyChanged(nameof(SettingsProxyAddress));
|
|
||||||
OnPropertyChanged(nameof(SettingsLyricsProvider));
|
|
||||||
OnPropertyChanged(nameof(SettingsReset));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadLanguageSettings()
|
|
||||||
{
|
|
||||||
var currentCulture = CultureInfo.CurrentUICulture.Name;
|
|
||||||
IsChineseSelected = currentCulture.StartsWith("zh");
|
|
||||||
IsEnglishSelected = currentCulture.StartsWith("en");
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnFollowSystemLanguageChanged(bool value)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
// Auto-detect system language
|
|
||||||
var systemCulture = CultureInfo.InstalledUICulture.Name;
|
|
||||||
ApplyLanguage(systemCulture.StartsWith("zh") ? "zh-CN" : "en-US");
|
|
||||||
}
|
|
||||||
SaveSettingsIfNotLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnIsChineseSelectedChanged(bool value)
|
|
||||||
{
|
|
||||||
if (value && !FollowSystemLanguage)
|
|
||||||
{
|
|
||||||
ApplyLanguage("zh-CN");
|
|
||||||
}
|
|
||||||
SaveSettingsIfNotLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnIsEnglishSelectedChanged(bool value)
|
|
||||||
{
|
|
||||||
if (value && !FollowSystemLanguage)
|
|
||||||
{
|
|
||||||
ApplyLanguage("en-US");
|
|
||||||
}
|
|
||||||
SaveSettingsIfNotLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnIsProxyEnabledChanged(bool value) => SaveSettingsIfNotLoading();
|
|
||||||
partial void OnProxyIpChanged(string value) => SaveSettingsIfNotLoading();
|
|
||||||
partial void OnProxyPortChanged(int value) => SaveSettingsIfNotLoading();
|
|
||||||
partial void OnIsTranslationEnabledChanged(bool value) => SaveSettingsIfNotLoading();
|
|
||||||
partial void OnIsOneLineModeChanged(bool value) => SaveSettingsIfNotLoading();
|
|
||||||
partial void OnSkipExistingLyricsChanged(bool value) => SaveSettingsIfNotLoading();
|
|
||||||
partial void OnSelectedEncodingChanged(string value) => SaveSettingsIfNotLoading();
|
|
||||||
|
|
||||||
private void ApplyLanguage(string cultureName)
|
|
||||||
{
|
|
||||||
_localizationService?.SetLanguage(cultureName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveSettingsIfNotLoading()
|
|
||||||
{
|
|
||||||
if (!_isLoading)
|
|
||||||
{
|
|
||||||
SaveSettingsToFile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadSettings()
|
|
||||||
{
|
|
||||||
// Load network settings
|
|
||||||
IsProxyEnabled = _globalOptions.NetworkOptions?.IsEnable ?? false;
|
|
||||||
ProxyIp = _globalOptions.NetworkOptions?.Ip ?? "127.0.0.1";
|
|
||||||
ProxyPort = _globalOptions.NetworkOptions?.Port ?? 7890;
|
|
||||||
|
|
||||||
// Load lyrics settings
|
|
||||||
var lyricsConfig = _globalOptions.Provider?.Lyric?.Config;
|
|
||||||
if (lyricsConfig != null)
|
|
||||||
{
|
|
||||||
IsTranslationEnabled = lyricsConfig.IsEnableTranslation;
|
|
||||||
IsOneLineMode = lyricsConfig.IsOneLine;
|
|
||||||
SkipExistingLyrics = lyricsConfig.IsSkipExistLyricFiles;
|
|
||||||
SelectedEncoding = lyricsConfig.FileEncoding ?? "utf-8";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load lyrics providers
|
|
||||||
var providers = _globalOptions.Provider?.Lyric?.Plugin;
|
|
||||||
if (providers != null)
|
|
||||||
{
|
|
||||||
foreach (var provider in providers.OrderBy(p => p.Priority))
|
|
||||||
{
|
|
||||||
LyricsProviders.Add(new LyricsProviderSettingViewModel
|
|
||||||
{
|
|
||||||
Name = provider.Name ?? "Unknown",
|
|
||||||
Priority = provider.Priority,
|
|
||||||
IsEnabled = provider.Priority != -1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveSettingsToFile()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Update GlobalOptions with current values
|
|
||||||
if (_globalOptions.NetworkOptions != null)
|
|
||||||
{
|
|
||||||
_globalOptions.NetworkOptions.IsEnable = IsProxyEnabled;
|
|
||||||
_globalOptions.NetworkOptions.Ip = ProxyIp;
|
|
||||||
_globalOptions.NetworkOptions.Port = ProxyPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_globalOptions.Provider?.Lyric?.Config != null)
|
|
||||||
{
|
|
||||||
_globalOptions.Provider.Lyric.Config.IsEnableTranslation = IsTranslationEnabled;
|
|
||||||
_globalOptions.Provider.Lyric.Config.IsOneLine = IsOneLineMode;
|
|
||||||
_globalOptions.Provider.Lyric.Config.IsSkipExistLyricFiles = SkipExistingLyrics;
|
|
||||||
_globalOptions.Provider.Lyric.Config.FileEncoding = SelectedEncoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize and save to file
|
|
||||||
var serializer = new SerializerBuilder()
|
|
||||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var yaml = serializer.Serialize(_globalOptions);
|
|
||||||
var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.yaml");
|
|
||||||
File.WriteAllText(configPath, yaml);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Log error but don't crash - settings save is best effort
|
|
||||||
System.Diagnostics.Debug.WriteLine($"Failed to save settings: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void ResetToDefaults()
|
|
||||||
{
|
|
||||||
_isLoading = true; // Prevent multiple saves during reset
|
|
||||||
IsProxyEnabled = false;
|
|
||||||
ProxyIp = "127.0.0.1";
|
|
||||||
ProxyPort = 7890;
|
|
||||||
IsTranslationEnabled = false;
|
|
||||||
IsOneLineMode = false;
|
|
||||||
SkipExistingLyrics = true;
|
|
||||||
SelectedEncoding = "utf-8";
|
|
||||||
_isLoading = false;
|
|
||||||
SaveSettingsToFile(); // Save once after all resets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class LyricsProviderSettingViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _name = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _priority;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isEnabled;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for all ViewModels.
|
|
||||||
/// </summary>
|
|
||||||
public abstract partial class ViewModelBase : ObservableObject
|
|
||||||
{
|
|
||||||
[ObservableProperty]
|
|
||||||
[NotifyPropertyChangedFor(nameof(IsNotBusy))]
|
|
||||||
private bool _isBusy;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _busyMessage;
|
|
||||||
|
|
||||||
public bool IsNotBusy => !IsBusy;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the ViewModel is activated (navigated to).
|
|
||||||
/// </summary>
|
|
||||||
public virtual Task OnActivatedAsync() => Task.CompletedTask;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the ViewModel is deactivated (navigated away).
|
|
||||||
/// </summary>
|
|
||||||
public virtual Task OnDeactivatedAsync() => Task.CompletedTask;
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
<Window xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:vm="using:ZonyLrcTools.Desktop.ViewModels"
|
|
||||||
xmlns:pages="using:ZonyLrcTools.Desktop.Views.Pages"
|
|
||||||
xmlns:loc="using:ZonyLrcTools.Desktop.Infrastructure.Localization"
|
|
||||||
x:Class="ZonyLrcTools.Desktop.Views.MainWindow"
|
|
||||||
x:DataType="vm:MainWindowViewModel"
|
|
||||||
Title="{Binding Title}"
|
|
||||||
Width="1200" Height="800"
|
|
||||||
MinWidth="900" MinHeight="600"
|
|
||||||
WindowStartupLocation="CenterScreen">
|
|
||||||
|
|
||||||
<Grid ColumnDefinitions="220,*">
|
|
||||||
<!-- Left Navigation Panel -->
|
|
||||||
<Border Grid.Column="0"
|
|
||||||
Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
Padding="8">
|
|
||||||
<DockPanel>
|
|
||||||
<!-- App Title -->
|
|
||||||
<StackPanel DockPanel.Dock="Top" Margin="8,16,8,24">
|
|
||||||
<TextBlock Text="ZonyLrcTools"
|
|
||||||
FontSize="24"
|
|
||||||
FontWeight="Bold"
|
|
||||||
HorizontalAlignment="Center" />
|
|
||||||
<TextBlock Text="{Binding HomeDescription}"
|
|
||||||
FontSize="12"
|
|
||||||
Opacity="0.7"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Margin="0,4,0,0" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Theme Toggle -->
|
|
||||||
<Button DockPanel.Dock="Bottom"
|
|
||||||
Content="{Binding ThemeButtonText}"
|
|
||||||
Command="{Binding ToggleThemeCommand}"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
HorizontalContentAlignment="Center"
|
|
||||||
Margin="8" />
|
|
||||||
|
|
||||||
<!-- Navigation Menu -->
|
|
||||||
<ListBox SelectedIndex="{Binding SelectedNavigationIndex}"
|
|
||||||
Margin="0,8"
|
|
||||||
SelectionChanged="OnNavigationSelectionChanged">
|
|
||||||
<ListBoxItem>
|
|
||||||
<TextBlock Text="{Binding NavHome}" VerticalAlignment="Center" />
|
|
||||||
</ListBoxItem>
|
|
||||||
<ListBoxItem>
|
|
||||||
<TextBlock Text="{Binding NavLyricsDownload}" VerticalAlignment="Center" />
|
|
||||||
</ListBoxItem>
|
|
||||||
<ListBoxItem>
|
|
||||||
<TextBlock Text="{Binding NavAlbumDownload}" VerticalAlignment="Center" />
|
|
||||||
</ListBoxItem>
|
|
||||||
<ListBoxItem>
|
|
||||||
<TextBlock Text="{Binding NavSettings}" VerticalAlignment="Center" />
|
|
||||||
</ListBoxItem>
|
|
||||||
</ListBox>
|
|
||||||
</DockPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Right Content Area -->
|
|
||||||
<Border Grid.Column="1" Padding="24">
|
|
||||||
<ContentControl Content="{Binding CurrentPage}">
|
|
||||||
<ContentControl.DataTemplates>
|
|
||||||
<DataTemplate DataType="vm:HomeViewModel">
|
|
||||||
<pages:HomePage />
|
|
||||||
</DataTemplate>
|
|
||||||
<DataTemplate DataType="vm:LyricsDownloadViewModel">
|
|
||||||
<pages:LyricsDownloadPage />
|
|
||||||
</DataTemplate>
|
|
||||||
<DataTemplate DataType="vm:AlbumDownloadViewModel">
|
|
||||||
<pages:AlbumDownloadPage />
|
|
||||||
</DataTemplate>
|
|
||||||
<DataTemplate DataType="vm:SettingsViewModel">
|
|
||||||
<pages:SettingsPage />
|
|
||||||
</DataTemplate>
|
|
||||||
</ContentControl.DataTemplates>
|
|
||||||
</ContentControl>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</Window>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using ZonyLrcTools.Desktop.ViewModels;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Views;
|
|
||||||
|
|
||||||
public partial class MainWindow : Window
|
|
||||||
{
|
|
||||||
public MainWindow()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNavigationSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (DataContext is not MainWindowViewModel viewModel) return;
|
|
||||||
if (sender is not ListBox listBox) return;
|
|
||||||
|
|
||||||
var services = App.Services;
|
|
||||||
if (services == null) return;
|
|
||||||
|
|
||||||
switch (listBox.SelectedIndex)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
viewModel.CurrentPage = services.GetRequiredService<HomeViewModel>();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
viewModel.CurrentPage = services.GetRequiredService<LyricsDownloadViewModel>();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
viewModel.CurrentPage = services.GetRequiredService<AlbumDownloadViewModel>();
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
viewModel.CurrentPage = services.GetRequiredService<SettingsViewModel>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:vm="using:ZonyLrcTools.Desktop.ViewModels"
|
|
||||||
x:Class="ZonyLrcTools.Desktop.Views.Pages.AlbumDownloadPage"
|
|
||||||
x:DataType="vm:AlbumDownloadViewModel">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
|
||||||
<!-- Title -->
|
|
||||||
<StackPanel Grid.Row="0" Margin="0,0,0,24">
|
|
||||||
<TextBlock Text="{Binding AlbumTitle}"
|
|
||||||
FontSize="28"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
<TextBlock Text="{Binding AlbumDescription}"
|
|
||||||
Opacity="0.7"
|
|
||||||
Margin="0,4,0,0" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Controls -->
|
|
||||||
<Border Grid.Row="1"
|
|
||||||
Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="16"
|
|
||||||
Margin="0,0,0,16">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<!-- Row 1: Folder Selection -->
|
|
||||||
<Grid ColumnDefinitions="*,Auto">
|
|
||||||
<TextBox Grid.Column="0"
|
|
||||||
Text="{Binding SelectedFolderPath}"
|
|
||||||
Watermark="{Binding AlbumSelectFolder}"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Margin="0,0,12,0" />
|
|
||||||
<Button Grid.Column="1"
|
|
||||||
Content="{Binding AlbumBrowse}"
|
|
||||||
Command="{Binding SelectFolderCommand}"
|
|
||||||
MinWidth="80" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Row 2: Options and Actions -->
|
|
||||||
<Grid ColumnDefinitions="Auto,Auto,*,Auto">
|
|
||||||
<!-- Parallel Count -->
|
|
||||||
<TextBlock Grid.Column="0"
|
|
||||||
Text="{Binding AlbumParallel}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="0,0,8,0" />
|
|
||||||
<TextBox Grid.Column="1"
|
|
||||||
Text="{Binding ParallelCount}"
|
|
||||||
Width="60"
|
|
||||||
TextAlignment="Center"
|
|
||||||
VerticalContentAlignment="Center" />
|
|
||||||
|
|
||||||
<!-- Spacer -->
|
|
||||||
<Panel Grid.Column="2" />
|
|
||||||
|
|
||||||
<!-- Download Buttons -->
|
|
||||||
<StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="8">
|
|
||||||
<Button Command="{Binding StartDownloadCommand}"
|
|
||||||
Content="{Binding AlbumStartDownload}"
|
|
||||||
IsVisible="{Binding !IsDownloading}"
|
|
||||||
Classes="accent"
|
|
||||||
MinWidth="100" />
|
|
||||||
<Button Command="{Binding CancelDownloadCommand}"
|
|
||||||
Content="{Binding AlbumStopDownload}"
|
|
||||||
IsVisible="{Binding IsDownloading}"
|
|
||||||
MinWidth="100" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Progress Bar (visible during download) -->
|
|
||||||
<StackPanel IsVisible="{Binding IsDownloading}" Spacing="8">
|
|
||||||
<ProgressBar Value="{Binding ProgressPercentage}"
|
|
||||||
Maximum="100"
|
|
||||||
Height="6" />
|
|
||||||
<TextBlock HorizontalAlignment="Center" FontSize="12" Opacity="0.8">
|
|
||||||
<Run Text="{Binding CompletedCount}" />
|
|
||||||
<Run Text=" / " />
|
|
||||||
<Run Text="{Binding TotalCount}" />
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- File List -->
|
|
||||||
<Border Grid.Row="2"
|
|
||||||
Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="1">
|
|
||||||
<DataGrid ItemsSource="{Binding MusicFiles}"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal"
|
|
||||||
BorderThickness="0">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTextColumn Header="{Binding ColumnSongName}"
|
|
||||||
Binding="{Binding Name}"
|
|
||||||
Width="*" />
|
|
||||||
<DataGridTextColumn Header="{Binding ColumnArtist}"
|
|
||||||
Binding="{Binding Artist}"
|
|
||||||
Width="200" />
|
|
||||||
<DataGridTextColumn Header="{Binding ColumnStatus}"
|
|
||||||
Binding="{Binding StatusMessage}"
|
|
||||||
Width="120" />
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Status Bar -->
|
|
||||||
<Border Grid.Row="3"
|
|
||||||
Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
Padding="16,12"
|
|
||||||
Margin="0,16,0,0"
|
|
||||||
CornerRadius="8">
|
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto">
|
|
||||||
<!-- Total -->
|
|
||||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="4">
|
|
||||||
<TextBlock Text="{Binding CommonTotal}" Opacity="0.8" />
|
|
||||||
<TextBlock Text="{Binding TotalCount}" FontWeight="SemiBold" />
|
|
||||||
<TextBlock Text="{Binding CommonFiles}" Opacity="0.8" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Spacer -->
|
|
||||||
<Panel Grid.Column="1" />
|
|
||||||
|
|
||||||
<!-- Success -->
|
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="4" Margin="0,0,24,0">
|
|
||||||
<TextBlock Text="{Binding CommonSuccess}" Opacity="0.8" />
|
|
||||||
<TextBlock Text="{Binding CompletedCount}"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="#4CAF50" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Failed -->
|
|
||||||
<StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="4">
|
|
||||||
<TextBlock Text="{Binding CommonFailed}" Opacity="0.8" />
|
|
||||||
<TextBlock Text="{Binding FailedCount}"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="#F44336" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Views.Pages;
|
|
||||||
|
|
||||||
public partial class AlbumDownloadPage : UserControl
|
|
||||||
{
|
|
||||||
public AlbumDownloadPage()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:vm="using:ZonyLrcTools.Desktop.ViewModels"
|
|
||||||
x:Class="ZonyLrcTools.Desktop.Views.Pages.HomePage"
|
|
||||||
x:DataType="vm:HomeViewModel">
|
|
||||||
|
|
||||||
<ScrollViewer>
|
|
||||||
<StackPanel Spacing="24">
|
|
||||||
<!-- Header -->
|
|
||||||
<StackPanel Spacing="8">
|
|
||||||
<TextBlock Text="{Binding WelcomeMessage}"
|
|
||||||
FontSize="32"
|
|
||||||
FontWeight="Bold" />
|
|
||||||
<TextBlock Text="{Binding Description}"
|
|
||||||
FontSize="14"
|
|
||||||
Opacity="0.7"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Quick Actions -->
|
|
||||||
<Border Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="24">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<TextBlock Text="Quick Actions"
|
|
||||||
FontSize="18"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
|
|
||||||
<WrapPanel Orientation="Horizontal">
|
|
||||||
<Button Margin="0,0,16,16"
|
|
||||||
Padding="24,16"
|
|
||||||
CornerRadius="8">
|
|
||||||
<StackPanel Spacing="8">
|
|
||||||
<TextBlock Text="Download Lyrics"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
<TextBlock Text="Batch download lyrics for your music"
|
|
||||||
FontSize="12"
|
|
||||||
Opacity="0.7" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button Margin="0,0,16,16"
|
|
||||||
Padding="24,16"
|
|
||||||
CornerRadius="8">
|
|
||||||
<StackPanel Spacing="8">
|
|
||||||
<TextBlock Text="Download Album Covers"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
<TextBlock Text="Get album artwork for your collection"
|
|
||||||
FontSize="12"
|
|
||||||
Opacity="0.7" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</WrapPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Features -->
|
|
||||||
<Border Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="24">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<TextBlock Text="Features"
|
|
||||||
FontSize="18"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
|
|
||||||
<ItemsControl>
|
|
||||||
<ItemsControl.Items>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,4">
|
|
||||||
<TextBlock Text="*" FontWeight="Bold" Foreground="{DynamicResource AppAccentBrush}" />
|
|
||||||
<TextBlock Text="Multiple lyrics sources: NetEase, QQ Music, KuGou, KuWo" />
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,4">
|
|
||||||
<TextBlock Text="*" FontWeight="Bold" Foreground="{DynamicResource AppAccentBrush}" />
|
|
||||||
<TextBlock Text="Batch processing with parallel downloads" />
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,4">
|
|
||||||
<TextBlock Text="*" FontWeight="Bold" Foreground="{DynamicResource AppAccentBrush}" />
|
|
||||||
<TextBlock Text="Support for translation lyrics" />
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,4">
|
|
||||||
<TextBlock Text="*" FontWeight="Bold" Foreground="{DynamicResource AppAccentBrush}" />
|
|
||||||
<TextBlock Text="Cross-platform: Windows, macOS, Linux" />
|
|
||||||
</StackPanel>
|
|
||||||
</ItemsControl.Items>
|
|
||||||
</ItemsControl>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Version Info -->
|
|
||||||
<TextBlock Opacity="0.5" FontSize="12">
|
|
||||||
<Run Text="Version " />
|
|
||||||
<Run Text="{Binding Version}" />
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Views.Pages;
|
|
||||||
|
|
||||||
public partial class HomePage : UserControl
|
|
||||||
{
|
|
||||||
public HomePage()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:vm="using:ZonyLrcTools.Desktop.ViewModels"
|
|
||||||
x:Class="ZonyLrcTools.Desktop.Views.Pages.LyricsDownloadPage"
|
|
||||||
x:DataType="vm:LyricsDownloadViewModel">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
|
||||||
<!-- Title -->
|
|
||||||
<StackPanel Grid.Row="0" Margin="0,0,0,24">
|
|
||||||
<TextBlock Text="{Binding LyricsTitle}"
|
|
||||||
FontSize="28"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
<TextBlock Text="{Binding LyricsDescription}"
|
|
||||||
Opacity="0.7"
|
|
||||||
Margin="0,4,0,0" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Controls -->
|
|
||||||
<Border Grid.Row="1"
|
|
||||||
Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="16"
|
|
||||||
Margin="0,0,0,16">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<!-- Row 1: Folder Selection -->
|
|
||||||
<Grid ColumnDefinitions="*,Auto">
|
|
||||||
<TextBox Grid.Column="0"
|
|
||||||
Text="{Binding SelectedFolderPath}"
|
|
||||||
Watermark="{Binding LyricsSelectFolder}"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Margin="0,0,12,0" />
|
|
||||||
<Button Grid.Column="1"
|
|
||||||
Content="{Binding LyricsBrowse}"
|
|
||||||
Command="{Binding SelectFolderCommand}"
|
|
||||||
MinWidth="80" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Row 2: Options and Actions -->
|
|
||||||
<Grid ColumnDefinitions="Auto,Auto,*,Auto">
|
|
||||||
<!-- Parallel Count -->
|
|
||||||
<TextBlock Grid.Column="0"
|
|
||||||
Text="{Binding LyricsParallel}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="0,0,8,0" />
|
|
||||||
<TextBox Grid.Column="1"
|
|
||||||
Text="{Binding ParallelCount}"
|
|
||||||
Width="60"
|
|
||||||
TextAlignment="Center"
|
|
||||||
VerticalContentAlignment="Center" />
|
|
||||||
|
|
||||||
<!-- Spacer -->
|
|
||||||
<Panel Grid.Column="2" />
|
|
||||||
|
|
||||||
<!-- Download Buttons -->
|
|
||||||
<StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="8">
|
|
||||||
<Button Command="{Binding StartDownloadCommand}"
|
|
||||||
Content="{Binding LyricsStartDownload}"
|
|
||||||
IsVisible="{Binding !IsDownloading}"
|
|
||||||
Classes="accent"
|
|
||||||
MinWidth="100" />
|
|
||||||
<Button Command="{Binding CancelDownloadCommand}"
|
|
||||||
Content="{Binding LyricsStopDownload}"
|
|
||||||
IsVisible="{Binding IsDownloading}"
|
|
||||||
MinWidth="100" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Progress Bar (visible during download) -->
|
|
||||||
<StackPanel IsVisible="{Binding IsDownloading}" Spacing="8">
|
|
||||||
<ProgressBar Value="{Binding ProgressPercentage}"
|
|
||||||
Maximum="100"
|
|
||||||
Height="6" />
|
|
||||||
<TextBlock HorizontalAlignment="Center" FontSize="12" Opacity="0.8">
|
|
||||||
<Run Text="{Binding CompletedCount}" />
|
|
||||||
<Run Text=" / " />
|
|
||||||
<Run Text="{Binding TotalCount}" />
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- File List -->
|
|
||||||
<Border Grid.Row="2"
|
|
||||||
Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="1">
|
|
||||||
<DataGrid ItemsSource="{Binding MusicFiles}"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal"
|
|
||||||
BorderThickness="0">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTextColumn Header="{Binding ColumnSongName}"
|
|
||||||
Binding="{Binding Name}"
|
|
||||||
Width="*" />
|
|
||||||
<DataGridTextColumn Header="{Binding ColumnArtist}"
|
|
||||||
Binding="{Binding Artist}"
|
|
||||||
Width="150" />
|
|
||||||
<DataGridTextColumn Header="{Binding ColumnFilePath}"
|
|
||||||
Binding="{Binding FilePath}"
|
|
||||||
Width="250" />
|
|
||||||
<DataGridTextColumn Header="{Binding ColumnStatus}"
|
|
||||||
Binding="{Binding StatusMessage}"
|
|
||||||
Width="100" />
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Status Bar -->
|
|
||||||
<Border Grid.Row="3"
|
|
||||||
Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
Padding="16,12"
|
|
||||||
Margin="0,16,0,0"
|
|
||||||
CornerRadius="8">
|
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto">
|
|
||||||
<!-- Total -->
|
|
||||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="4">
|
|
||||||
<TextBlock Text="{Binding CommonTotal}" Opacity="0.8" />
|
|
||||||
<TextBlock Text="{Binding TotalCount}" FontWeight="SemiBold" />
|
|
||||||
<TextBlock Text="{Binding CommonFiles}" Opacity="0.8" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Spacer -->
|
|
||||||
<Panel Grid.Column="1" />
|
|
||||||
|
|
||||||
<!-- Success -->
|
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="4" Margin="0,0,24,0">
|
|
||||||
<TextBlock Text="{Binding CommonSuccess}" Opacity="0.8" />
|
|
||||||
<TextBlock Text="{Binding CompletedCount}"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="#4CAF50" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Failed -->
|
|
||||||
<StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="4">
|
|
||||||
<TextBlock Text="{Binding CommonFailed}" Opacity="0.8" />
|
|
||||||
<TextBlock Text="{Binding FailedCount}"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="#F44336" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Views.Pages;
|
|
||||||
|
|
||||||
public partial class LyricsDownloadPage : UserControl
|
|
||||||
{
|
|
||||||
public LyricsDownloadPage()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:vm="using:ZonyLrcTools.Desktop.ViewModels"
|
|
||||||
x:Class="ZonyLrcTools.Desktop.Views.Pages.SettingsPage"
|
|
||||||
x:DataType="vm:SettingsViewModel">
|
|
||||||
|
|
||||||
<ScrollViewer>
|
|
||||||
<StackPanel Spacing="24">
|
|
||||||
<!-- Title -->
|
|
||||||
<StackPanel Margin="0,0,0,8">
|
|
||||||
<TextBlock Text="{Binding SettingsTitle}"
|
|
||||||
FontSize="28"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Language Settings -->
|
|
||||||
<Border Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="24">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<TextBlock Text="{Binding SettingsLanguage}"
|
|
||||||
FontSize="18"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
|
|
||||||
<CheckBox Content="{Binding SettingsFollowSystem}"
|
|
||||||
IsChecked="{Binding FollowSystemLanguage}" />
|
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="16" IsEnabled="{Binding !FollowSystemLanguage}">
|
|
||||||
<RadioButton GroupName="Language"
|
|
||||||
Content="{Binding SettingsChinese}"
|
|
||||||
IsChecked="{Binding IsChineseSelected}" />
|
|
||||||
<RadioButton GroupName="Language"
|
|
||||||
Content="{Binding SettingsEnglish}"
|
|
||||||
IsChecked="{Binding IsEnglishSelected}" />
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Network Settings -->
|
|
||||||
<Border Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="24">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<TextBlock Text="{Binding SettingsProxy}"
|
|
||||||
FontSize="18"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
|
|
||||||
<CheckBox Content="{Binding SettingsProxyEnable}"
|
|
||||||
IsChecked="{Binding IsProxyEnabled}" />
|
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto,120" IsEnabled="{Binding IsProxyEnabled}">
|
|
||||||
<TextBox Grid.Column="0"
|
|
||||||
Text="{Binding ProxyIp}"
|
|
||||||
Watermark="{Binding SettingsProxyAddress}"
|
|
||||||
Margin="0,0,8,0" />
|
|
||||||
<TextBlock Grid.Column="1"
|
|
||||||
Text=":"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="0,0,8,0" />
|
|
||||||
<NumericUpDown Grid.Column="2"
|
|
||||||
Value="{Binding ProxyPort}"
|
|
||||||
Minimum="1"
|
|
||||||
Maximum="65535" />
|
|
||||||
</Grid>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Lyrics Settings -->
|
|
||||||
<Border Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="24">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<TextBlock Text="Lyrics"
|
|
||||||
FontSize="18"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
|
|
||||||
<CheckBox Content="Enable translation lyrics"
|
|
||||||
IsChecked="{Binding IsTranslationEnabled}" />
|
|
||||||
|
|
||||||
<CheckBox Content="One-line mode (original + translation on same line)"
|
|
||||||
IsChecked="{Binding IsOneLineMode}" />
|
|
||||||
|
|
||||||
<CheckBox Content="Skip existing lyrics files"
|
|
||||||
IsChecked="{Binding SkipExistingLyrics}" />
|
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
|
||||||
<TextBlock Text="File Encoding:"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
<ComboBox ItemsSource="{Binding AvailableEncodings}"
|
|
||||||
SelectedItem="{Binding SelectedEncoding}"
|
|
||||||
Width="150" />
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Lyrics Providers -->
|
|
||||||
<Border Background="{DynamicResource NavigationBackgroundBrush}"
|
|
||||||
CornerRadius="8"
|
|
||||||
Padding="24">
|
|
||||||
<StackPanel Spacing="16">
|
|
||||||
<TextBlock Text="{Binding SettingsLyricsProvider}"
|
|
||||||
FontSize="18"
|
|
||||||
FontWeight="SemiBold" />
|
|
||||||
|
|
||||||
<ItemsControl ItemsSource="{Binding LyricsProviders}">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:LyricsProviderSettingViewModel">
|
|
||||||
<Border Background="{DynamicResource AppBackgroundBrush}"
|
|
||||||
CornerRadius="4"
|
|
||||||
Padding="12,8"
|
|
||||||
Margin="0,4">
|
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
|
||||||
<CheckBox Grid.Column="0"
|
|
||||||
IsChecked="{Binding IsEnabled}"
|
|
||||||
Margin="0,0,12,0" />
|
|
||||||
<TextBlock Grid.Column="1"
|
|
||||||
Text="{Binding Name}"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
<TextBlock Grid.Column="2"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Opacity="0.5">
|
|
||||||
<Run Text="Priority: " />
|
|
||||||
<Run Text="{Binding Priority}" />
|
|
||||||
</TextBlock>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Actions -->
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="16" HorizontalAlignment="Right">
|
|
||||||
<Button Content="{Binding SettingsReset}"
|
|
||||||
Command="{Binding ResetToDefaultsCommand}" />
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
|
|
||||||
namespace ZonyLrcTools.Desktop.Views.Pages;
|
|
||||||
|
|
||||||
public partial class SettingsPage : UserControl
|
|
||||||
{
|
|
||||||
public SettingsPage()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<OutputType>WinExe</OutputType>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
|
||||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<!-- Avalonia -->
|
|
||||||
<PackageReference Include="Avalonia" />
|
|
||||||
<PackageReference Include="Avalonia.Desktop" />
|
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" />
|
|
||||||
<PackageReference Include="Avalonia.Fonts.Inter" />
|
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" />
|
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Condition="'$(Configuration)' == 'Debug'" />
|
|
||||||
<!-- MVVM -->
|
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
|
||||||
<!-- DI & Hosting -->
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
|
||||||
<!-- Logging -->
|
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.File" />
|
|
||||||
<!-- Encoding -->
|
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" />
|
|
||||||
<!-- Localization -->
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="..\ZonyLrcTools.Cli\config.yaml" Link="config.yaml">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="..\ZonyLrcTools.Cli\BlockWords.json" Link="BlockWords.json">
|
|
||||||
<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">
|
|
||||||
<assemblyIdentity version="1.0.0.0" name="ZonyLrcTools.Desktop"/>
|
|
||||||
|
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
|
||||||
<application>
|
|
||||||
<!-- Windows 10 and Windows 11 -->
|
|
||||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
|
||||||
</application>
|
|
||||||
</compatibility>
|
|
||||||
|
|
||||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<windowsSettings>
|
|
||||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
|
||||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
|
||||||
</windowsSettings>
|
|
||||||
</application>
|
|
||||||
</assembly>
|
|
||||||
@@ -9,18 +9,18 @@ namespace ZonyLrcTools.Tests.Infrastructure.Exceptions
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void LoadMessage_Test()
|
public void LoadMessage_Test()
|
||||||
{
|
{
|
||||||
// ErrorCodeHelper.LoadErrorMessage();
|
ErrorCodeHelper.LoadErrorMessage();
|
||||||
//
|
|
||||||
// ErrorCodeHelper.ErrorMessages.ShouldNotBeNull();
|
ErrorCodeHelper.ErrorMessages.ShouldNotBeNull();
|
||||||
// ErrorCodeHelper.ErrorMessages.Count.ShouldBe(17);
|
ErrorCodeHelper.ErrorMessages.Count.ShouldBe(17);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetMessage_Test()
|
public void GetMessage_Test()
|
||||||
{
|
{
|
||||||
// ErrorCodeHelper.LoadErrorMessage();
|
ErrorCodeHelper.LoadErrorMessage();
|
||||||
|
|
||||||
// ErrorCodeHelper.GetMessage(ErrorCodes.DirectoryNotExist).ShouldBe("需要扫描的目录不存在,请确认路径是否正确。");
|
ErrorCodeHelper.GetMessage(ErrorCodes.DirectoryNotExist).ShouldBe("需要扫描的目录不存在,请确认路径是否正确。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ Enhancement: None
|
|||||||
|
|
||||||
Fixed Bugs:
|
Fixed Bugs:
|
||||||
|
|
||||||
- [#167] Handle macOS code signing issues. (处理 macOS 代码签名问题。)
|
- 修复了 #156 的问题。
|
||||||
Reference in New Issue
Block a user