mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-09-05 04:56:52 +00:00
Compare commits
34 Commits
ZonyLrcToo
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6abeb40315 | ||
![]() |
1fdbaef7a8 | ||
![]() |
36ef2d2c3a | ||
![]() |
fa4620c37d | ||
![]() |
9ab9cd50e2 | ||
![]() |
e66ef89e7a | ||
![]() |
8b5d5c64b0 | ||
![]() |
2dca5239f5 | ||
![]() |
d5689164b5 | ||
![]() |
b57739f543 | ||
![]() |
a2c44901fd | ||
![]() |
6ad2992144 | ||
![]() |
3b0c55ac00 | ||
![]() |
81bf6ebe3f | ||
![]() |
aa3c45101b | ||
![]() |
d00d735bf0 | ||
![]() |
7fa595a71b | ||
![]() |
4f15d06e63 | ||
![]() |
5ccd8a7c53 | ||
![]() |
14812bceb7 | ||
![]() |
be538eb650 | ||
![]() |
888c7f511b | ||
![]() |
7bd1915f11 | ||
![]() |
aa78ea0713 | ||
![]() |
1342ca936c | ||
![]() |
46ac2d57a2 | ||
![]() |
444f4e75a6 | ||
![]() |
61aa31cc6f | ||
![]() |
46d11a86ea | ||
![]() |
8aaae90ed8 | ||
![]() |
d91cc68a60 | ||
![]() |
b6e3cb6038 | ||
![]() |
093e2879ad | ||
![]() |
fea5e1124f |
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4.1.7
|
||||
with:
|
||||
name: release-files
|
||||
path: .
|
||||
|
11
Directory.Build.props
Normal file
11
Directory.Build.props
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>5.0.0.0</Version>
|
||||
<Authors>Zony(real-zony)</Authors>
|
||||
<RepositoryUrl>https://github.com/real-zony/ZonyLrcToolsX</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
@@ -1,46 +1,53 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.0"/>
|
||||
<PackageVersion Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.0"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0"/>
|
||||
<PackageVersion Include="QRCoder" Version="1.4.3"/>
|
||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0"/>
|
||||
<PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0"/>
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0"/>
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0"/>
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0"/>
|
||||
<PackageVersion Include="Polly" Version="8.2.0"/>
|
||||
<PackageVersion Include="TagLibSharp" Version="2.3.0"/>
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||
<PackageVersion Include="NetEscapades.Configuration.Yaml" Version="3.1.0"/>
|
||||
<PackageVersion Include="MusicDecrypto.Library" Version="2.3.0"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0"/>
|
||||
<PackageVersion Include="AutoMapper" Version="12.0.1"/>
|
||||
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.0"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.0"/>
|
||||
<PackageVersion Include="SuperSocket.WebSocket" Version="2.0.0-beta.11"/>
|
||||
<PackageVersion Include="SuperSocket.WebSocket.Server" Version="2.0.0-beta.11"/>
|
||||
|
||||
<!-- Testing Projects -->
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
|
||||
<PackageVersion Include="Shouldly" Version="4.2.1"/>
|
||||
<PackageVersion Include="xunit" Version="2.6.3"/>
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.5"/>
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.0"/>
|
||||
|
||||
<!-- Avalonia -->
|
||||
<PackageVersion Include="Avalonia" Version="11.0.6"/>
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.6"/>
|
||||
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.0.6"/>
|
||||
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.0.6"/>
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.0.6"/>
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.6"/>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||
<PackageVersion Include="FluentIcons.Avalonia.Fluent" Version="1.1.244" />
|
||||
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
|
||||
<PackageVersion Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="QRCoder" Version="1.5.1" />
|
||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Async" Version="2.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
<PackageVersion Include="Polly" Version="8.4.0" />
|
||||
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
|
||||
<PackageVersion Include="MusicDecrypto.Library" Version="2.4.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.6" />
|
||||
<PackageVersion Include="SuperSocket.WebSocket" Version="2.0.0-beta.11" />
|
||||
<PackageVersion Include="SuperSocket.WebSocket.Server" Version="2.0.0-beta.11" />
|
||||
<!-- Testing Projects -->
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageVersion Include="Shouldly" Version="4.2.1" />
|
||||
<PackageVersion Include="xunit" Version="2.8.1" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageVersion>
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageVersion>
|
||||
<!-- Avalonia -->
|
||||
<PackageVersion Include="Avalonia" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.0.11" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.11" />
|
||||
<PackageVersion Include="YamlDotNet" Version="15.3.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
39
README.md
39
README.md
@@ -1,29 +1,38 @@
|
||||
简体中文 | [English](./docs/en_US.md)
|
||||
English | [简体中文](./zh_CN.md)
|
||||
|
||||
# 免责声明
|
||||
# Disclaimer
|
||||
|
||||
- 本工具仅作个人学习研究使用,可运行的二进制文件仅用于演示功能,不得将源码及其产物用于商业用途,否则由此造成的相关法律问题,[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||
- 任何单位或个人因下载使用软件所产生的任何意外、疏忽、合约毁坏、诽谤、版权或知识产权侵犯及其造成的损失 (包括但不限于直接、间接、附带或衍生的损失等),[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||
- 用户明确并同意本声明条款列举的全部内容,对使用本工具可能存在的风险和相关后果将完全由用户自行承担,[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||
- This tool is for personal learning and research purposes only. The executable binary files are for demonstration purposes only and the source code and its products must not be used for commercial purposes. Otherwise, I [here](https://github.com/real-zony) will not be responsible for any related legal issues.
|
||||
- Any unit or individual that downloads and uses the software resulting in any accidents, negligence, contract breaches, defamation, copyright or intellectual property infringement, and their resulting losses (including but not limited to direct, indirect, incidental or derivative losses), I [here](https://github.com/real-zony) will not bear any legal responsibility.
|
||||
- Users clearly agree to all the terms listed in this statement and fully assume the risks and consequences of using this tool. I [here](https://github.com/real-zony) will not bear any legal responsibility.
|
||||
|
||||
# 简介
|
||||
# Introduction
|
||||
|
||||
ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具,**QQ 群:337656932**,群文件里面有详细的视频教程。
|
||||
ZonyLrcToolX 4 is a cross-platform lyrics download tool based on CEF. **QQ Group: 337656932**. Detailed video tutorials are available in the group files.
|
||||
|
||||
🚧 当前版本正在开发当中。
|
||||
🚧 如果你想查看可以工作的代码,请切换到 dev 分支。
|
||||
🚧 The current version is under development.
|
||||
🚧 If you want to see working code, please switch to the dev branch.
|
||||
|
||||
# 下载
|
||||
# Download
|
||||
|
||||
如果你要获取最新版本,请访问 **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** 页面进行下载。
|
||||
To get the latest version, please visit the **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** page for download.
|
||||
|
||||
# 用法
|
||||
## Arch Linux User Repository
|
||||
|
||||
软件的具体使用方法已经迁移到了全新的文档站点,请跳转到 [https://docs.myzony.com](https://docs.myzony.com),里面包含完整的说明文档。
|
||||
This software has been packaged into [AUR](https://aur.archlinux.org/packages/zonylrctoolsx-bin). Arch Linux and its derivative distribution users can install it with the following command:
|
||||
|
||||
# 捐赠
|
||||
```bash
|
||||
# yay or other AUR Helpers
|
||||
yay -S zonylrctoolsx-bin
|
||||
|
||||
<img src="./docs/img/alipay.jpg" width="200"/><img src="./docs/img/wechat.jpg" width="200"/>
|
||||
ZonyLrcTools.Cli --help
|
||||
```
|
||||
|
||||
# Usage
|
||||
Detailed usage instructions have been moved to a new documentation site. Please visit [https://docs.myzony.com](https://docs.myzony.com) for complete documentation.
|
||||
|
||||
# Donation
|
||||
[爱发电](https://afdian.net/a/zony-lrc-tools)
|
||||
|
||||
# Star History
|
||||
|
||||
|
@@ -11,8 +11,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Cli", "src\Zon
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Tests", "tests\ZonyLrcTools.Tests\ZonyLrcTools.Tests.csproj", "{FFBD3200-568F-455B-8390-5E76C51D522C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.LocalServer", "src\ZonyLrcTools.LocalServer\ZonyLrcTools.LocalServer.csproj", "{2875A08A-FFD6-4863-B815-5384DCFE88FC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Common", "src\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj", "{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Desktop", "src\ZonyLrcTools.Desktop\ZonyLrcTools.Desktop.csproj", "{DC92902B-4303-4E43-AFB3-3F93FD3986AD}"
|
||||
@@ -31,10 +29,6 @@ Global
|
||||
{FFBD3200-568F-455B-8390-5E76C51D522C}.Debug|Any CPU.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.Build.0 = Release|Any CPU
|
||||
{2875A08A-FFD6-4863-B815-5384DCFE88FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2875A08A-FFD6-4863-B815-5384DCFE88FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2875A08A-FFD6-4863-B815-5384DCFE88FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2875A08A-FFD6-4863-B815-5384DCFE88FC}.Release|Any CPU.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.Build.0 = Debug|Any CPU
|
||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -53,7 +47,6 @@ Global
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||
{FFBD3200-568F-455B-8390-5E76C51D522C} = {AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}
|
||||
{2875A08A-FFD6-4863-B815-5384DCFE88FC} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||
{DC92902B-4303-4E43-AFB3-3F93FD3986AD} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
|
||||
EndGlobalSection
|
||||
|
@@ -1,18 +1,39 @@
|
||||
English | [简体中文](./zh_CN.md)
|
||||
|
||||
## Overview
|
||||
# Disclaimer
|
||||
|
||||
ZonyLrcToolX 2.0 is a cross-platform lyric downlaod tool based on CEF.
|
||||
- This tool is for personal learning and research purposes only. The executable binary files are for demonstration purposes only and the source code and its products must not be used for commercial purposes. Otherwise, I [here](https://github.com/real-zony) will not be responsible for any related legal issues.
|
||||
- Any unit or individual that downloads and uses the software resulting in any accidents, negligence, contract breaches, defamation, copyright or intellectual property infringement, and their resulting losses (including but not limited to direct, indirect, incidental or derivative losses), I [here](https://github.com/real-zony) will not bear any legal responsibility.
|
||||
- Users clearly agree to all the terms listed in this statement and fully assume the risks and consequences of using this tool. I [here](https://github.com/real-zony) will not bear any legal responsibility.
|
||||
|
||||
🚧 The current version is under development.
|
||||
🚧 If you want to see the working code, please switch to the 1.0 branch.
|
||||
# Introduction
|
||||
|
||||
## Usage
|
||||
ZonyLrcToolX 4 is a cross-platform lyrics download tool based on CEF. **QQ Group: 337656932**. Detailed video tutorials are available in the group files.
|
||||
|
||||
## Donation
|
||||
🚧 The current version is under development.
|
||||
🚧 If you want to see working code, please switch to the dev branch.
|
||||
|
||||
## Roadmap
|
||||
# Download
|
||||
|
||||
- [ ] Supports cross-platform CLI tools.
|
||||
- [ ] Web GUI based site (local).
|
||||
- [ ] Support plug-in system (Lua Engine).
|
||||
To get the latest version, please visit the **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** page for download.
|
||||
|
||||
## Arch Linux User Repository
|
||||
|
||||
This software has been packaged into [AUR](https://aur.archlinux.org/packages/zonylrctoolsx-bin). Arch Linux and its derivative distribution users can install it with the following command:
|
||||
|
||||
```bash
|
||||
# yay or other AUR Helpers
|
||||
yay -S zonylrctoolsx-bin
|
||||
|
||||
ZonyLrcTools.Cli --help
|
||||
```
|
||||
|
||||
# Usage
|
||||
Detailed usage instructions have been moved to a new documentation site. Please visit [https://docs.myzony.com](https://docs.myzony.com) for complete documentation.
|
||||
|
||||
# Donation
|
||||
[爱发电](https://afdian.net/a/zony-lrc-tools)
|
||||
|
||||
# Star History
|
||||
|
||||
[](https://star-history.com/#real-zony/ZonyLrcToolsX&Timeline)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 247 KiB |
Binary file not shown.
Before Width: | Height: | Size: 211 KiB |
40
docs/zh_CN.md
Normal file
40
docs/zh_CN.md
Normal file
@@ -0,0 +1,40 @@
|
||||
简体中文 | [English](./docs/en_US.md)
|
||||
|
||||
# 免责声明
|
||||
|
||||
- 本工具仅作个人学习研究使用,可运行的二进制文件仅用于演示功能,不得将源码及其产物用于商业用途,否则由此造成的相关法律问题,[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||
- 任何单位或个人因下载使用软件所产生的任何意外、疏忽、合约毁坏、诽谤、版权或知识产权侵犯及其造成的损失 (包括但不限于直接、间接、附带或衍生的损失等),[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||
- 用户明确并同意本声明条款列举的全部内容,对使用本工具可能存在的风险和相关后果将完全由用户自行承担,[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||
|
||||
# 简介
|
||||
|
||||
ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具,**QQ 群:337656932**,群文件里面有详细的视频教程。
|
||||
|
||||
🚧 当前版本正在开发当中。
|
||||
🚧 如果你想查看可以工作的代码,请切换到 dev 分支。
|
||||
|
||||
# 下载
|
||||
|
||||
如果你要获取最新版本,请访问 **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** 页面进行下载。
|
||||
|
||||
## Arch Linux 用户仓库
|
||||
|
||||
本软件已打包到 [AUR](https://aur.archlinux.org/packages/zonylrctoolsx-bin)。Arch Linux 及其衍生发行版用户可用如下命令安装:
|
||||
|
||||
```bash
|
||||
# yay 或其他 AUR Helper
|
||||
yay -S zonylrctoolsx-bin
|
||||
|
||||
ZonyLrcTools.Cli --help
|
||||
```
|
||||
|
||||
# 用法
|
||||
|
||||
软件的具体使用方法已经迁移到了全新的文档站点,请跳转到 [https://docs.myzony.com](https://docs.myzony.com),里面包含完整的说明文档。
|
||||
|
||||
# 捐赠
|
||||
[爱发电](https://afdian.net/a/zony-lrc-tools)
|
||||
|
||||
# Star History
|
||||
|
||||
[](https://star-history.com/#real-zony/ZonyLrcToolsX&Timeline)
|
@@ -81,8 +81,7 @@ namespace ZonyLrcTools.Cli
|
||||
.ConfigureLogging(l => l.AddSerilog())
|
||||
.ConfigureHostConfiguration(builder =>
|
||||
{
|
||||
builder
|
||||
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
|
||||
builder.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
|
||||
.AddYamlFile("config.yaml");
|
||||
})
|
||||
.ConfigureServices((_, services) =>
|
||||
|
@@ -3,10 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyVersion>0.0.0.1</AssemblyVersion>
|
||||
<FileVersion>0.0.0.1</FileVersion>
|
||||
<PackageVersion>0.0.0.1</PackageVersion>
|
||||
<Version>0.0.0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -1,61 +1,61 @@
|
||||
globalOption:
|
||||
# 允许扫描的歌曲文件后缀名。
|
||||
supportFileExtensions:
|
||||
- '*.mp3'
|
||||
- '*.flac'
|
||||
- '*.wav'
|
||||
- '*.m4a'
|
||||
- '*.ogg'
|
||||
- '*.opus'
|
||||
# 网络代理服务设置,仅支持 HTTP 代理。
|
||||
networkOptions:
|
||||
isEnable: false # 是否启用代理。
|
||||
ip: 127.0.0.1 # 代理服务 IP 地址。
|
||||
port: 4780 # 代理服务端口号。
|
||||
updateUrl: https://api.myzony.com/lrc-tools/update # 更新检查地址。
|
||||
|
||||
# 下载器的相关参数配置。
|
||||
provider:
|
||||
# 标签扫描器的相关参数配置。
|
||||
tag:
|
||||
# 支持的标签扫描器。
|
||||
plugin:
|
||||
- name: Taglib # 基于 Taglib 库的标签扫描器。
|
||||
priority: 1 # 优先级,升序排列。
|
||||
- name: FileName # 基于文件名的标签扫描器。
|
||||
priority: 2
|
||||
# 基于文件名扫描器的扩展参数。
|
||||
extensions:
|
||||
# 正则表达式,用于匹配文件名中的作者信息和歌曲信息,可根据
|
||||
# 自己的需求进行调整。
|
||||
regularExpressions: "(?'artist'.+)\\s-\\s(?'name'.+)"
|
||||
# 歌曲标签屏蔽字典替换功能。
|
||||
blockWord:
|
||||
isEnable: false # 是否启用屏蔽字典。
|
||||
filePath: 'BlockWords.json' # 屏蔽字典的路径。
|
||||
# 歌词下载器的相关参数配置。
|
||||
lyric:
|
||||
# 支持的歌词下载器。
|
||||
plugin:
|
||||
- name: NetEase # 基于网易云音乐的歌词下载器。
|
||||
priority: 1 # 优先级,升序排列,改为 -1 时禁用。
|
||||
depth: 10 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
|
||||
additional:
|
||||
isEnableRomanOutput: false # 是否启用罗马音输出,本参数仅当对应歌曲有罗马音歌词时有效。
|
||||
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
||||
priority: 2
|
||||
# depth: 10 # 暂时不支持。
|
||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||
priority: 3
|
||||
depth: 10
|
||||
- name: KuWo # 基于酷我音乐的歌词下载器。
|
||||
priority: 4
|
||||
depth: 10
|
||||
# 歌词下载的一些共有配置参数。
|
||||
config:
|
||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||
isEnableTranslation: true # 是否启用翻译歌词。
|
||||
isOnlyOutputTranslation: false # 是否只输出翻译歌词。
|
||||
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||
fileEncoding: 'utf-8' # 歌词文件的编码格式。
|
||||
# 允许扫描的歌曲文件后缀名。
|
||||
supportFileExtensions:
|
||||
- '*.mp3'
|
||||
- '*.flac'
|
||||
- '*.wav'
|
||||
- '*.m4a'
|
||||
- '*.ogg'
|
||||
- '*.opus'
|
||||
|
||||
# 网络代理服务设置,仅支持 HTTP 代理。
|
||||
networkOptions:
|
||||
isEnable: false # 是否启用代理。
|
||||
ip: 127.0.0.1 # 代理服务 IP 地址。
|
||||
port: 4780 # 代理服务端口号。
|
||||
updateUrl: https://api.myzony.com/lrc-tools/update # 更新检查地址。
|
||||
|
||||
# 下载器的相关参数配置。
|
||||
provider:
|
||||
# 标签扫描器的相关参数配置。
|
||||
tag:
|
||||
# 支持的标签扫描器。
|
||||
plugin:
|
||||
- name: Taglib # 基于 Taglib 库的标签扫描器。
|
||||
priority: 1 # 优先级,升序排列。
|
||||
- name: FileName # 基于文件名的标签扫描器。
|
||||
priority: 2
|
||||
# 基于文件名扫描器的扩展参数。
|
||||
extensions:
|
||||
# 正则表达式,用于匹配文件名中的作者信息和歌曲信息,可根据
|
||||
# 自己的需求进行调整。
|
||||
regularExpressions: "(?'artist'.+)\\s-\\s(?'name'.+)"
|
||||
# 歌曲标签屏蔽字典替换功能。
|
||||
blockWord:
|
||||
isEnable: false # 是否启用屏蔽字典。
|
||||
filePath: 'BlockWords.json' # 屏蔽字典的路径。
|
||||
# 歌词下载器的相关参数配置。
|
||||
lyric:
|
||||
# 支持的歌词下载器。
|
||||
plugin:
|
||||
- name: NetEase # 基于网易云音乐的歌词下载器。
|
||||
priority: 1 # 优先级,升序排列,改为 -1 时禁用。
|
||||
depth: 10 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
|
||||
additional:
|
||||
isEnableRomanOutput: false # 是否启用罗马音输出,本参数仅当对应歌曲有罗马音歌词时有效。
|
||||
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
||||
priority: 2
|
||||
# depth: 10 # 暂时不支持。
|
||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||
priority: 3
|
||||
depth: 10
|
||||
- name: KuWo # 基于酷我音乐的歌词下载器。
|
||||
priority: 4
|
||||
depth: 10
|
||||
# 歌词下载的一些共有配置参数。
|
||||
config:
|
||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||
isEnableTranslation: true # 是否启用翻译歌词。
|
||||
isOnlyOutputTranslation: false # 是否只输出翻译歌词。
|
||||
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||
fileEncoding: 'utf-8' # 歌词文件的编码格式。
|
@@ -16,5 +16,10 @@
|
||||
/// 搜索深度,值越大搜索结果越多,但搜索时间越长。
|
||||
/// </summary>
|
||||
public int Depth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 歌词下载器的扩展属性。
|
||||
/// </summary>
|
||||
public Dictionary<string, string>? Additional { get; set; }
|
||||
}
|
||||
}
|
@@ -19,5 +19,10 @@ namespace ZonyLrcTools.Common.Configuration
|
||||
/// 代理服务器的 端口。
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新检查的 Url。
|
||||
/// </summary>
|
||||
public string UpdateUrl { get; set; } = default!;
|
||||
}
|
||||
}
|
@@ -8,4 +8,9 @@ public class TagInfoOptions
|
||||
/// 屏蔽词功能相关配置。
|
||||
/// </summary>
|
||||
public BlockWordOptions BlockWord { get; set; } = null!;
|
||||
|
||||
public TagInfoProviderOptions GetTagProviderOption(string name)
|
||||
{
|
||||
return Plugin.FirstOrDefault(x => x.Name == name)!;
|
||||
}
|
||||
}
|
@@ -6,6 +6,6 @@ namespace ZonyLrcTools.Common.Configuration
|
||||
|
||||
public int Priority { get; set; }
|
||||
|
||||
public Dictionary<string, string> Extensions { get; set; } = null!;
|
||||
public Dictionary<string, string>? Extensions { get; set; } = null!;
|
||||
}
|
||||
}
|
@@ -47,7 +47,7 @@ namespace ZonyLrcTools.Common.Infrastructure.DependencyInject
|
||||
.AddYamlFile("config.yaml")
|
||||
.Build();
|
||||
|
||||
services.Configure<GlobalOptions>(configuration.GetSection("globalOption"));
|
||||
services.Configure<GlobalOptions>(configuration);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@@ -10,9 +10,11 @@ public interface ILyricsDownloader
|
||||
/// </summary>
|
||||
/// <param name="needDownloadMusicInfos">需要下载的歌词信息。</param>
|
||||
/// <param name="parallelCount">下载线程/并发量。</param>
|
||||
/// <param name="callback">任务完成之后的回调方法。</param>
|
||||
/// <param name="cancellationToken">任务取消标记。</param>
|
||||
Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
|
||||
int parallelCount = 2,
|
||||
int parallelCount = 1,
|
||||
Func<MusicInfo, Task>? callback = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
@@ -10,6 +10,7 @@ using ZonyLrcTools.Common.Infrastructure.Threading;
|
||||
|
||||
namespace ZonyLrcTools.Common.Lyrics;
|
||||
|
||||
/// <inheritdoc cref="ZonyLrcTools.Common.Lyrics.ILyricsDownloader" />
|
||||
public class LyricsDownloader : ILyricsDownloader, ISingletonDependency
|
||||
{
|
||||
private readonly IEnumerable<ILyricsProvider> _lyricsProviders;
|
||||
@@ -38,6 +39,7 @@ public class LyricsDownloader : ILyricsDownloader, ISingletonDependency
|
||||
|
||||
public async Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
|
||||
int parallelCount = 1,
|
||||
Func<MusicInfo, Task>? callback = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _logger.InfoAsync("开始下载歌词文件数据...");
|
||||
@@ -57,12 +59,12 @@ public class LyricsDownloader : ILyricsDownloader, ISingletonDependency
|
||||
{
|
||||
await DownloadAndWriteLyricsAsync(lyricsProvider, info);
|
||||
|
||||
if (info.IsSuccessful)
|
||||
{
|
||||
_logger.LogSuccessful(info);
|
||||
return;
|
||||
}
|
||||
if (!info.IsSuccessful) continue;
|
||||
_logger.LogSuccessful(info);
|
||||
break;
|
||||
}
|
||||
|
||||
if (callback != null) await callback(info);
|
||||
}, cancellationToken), cancellationToken));
|
||||
|
||||
await Task.WhenAll(downloadTasks);
|
||||
|
@@ -8,7 +8,7 @@ using ZonyLrcTools.Common.Lyrics.Providers.KuGou.JsonModel;
|
||||
|
||||
namespace ZonyLrcTools.Common.Lyrics.Providers.KuGou
|
||||
{
|
||||
public class KuGourLyricsProvider : LyricsProvider
|
||||
public class KuGouLyricsProvider : LyricsProvider
|
||||
{
|
||||
public override string DownloaderName => InternalLyricsProviderNames.KuGou;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ZonyLrcTools.Common.Lyrics.Providers.KuGou
|
||||
private const string KuGouGetLyricAccessKeyUrl = @"http://lyrics.kugou.com/search";
|
||||
private const string KuGouGetLyricUrl = @"http://lyrics.kugou.com/download";
|
||||
|
||||
public KuGourLyricsProvider(IWarpHttpClient warpHttpClient,
|
||||
public KuGouLyricsProvider(IWarpHttpClient warpHttpClient,
|
||||
ILyricsItemCollectionFactory lyricsItemCollectionFactory,
|
||||
IOptions<GlobalOptions> options)
|
||||
{
|
@@ -4,13 +4,49 @@ namespace ZonyLrcTools.Common.Lyrics.Providers.KuWo.JsonModel;
|
||||
|
||||
public class SongSearchRequest
|
||||
{
|
||||
[JsonProperty("key")] public string Keyword { get; set; }
|
||||
[JsonProperty("all")] public string Keyword { get; set; }
|
||||
|
||||
[JsonProperty("pn")] public int PageNumber { get; }
|
||||
|
||||
[JsonProperty("rn")] public int PageSize { get; }
|
||||
|
||||
public SongSearchRequest(string name, string artist, int pageNumber = 1, int pageSize = 20)
|
||||
[JsonProperty("ft")] public string Unknown1 { get; } = "music";
|
||||
|
||||
[JsonProperty("newsearch")] public string Unknown2 { get; } = "1";
|
||||
|
||||
[JsonProperty("alflac")] public string Unknown3 { get; } = "1";
|
||||
|
||||
[JsonProperty("itemset")] public string Unknown4 { get; } = "web_2013";
|
||||
|
||||
[JsonProperty("client")] public string Unknown5 { get; } = "kt";
|
||||
|
||||
[JsonProperty("cluster")] public string Unknown6 { get; } = "0";
|
||||
|
||||
[JsonProperty("vermerge")] public string Unknown7 { get; } = "1";
|
||||
|
||||
[JsonProperty("rformat")] public string Unknown8 { get; } = "json";
|
||||
|
||||
[JsonProperty("encoding")] public string Unknown9 { get; } = "utf8";
|
||||
|
||||
[JsonProperty("show_copyright_off")] public string Unknown10 { get; } = "1";
|
||||
|
||||
[JsonProperty("pcmp4")] public string Unknown11 { get; } = "1";
|
||||
|
||||
[JsonProperty("ver")] public string Unknown12 { get; } = "mbox";
|
||||
|
||||
[JsonProperty("plat")] public string Unknown13 { get; } = "pc";
|
||||
|
||||
[JsonProperty("vipver")] public string Unknown14 { get; } = "MUSIC_9.2.0.0_W6";
|
||||
|
||||
[JsonProperty("devid")] public string Unknown15 { get; } = "11404450";
|
||||
|
||||
[JsonProperty("newver")] public string Unknown16 { get; } = "1";
|
||||
|
||||
[JsonProperty("issubtitle")] public string Unknown17 { get; } = "1";
|
||||
|
||||
[JsonProperty("pcjson")] public string Unknown18 { get; } = "1";
|
||||
|
||||
public SongSearchRequest(string name, string artist, int pageNumber = 0, int pageSize = 20)
|
||||
{
|
||||
Keyword = $"{name} {artist}";
|
||||
PageNumber = pageNumber;
|
||||
|
@@ -4,15 +4,14 @@ namespace ZonyLrcTools.Common.Lyrics.Providers.KuWo.JsonModel;
|
||||
|
||||
public class SongSearchResponse
|
||||
{
|
||||
[JsonProperty("code")] public int Code { get; set; }
|
||||
[JsonProperty("TOTAL")] public int TotalCount { get; set; }
|
||||
|
||||
[JsonProperty("data")] public SongSearchResponseInnerData InnerData { get; set; } = null!;
|
||||
|
||||
[JsonProperty("msg")] public string? ErrorMessage { get; set; }
|
||||
[JsonProperty("abslist")]
|
||||
public IList<SongSearchResponseSongDetail> SongList { get; set; }
|
||||
|
||||
public long GetMatchedMusicId(string musicName, string artistName, long? duration)
|
||||
{
|
||||
var prefectMatch = InnerData.SongItems.FirstOrDefault(x => x.Name == musicName && x.Artist == artistName);
|
||||
var prefectMatch = SongList.FirstOrDefault(x => x.Name == musicName && x.Artist == artistName);
|
||||
if (prefectMatch != null)
|
||||
{
|
||||
return prefectMatch.MusicId;
|
||||
@@ -20,27 +19,42 @@ public class SongSearchResponse
|
||||
|
||||
if (duration is null or 0)
|
||||
{
|
||||
return InnerData.SongItems.First().MusicId;
|
||||
return SongList.First().MusicId;
|
||||
}
|
||||
|
||||
return InnerData.SongItems.OrderBy(t => Math.Abs(t.Duration - duration.Value)).First().MusicId;
|
||||
return SongList.OrderBy(t => Math.Abs(t.Duration - duration.Value)).First().MusicId;
|
||||
}
|
||||
}
|
||||
|
||||
public class SongSearchResponseInnerData
|
||||
public class SongSearchResponseSongDetail
|
||||
{
|
||||
[JsonProperty("total")] public string? Total { get; set; }
|
||||
/// <summary>
|
||||
/// 专辑名称。
|
||||
/// </summary>
|
||||
[JsonProperty("ALBUM")]
|
||||
public string Album { get; set; }
|
||||
|
||||
[JsonProperty("list")] public ICollection<SongSearchResponseDetail> SongItems { get; set; } = null!;
|
||||
}
|
||||
/// <summary>
|
||||
/// 歌手名称。
|
||||
/// </summary>
|
||||
[JsonProperty("ARTIST")]
|
||||
public string Artist { get; set; }
|
||||
|
||||
public class SongSearchResponseDetail
|
||||
{
|
||||
[JsonProperty("artist")] public string? Artist { get; set; }
|
||||
/// <summary>
|
||||
/// 歌曲名称。
|
||||
/// </summary>
|
||||
[JsonProperty("SONGNAME")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("name")] public string? Name { get; set; }
|
||||
/// <summary>
|
||||
/// 歌曲的 ID。
|
||||
/// </summary>
|
||||
[JsonProperty("DC_TARGETID")]
|
||||
public long MusicId { get; set; }
|
||||
|
||||
[JsonProperty("rid")] public long MusicId { get; set; }
|
||||
|
||||
[JsonProperty("duration")] public long Duration { get; set; }
|
||||
/// <summary>
|
||||
/// 歌曲的时间长度。
|
||||
/// </summary>
|
||||
[JsonProperty("DURATION")]
|
||||
public long Duration { get; set; }
|
||||
}
|
@@ -13,29 +13,27 @@ public class KuWoLyricsProvider : LyricsProvider
|
||||
{
|
||||
public override string DownloaderName => InternalLyricsProviderNames.KuWo;
|
||||
|
||||
private const string KuWoSearchMusicUrl = @"https://www.kuwo.cn/api/www/search/searchMusicBykeyWord";
|
||||
private const string KuWoSearchMusicUrl = @"https://search.kuwo.cn/r.s";
|
||||
private const string KuWoSearchLyricsUrl = @"https://m.kuwo.cn/newh5/singles/songinfoandlrc";
|
||||
private const string KuWoDefaultToken = "ABCDE12345";
|
||||
|
||||
private readonly IWarpHttpClient _warpHttpClient;
|
||||
private readonly ILyricsItemCollectionFactory _lyricsItemCollectionFactory;
|
||||
private readonly GlobalOptions _options;
|
||||
|
||||
private static readonly ProductInfoHeaderValue UserAgent = new("Chrome", "81.0.4044.138");
|
||||
|
||||
public KuWoLyricsProvider(IWarpHttpClient warpHttpClient,
|
||||
ILyricsItemCollectionFactory lyricsItemCollectionFactory,
|
||||
IOptions<GlobalOptions> options)
|
||||
{
|
||||
_warpHttpClient = warpHttpClient;
|
||||
_lyricsItemCollectionFactory = lyricsItemCollectionFactory;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
protected override async ValueTask<object> DownloadDataAsync(LyricsProviderArgs args)
|
||||
{
|
||||
var songSearchResponse = await _warpHttpClient.GetAsync<SongSearchResponse>(KuWoSearchMusicUrl,
|
||||
new SongSearchRequest(args.SongName, args.Artist, pageSize: _options.Provider.Lyric.GetLyricProviderOption(DownloaderName).Depth),
|
||||
new SongSearchRequest(args.SongName, args.Artist,
|
||||
pageSize: _options.Provider.Lyric.GetLyricProviderOption(DownloaderName).Depth),
|
||||
op =>
|
||||
{
|
||||
op.Headers.UserAgent.Add(UserAgent);
|
||||
@@ -55,7 +53,8 @@ public class KuWoLyricsProvider : LyricsProvider
|
||||
});
|
||||
}
|
||||
|
||||
protected override async ValueTask<LyricsItemCollection> GenerateLyricAsync(object lyricsObject, LyricsProviderArgs args)
|
||||
protected override async ValueTask<LyricsItemCollection> GenerateLyricAsync(object lyricsObject,
|
||||
LyricsProviderArgs args)
|
||||
{
|
||||
await ValueTask.CompletedTask;
|
||||
|
||||
@@ -69,7 +68,8 @@ public class KuWoLyricsProvider : LyricsProvider
|
||||
{
|
||||
var position = double.Parse(l.Position);
|
||||
var positionSpan = TimeSpan.FromSeconds(position);
|
||||
return new LyricsItem(positionSpan.Minutes, double.Parse($"{positionSpan.Seconds}.{positionSpan.Milliseconds}"), l.Text);
|
||||
return new LyricsItem(positionSpan.Minutes,
|
||||
double.Parse($"{positionSpan.Seconds}.{positionSpan.Milliseconds}"), l.Text);
|
||||
});
|
||||
|
||||
var lyricsItemCollection = new LyricsItemCollection(_options.Provider.Lyric.Config);
|
||||
@@ -79,12 +79,7 @@ public class KuWoLyricsProvider : LyricsProvider
|
||||
|
||||
protected virtual void ValidateSongSearchResponse(SongSearchResponse response, LyricsProviderArgs args)
|
||||
{
|
||||
if (response.Code != 200)
|
||||
{
|
||||
throw new ErrorCodeException(ErrorCodes.TheReturnValueIsIllegal, response.ErrorMessage, args);
|
||||
}
|
||||
|
||||
if (response.InnerData.SongItems.Count == 0)
|
||||
if (response.TotalCount == 0)
|
||||
{
|
||||
throw new ErrorCodeException(ErrorCodes.NoMatchingSong, attachObj: args);
|
||||
}
|
||||
|
@@ -4,13 +4,13 @@ namespace ZonyLrcTools.Common.Lyrics.Providers.NetEase.JsonModel
|
||||
{
|
||||
public class GetSongDetailsRequest
|
||||
{
|
||||
public GetSongDetailsRequest(int songId)
|
||||
public GetSongDetailsRequest(long songId)
|
||||
{
|
||||
SongId = songId;
|
||||
SongIds = $"%5B{songId}%5D";
|
||||
}
|
||||
|
||||
[JsonProperty("id")] public int SongId { get; }
|
||||
[JsonProperty("id")] public long SongId { get; }
|
||||
|
||||
[JsonProperty("ids")] public string SongIds { get; }
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ namespace ZonyLrcTools.Common.Lyrics.Providers.NetEase.JsonModel
|
||||
|
||||
[JsonProperty("code")] public int StatusCode { get; set; }
|
||||
|
||||
public int GetFirstMatchSongId(string songName, long? duration)
|
||||
public long GetFirstMatchSongId(string songName, long? duration)
|
||||
{
|
||||
var perfectMatch = Items.SongItems.FirstOrDefault(x => x.Name == songName);
|
||||
if (perfectMatch != null)
|
||||
@@ -44,7 +44,7 @@ namespace ZonyLrcTools.Common.Lyrics.Providers.NetEase.JsonModel
|
||||
/// 歌曲的 Sid (Song Id)。
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 歌曲的演唱者。
|
||||
|
@@ -57,5 +57,25 @@ namespace ZonyLrcTools.Common
|
||||
|
||||
[GeneratedRegex(@"[<>:""/\\|?*]")]
|
||||
private static partial Regex InvalidFilePathRegex();
|
||||
|
||||
public static bool operator ==(MusicInfo? left, MusicInfo? right)
|
||||
{
|
||||
if (left is null && right is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (left is null || right is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return left.FilePath == right.FilePath;
|
||||
}
|
||||
|
||||
public static bool operator !=(MusicInfo? left, MusicInfo? right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
// namespace ZonyLrcTools.Common.MusicScanner;
|
||||
//
|
||||
// public class QQMusicSongListMusicScanner : IMusicScanner
|
||||
// {
|
||||
// }
|
@@ -4,7 +4,6 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>4.0.0.57</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -16,6 +15,7 @@
|
||||
<PackageReference Include="Polly"/>
|
||||
<PackageReference Include="QRCoder"/>
|
||||
<PackageReference Include="TagLibSharp"/>
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -2,14 +2,34 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="ZonyLrcTools.Desktop.App"
|
||||
xmlns:local="using:ZonyLrcTools.Desktop"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia"
|
||||
RequestedThemeVariant="Default"
|
||||
xmlns:ic="using:FluentIcons.Avalonia.Fluent">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator/>
|
||||
<local:ViewLocator />
|
||||
</Application.DataTemplates>
|
||||
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ic:SymbolIconSource x:Key="OpenIcon" Symbol="Open" />
|
||||
<ic:SymbolIconSource x:Key="CodeIcon" Symbol="Code" />
|
||||
<ic:SymbolIconSource x:Key="ChatHelpIcon" Symbol="ChatHelp" />
|
||||
<ic:SymbolIconSource x:Key="SettingsIcon" Symbol="Settings" />
|
||||
<ic:SymbolIconSource x:Key="DownloadIcon" Symbol="ArrowDownload" />
|
||||
<ic:SymbolIconSource x:Key="TagIcon" Symbol="Tag" />
|
||||
|
||||
<FontFamily x:Key="GlobalFontFamily">Microsoft YaHei, Simsun, Arial</FontFamily>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
<styling:FluentAvaloniaTheme />
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{StaticResource GlobalFontFamily}" />
|
||||
</Style>
|
||||
<StyleInclude Source="/Controls/DownloadStatusControl.axaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
@@ -1,13 +1,45 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
using ZonyLrcTools.Common.Infrastructure.Network;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
using ZonyLrcTools.Desktop.Views;
|
||||
|
||||
namespace ZonyLrcTools.Desktop;
|
||||
|
||||
public partial class App : Application
|
||||
public class App : Application
|
||||
{
|
||||
public new static App Current => (App)Application.Current!;
|
||||
public IServiceProvider Services { get; } = ConfigureServices();
|
||||
|
||||
public IOptions<GlobalOptions> GlobalOptions => Services.GetRequiredService<IOptions<GlobalOptions>>();
|
||||
|
||||
private Lazy<ISerializer> _yamlSerializer = new(() => new SerializerBuilder()
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.WithDefaultScalarStyle(ScalarStyle.DoubleQuoted)
|
||||
.Build());
|
||||
|
||||
private static IServiceProvider ConfigureServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.BeginAutoDependencyInject<Program>();
|
||||
services.BeginAutoDependencyInject<IWarpHttpClient>();
|
||||
services.ConfigureConfiguration();
|
||||
services.ConfigureToolService();
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
@@ -19,10 +51,17 @@ public partial class App : Application
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainWindowViewModel(),
|
||||
DataContext = new HomeViewModel(),
|
||||
};
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
public void UpdateConfiguration()
|
||||
{
|
||||
var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.yaml");
|
||||
var yamlString = _yamlSerializer.Value.Serialize(GlobalOptions.Value);
|
||||
File.WriteAllText(configPath, yamlString);
|
||||
}
|
||||
}
|
BIN
src/ZonyLrcTools.Desktop/Assets/logo.ico
Normal file
BIN
src/ZonyLrcTools.Desktop/Assets/logo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
@@ -0,0 +1,92 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:ZonyLrcTools.Desktop.Controls"
|
||||
xmlns:fluent="using:FluentIcons.Avalonia.Fluent">
|
||||
|
||||
<Design.PreviewWith>
|
||||
<controls:DownloadStatusControl Status="Completed" />
|
||||
</Design.PreviewWith>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl">
|
||||
<!-- Set Defaults -->
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<Border Name="IconContainer" Width="20" Height="20">
|
||||
<fluent:SymbolIcon Name="StatusIcon" Symbol="{TemplateBinding Symbol}" />
|
||||
</Border>
|
||||
<TextBlock Text="{TemplateBinding Text}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=NotStarted]">
|
||||
<Setter Property="Symbol" Value="QuestionCircle" />
|
||||
<Setter Property="Text" Value="未下载" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Completed]">
|
||||
<Setter Property="Symbol" Value="CheckmarkCircle" />
|
||||
<Setter Property="Text" Value="已完成" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Failed]">
|
||||
<Setter Property="Symbol" Value="DismissCircle" />
|
||||
<Setter Property="Text" Value="失败" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Completed] /template/ Border#IconContainer">
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:0.5">
|
||||
<KeyFrame Cue="0%">
|
||||
<Setter Property="RotateTransform.Angle" Value="0"/>
|
||||
<Setter Property="ScaleTransform.ScaleX" Value="1"/>
|
||||
<Setter Property="ScaleTransform.ScaleY" Value="1"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="50%">
|
||||
<Setter Property="RotateTransform.Angle" Value="180"/>
|
||||
<Setter Property="ScaleTransform.ScaleX" Value="1.2"/>
|
||||
<Setter Property="ScaleTransform.ScaleY" Value="1.2"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="RotateTransform.Angle" Value="360"/>
|
||||
<Setter Property="ScaleTransform.ScaleX" Value="1"/>
|
||||
<Setter Property="ScaleTransform.ScaleY" Value="1"/>
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl[Status=Failed] /template/ Border#IconContainer">
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:0.5" IterationCount="3">
|
||||
<KeyFrame Cue="0%">
|
||||
<Setter Property="TranslateTransform.X" Value="0"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="25%">
|
||||
<Setter Property="TranslateTransform.X" Value="-2"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="50%">
|
||||
<Setter Property="TranslateTransform.X" Value="0"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="75%">
|
||||
<Setter Property="TranslateTransform.X" Value="2"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="TranslateTransform.X" Value="0"/>
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
|
||||
<Style Selector="controls|DownloadStatusControl /template/ Border#IconContainer">
|
||||
<Setter Property="RenderTransform">
|
||||
<TransformGroup>
|
||||
<RotateTransform/>
|
||||
<ScaleTransform/>
|
||||
<TranslateTransform/>
|
||||
</TransformGroup>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Styles>
|
@@ -0,0 +1,36 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using FluentIcons.Common;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Controls;
|
||||
|
||||
public class DownloadStatusControl : TemplatedControl
|
||||
{
|
||||
public static readonly StyledProperty<DownloadStatus> StatusProperty =
|
||||
AvaloniaProperty.Register<DownloadStatusControl, DownloadStatus>(nameof(Status));
|
||||
|
||||
public static readonly StyledProperty<Symbol> SymbolProperty =
|
||||
AvaloniaProperty.Register<DownloadStatusControl, Symbol>(nameof(Symbol));
|
||||
|
||||
public static readonly StyledProperty<string> TextProperty =
|
||||
AvaloniaProperty.Register<DownloadStatusControl, string>(nameof(Text));
|
||||
|
||||
public DownloadStatus Status
|
||||
{
|
||||
get => GetValue(StatusProperty);
|
||||
set => SetValue(StatusProperty, value);
|
||||
}
|
||||
|
||||
public Symbol Symbol
|
||||
{
|
||||
get => GetValue(SymbolProperty);
|
||||
set => SetValue(SymbolProperty, value);
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => GetValue(TextProperty);
|
||||
set => SetValue(TextProperty, value);
|
||||
}
|
||||
}
|
3
src/ZonyLrcTools.Desktop/FodyWeavers.xml
Normal file
3
src/ZonyLrcTools.Desktop/FodyWeavers.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ReactiveUI />
|
||||
</Weavers>
|
33
src/ZonyLrcTools.Desktop/Helpers/UrlHelper.cs
Normal file
33
src/ZonyLrcTools.Desktop/Helpers/UrlHelper.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Helpers;
|
||||
|
||||
public static class UrlHelper
|
||||
{
|
||||
public static void OpenLink(string url)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
using var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "/bin/sh",
|
||||
Arguments = $"-c \"xdg-open {url.Replace("\"", "\\\"")}\"",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
using var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? url : "open",
|
||||
Arguments = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? url : "",
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
using ZonyLrcTools.Common.Infrastructure.Logging;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Infrastructure.Logging;
|
||||
|
||||
public class SerilogWarpLogger(ILogger<SerilogWarpLogger> logger) : IWarpLogger, ITransientDependency
|
||||
{
|
||||
public Task DebugAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogDebug(message, exception);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task InfoAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogInformation(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task WarnAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogWarning(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ErrorAsync(string message, Exception? exception = null)
|
||||
{
|
||||
logger.LogError(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
50
src/ZonyLrcTools.Desktop/Pages/HomePage.axaml
Normal file
50
src/ZonyLrcTools.Desktop/Pages/HomePage.axaml
Normal file
@@ -0,0 +1,50 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:ZonyLrcTools.Desktop.ViewModels"
|
||||
xmlns:controls="clr-namespace:ZonyLrcTools.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Pages.HomePage"
|
||||
x:DataType="viewModels:HomeViewModel">
|
||||
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Bottom" Margin="10">
|
||||
<TextBlock Text="下载进度:" Margin="0,0,0,5" />
|
||||
<ProgressBar Value="{Binding DownloadProgress}" Maximum="{Binding MaxProgress}" Height="20" />
|
||||
</StackPanel>
|
||||
|
||||
<DataGrid AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
GridLinesVisibility="All"
|
||||
BorderThickness="1"
|
||||
BorderBrush="Gray"
|
||||
ItemsSource="{Binding Songs}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="歌曲名称" Binding="{Binding SongName}" Width="*" />
|
||||
<DataGridTextColumn Header="歌手名称" Binding="{Binding ArtistName}" Width="*" />
|
||||
<DataGridTextColumn Header="文件路径" Binding="{Binding FilePath}" Width="2*" />
|
||||
<DataGridTemplateColumn Header="下载状态" Width="1*">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<controls:DownloadStatusControl Status="{Binding DownloadStatus}" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
|
||||
<DataGrid.Styles>
|
||||
<Style Selector="DataGridColumnHeader">
|
||||
<Setter Property="Background" Value="{DynamicResource ControlFillColorDefault}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimary}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="12,8,12,8" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<Style Selector="DataGridCell">
|
||||
<Setter Property="Padding" Value="12,8" />
|
||||
</Style>
|
||||
</DataGrid.Styles>
|
||||
</DataGrid>
|
||||
</DockPanel>
|
||||
</UserControl>
|
24
src/ZonyLrcTools.Desktop/Pages/HomePage.axaml.cs
Normal file
24
src/ZonyLrcTools.Desktop/Pages/HomePage.axaml.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Pages;
|
||||
|
||||
public partial class HomePage : UserControl
|
||||
{
|
||||
public HomePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (DataContext is HomeViewModel vm)
|
||||
{
|
||||
vm.MaxProgress = 100;
|
||||
vm.DownloadProgress = 0;
|
||||
}
|
||||
}
|
||||
}
|
190
src/ZonyLrcTools.Desktop/Pages/SettingsPage.axaml
Normal file
190
src/ZonyLrcTools.Desktop/Pages/SettingsPage.axaml
Normal file
@@ -0,0 +1,190 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Pages.SettingsPage"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:settings="clr-namespace:ZonyLrcTools.Desktop.ViewModels.Settings"
|
||||
x:DataType="settings:SettingsViewModel">
|
||||
|
||||
<UserControl.Resources>
|
||||
<settings:NullToDefaultValueConverter x:Key="NullToDefaultValueConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Orientation="Vertical" Margin="24" Spacing="8">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto">
|
||||
<Image Width="64" Height="64" HorizontalAlignment="Left"
|
||||
Source="/Assets/logo.ico"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality" />
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
Margin="12,0,0,0" VerticalAlignment="Center">
|
||||
<TextBlock Text="ZonyLrcToolsX" />
|
||||
<TextBlock Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Version, StringFormat='Version {0}'}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Margin="0,20,0,0"
|
||||
Text="软件设置" />
|
||||
<!-- 全局设置 -->
|
||||
<ui:SettingsExpander Header="全局设置" Description="配置软件的全局设置" IconSource="{StaticResource SettingsIcon}">
|
||||
<ui:SettingsExpanderItem Content="是否合并歌词为一行">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsOneLine}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="换行符">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ComboBox ItemsSource="{Binding Config.LineBreakOptions}"
|
||||
SelectedItem="{Binding Config.SelectedLineBreak}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="启用翻译歌词">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsEnableTranslation}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="跳过已经存在的歌词文件(不覆盖)">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsSkipExistLyricFiles}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="文件编码">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ComboBox ItemsSource="{Binding Config.FileEncodingOptions}"
|
||||
SelectedItem="{Binding Config.SelectedFileEncoding}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="只输出翻译歌词">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Config.IsOnlyOutputTranslation}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<!-- 歌词下载器配置 -->
|
||||
<ui:SettingsExpander Header="歌词下载器设置" Description="配置每个单独的歌词下载器参数" IconSource="{StaticResource DownloadIcon}">
|
||||
<ItemsRepeater ItemsSource="{Binding LyricsProviders}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ui:SettingsExpander Margin="0,0,0,10">
|
||||
<ui:SettingsExpander.Header>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</ui:SettingsExpander.Header>
|
||||
|
||||
<ui:SettingsExpanderItem Content="下载器优先级">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<NumericUpDown Value="{Binding Priority, Converter={StaticResource NullToDefaultValueConverter}}" Minimum="-1" Maximum="100" Increment="1" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="搜索深度">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<NumericUpDown Value="{Binding Depth, Converter={StaticResource NullToDefaultValueConverter}}" Minimum="1" Maximum="100" Increment="1" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<!-- 标签设置 -->
|
||||
<ui:SettingsExpander Header="标签读取设置" Description="配置读取音乐文件标签时的一些参数" IconSource="{StaticResource TagIcon}">
|
||||
<!-- Block Word Options -->
|
||||
<ui:SettingsExpanderItem Content="是否启用屏蔽字典">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding Tag.BlockWord.IsEnable}" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="屏蔽字典的路径:">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBox Text="{Binding Tag.BlockWord.FilePath}" Width="200" />
|
||||
<Button Content="浏览" Command="{Binding BrowseBlockWordFileCommand}" CommandParameter="{Binding $parent[Window]}" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpander Header="标签扫描器" Description="配置标签扫描器">
|
||||
<ItemsRepeater ItemsSource="{Binding Tag.TagInfoProviders}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ui:SettingsExpander Margin="0,0,0,10">
|
||||
<ui:SettingsExpander.Header>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</ui:SettingsExpander.Header>
|
||||
|
||||
<ui:SettingsExpanderItem Content="优先级">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<NumericUpDown Value="{Binding Priority , Converter={StaticResource NullToDefaultValueConverter}}" Minimum="-1" Maximum="100" Increment="1" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem Content="扩展配置">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ItemsRepeater ItemsSource="{Binding Extensions}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid ColumnDefinitions="Auto,*" Margin="0,5">
|
||||
<TextBlock Grid.Column="0" Text="{Binding Key}" Margin="0,0,10,0" />
|
||||
<TextBox Grid.Column="1" Text="{Binding Value}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ui:SettingsExpander>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<TextBlock Margin="0,20,0,0"
|
||||
Text="关于" />
|
||||
|
||||
<ui:SettingsExpander Description="获得帮助与支持信息" Header="帮助与支持"
|
||||
IconSource="{StaticResource ChatHelpIcon}">
|
||||
<ui:SettingsExpanderItem>
|
||||
<!-- QQ 群 -->
|
||||
<StackPanel Orientation="Vertical" Spacing="10">
|
||||
<TextBlock Text="QQ 群: 337656932" />
|
||||
<ui:HyperlinkButton NavigateUri="https://docs.myzony.com/">
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<ui:SymbolIcon Symbol="Link" />
|
||||
<TextBlock Text="使用文档" />
|
||||
</StackPanel>
|
||||
</ui:HyperlinkButton>
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<ui:SettingsExpander ActionIconSource="{StaticResource OpenIcon}" Click="OnGitHubClick"
|
||||
Description="获取软件的源代码" Header="GitHub 仓库"
|
||||
IconSource="{StaticResource CodeIcon}" IsClickEnabled="True" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
20
src/ZonyLrcTools.Desktop/Pages/SettingsPage.axaml.cs
Normal file
20
src/ZonyLrcTools.Desktop/Pages/SettingsPage.axaml.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Desktop.Helpers;
|
||||
using ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Pages;
|
||||
|
||||
public partial class SettingsPage : UserControl
|
||||
{
|
||||
public SettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new SettingsViewModel(App.Current.Services.GetRequiredService<IOptions<GlobalOptions>>().Value);
|
||||
}
|
||||
|
||||
private void OnGitHubClick(object? sender, RoutedEventArgs? eventArgs) => UrlHelper.OpenLink("https://github.com/real-zony/ZonyLrcToolsX");
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace ZonyLrcTools.Desktop;
|
||||
|
||||
@@ -10,8 +11,11 @@ class Program
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
|
77
src/ZonyLrcTools.Desktop/ViewModels/HomeViewModel.cs
Normal file
77
src/ZonyLrcTools.Desktop/ViewModels/HomeViewModel.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using ReactiveUI;
|
||||
using ZonyLrcTools.Common;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
public class HomeViewModel : ViewModelBase
|
||||
{
|
||||
private ObservableCollection<SongInfoViewModel> _songs = new();
|
||||
|
||||
public ObservableCollection<SongInfoViewModel> Songs
|
||||
{
|
||||
get => _songs;
|
||||
set => this.RaiseAndSetIfChanged(ref _songs, value);
|
||||
}
|
||||
|
||||
private int _downloadProgress;
|
||||
|
||||
public int DownloadProgress
|
||||
{
|
||||
get => _downloadProgress;
|
||||
set => this.RaiseAndSetIfChanged(ref _downloadProgress, value);
|
||||
}
|
||||
|
||||
private int _maxProgress;
|
||||
|
||||
public int MaxProgress
|
||||
{
|
||||
get => _maxProgress;
|
||||
set => this.RaiseAndSetIfChanged(ref _maxProgress, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class SongInfoViewModel : ReactiveObject
|
||||
{
|
||||
public SongInfoViewModel(MusicInfo info)
|
||||
{
|
||||
_info = info;
|
||||
DownloadStatus = DownloadStatus.NotStarted;
|
||||
|
||||
this.WhenAnyValue(x => x.Info)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(SongName));
|
||||
this.RaisePropertyChanged(nameof(ArtistName));
|
||||
this.RaisePropertyChanged(nameof(FilePath));
|
||||
});
|
||||
}
|
||||
|
||||
private MusicInfo _info;
|
||||
|
||||
public MusicInfo Info
|
||||
{
|
||||
get => _info;
|
||||
set => this.RaiseAndSetIfChanged(ref _info, value);
|
||||
}
|
||||
|
||||
public string SongName => Info.Name;
|
||||
public string ArtistName => Info.Artist;
|
||||
public string FilePath => Info.FilePath;
|
||||
|
||||
private DownloadStatus _downloadStatus;
|
||||
|
||||
public DownloadStatus DownloadStatus
|
||||
{
|
||||
get => _downloadStatus;
|
||||
set => this.RaiseAndSetIfChanged(ref _downloadStatus, value);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DownloadStatus
|
||||
{
|
||||
NotStarted = 1,
|
||||
Completed,
|
||||
Failed
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
public class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
public string Greeting => "Welcome to Avalonia!";
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class BlockWordViewModel : ViewModelBase
|
||||
{
|
||||
public BlockWordViewModel(GlobalOptions options)
|
||||
{
|
||||
IsEnable = options.Provider.Tag.BlockWord.IsEnable;
|
||||
FilePath = options.Provider.Tag.BlockWord.FilePath;
|
||||
|
||||
SubscribeToProperty(this, x => x.IsEnable, x => options.Provider.Tag.BlockWord.IsEnable = x);
|
||||
SubscribeToProperty(this, x => x.FilePath, x => options.Provider.Tag.BlockWord.FilePath = x!);
|
||||
}
|
||||
|
||||
[Reactive] public bool IsEnable { get; set; }
|
||||
|
||||
[Reactive] public string? FilePath { get; set; }
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class GlobalConfigurationViewModel : ViewModelBase
|
||||
{
|
||||
public GlobalConfigurationViewModel(GlobalOptions config)
|
||||
{
|
||||
var globalConfig = config.Provider.Lyric.Config;
|
||||
|
||||
InitializeLineBreakComboBoxItem(globalConfig);
|
||||
|
||||
IsOneLine = globalConfig.IsOneLine;
|
||||
IsEnableTranslation = globalConfig.IsEnableTranslation;
|
||||
IsSkipExistLyricFiles = globalConfig.IsSkipExistLyricFiles;
|
||||
IsOnlyOutputTranslation = globalConfig.IsOnlyOutputTranslation;
|
||||
|
||||
SubscribeToProperty(this, x => x.IsOneLine, x => globalConfig.IsOneLine = x);
|
||||
SubscribeToProperty(this, x => x.IsEnableTranslation, x => globalConfig.IsEnableTranslation = x);
|
||||
SubscribeToProperty(this, x => x.IsSkipExistLyricFiles, x => globalConfig.IsSkipExistLyricFiles = x);
|
||||
SubscribeToProperty(this, x => x.IsOnlyOutputTranslation, x => globalConfig.IsOnlyOutputTranslation = x);
|
||||
}
|
||||
|
||||
[Reactive] public bool IsOneLine { get; set; }
|
||||
|
||||
[Reactive] public bool IsEnableTranslation { get; set; }
|
||||
|
||||
[Reactive] public bool IsSkipExistLyricFiles { get; set; }
|
||||
|
||||
[Reactive] public bool IsOnlyOutputTranslation { get; set; }
|
||||
|
||||
private void InitializeLineBreakComboBoxItem(GlobalLyricsConfigOptions config)
|
||||
{
|
||||
LineBreakOptions =
|
||||
[
|
||||
new TextComboboxItem { Name = "Windows", Value = "\r\n" },
|
||||
new TextComboboxItem { Name = "Unix", Value = "\n" },
|
||||
new TextComboboxItem { Name = "Mac", Value = "\r" }
|
||||
];
|
||||
SelectedLineBreak = LineBreakOptions.FirstOrDefault(x => x.Value == config.LineBreak)
|
||||
?? LineBreakOptions.First();
|
||||
|
||||
FileEncodingOptions = Encoding.GetEncodings()
|
||||
.Select(x => new TextComboboxItem { Name = x.DisplayName, Value = x.Name })
|
||||
.ToList();
|
||||
FileEncodingOptions.Insert(0, new TextComboboxItem { Name = "UTF-8-BOM", Value = "utf-8-bom" });
|
||||
SelectedFileEncoding = FileEncodingOptions.FirstOrDefault(x => x.Value == config.FileEncoding)
|
||||
?? FileEncodingOptions.First();
|
||||
|
||||
this.WhenAnyValue(x => x.SelectedLineBreak)
|
||||
.Subscribe(x => config.LineBreak = x.Value);
|
||||
this.WhenAnyValue(x => x.SelectedFileEncoding)
|
||||
.Subscribe(x => config.FileEncoding = x.Value);
|
||||
}
|
||||
|
||||
public List<TextComboboxItem> LineBreakOptions { get; private set; }
|
||||
|
||||
[Reactive] public TextComboboxItem SelectedLineBreak { get; set; }
|
||||
|
||||
public List<TextComboboxItem> FileEncodingOptions { get; private set; }
|
||||
|
||||
[Reactive] public TextComboboxItem SelectedFileEncoding { get; set; }
|
||||
|
||||
public class TextComboboxItem
|
||||
{
|
||||
public string Name { get; set; } = default!;
|
||||
public string Value { get; set; } = default!;
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class LyricsProviderViewModel : ViewModelBase
|
||||
{
|
||||
public LyricsProviderViewModel(LyricsProviderOptions options)
|
||||
{
|
||||
var globalOptions = App.Current.Services.GetRequiredService<IOptions<GlobalOptions>>().Value;
|
||||
|
||||
Name = options.Name;
|
||||
Priority = options.Priority;
|
||||
Depth = options.Depth;
|
||||
|
||||
SubscribeToProperty(this, x => x.Priority, x => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Priority = x);
|
||||
SubscribeToProperty(this, x => x.Depth, x => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Depth = x);
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
[Reactive] public int Priority { get; set; }
|
||||
|
||||
[Reactive] public int Depth { get; set; }
|
||||
}
|
||||
|
||||
internal class NullToDefaultValueConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null ? System.Convert.ToDecimal(value) : 0;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is decimal dValue)
|
||||
{
|
||||
return System.Convert.ToInt32(dValue);
|
||||
}
|
||||
|
||||
return BindingOperations.DoNothing;
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using ReactiveUI;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
public SettingsViewModel(GlobalOptions globalOptions)
|
||||
{
|
||||
Config = new GlobalConfigurationViewModel(globalOptions);
|
||||
LyricsProviders = new ObservableCollection<LyricsProviderViewModel>(globalOptions.Provider.Lyric.Plugin.Select(p => new LyricsProviderViewModel(p)));
|
||||
Tag = new TagInfoViewModel(globalOptions);
|
||||
BrowseBlockWordFileCommand = ReactiveCommand.CreateFromTask<Window>(BrowseBlockWordFile);
|
||||
}
|
||||
|
||||
public static string Version => typeof(Program).Assembly.GetName().Version!.ToString();
|
||||
|
||||
public TagInfoViewModel Tag { get; }
|
||||
|
||||
public ReactiveCommand<Window, Unit> BrowseBlockWordFileCommand { get; }
|
||||
|
||||
public GlobalConfigurationViewModel Config { get; }
|
||||
|
||||
public ObservableCollection<LyricsProviderViewModel> LyricsProviders { get; }
|
||||
|
||||
private async Task BrowseBlockWordFile(Window parentWindow)
|
||||
{
|
||||
var storage = parentWindow.StorageProvider;
|
||||
if (storage.CanOpen)
|
||||
{
|
||||
var options = new FilePickerOpenOptions
|
||||
{
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = new[]
|
||||
{
|
||||
new FilePickerFileType("JSON")
|
||||
{
|
||||
Patterns = new[] { "*.json" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var files = await storage.OpenFilePickerAsync(options);
|
||||
if (files.Count > 0)
|
||||
{
|
||||
Tag.BlockWord.FilePath = files[0].Path.LocalPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class TagInfoProviderViewModel : ViewModelBase
|
||||
{
|
||||
public TagInfoProviderViewModel(TagInfoProviderOptions options)
|
||||
{
|
||||
var globalOptions = App.Current.Services.GetRequiredService<IOptions<GlobalOptions>>().Value;
|
||||
|
||||
Name = options.Name;
|
||||
Priority = options.Priority;
|
||||
Extensions = options.Extensions == null ? [] : new ObservableCollection<ObservableKeyValuePair>(options.Extensions.Select(x => new ObservableKeyValuePair(x.Key, x.Value)));
|
||||
|
||||
SubscribeToProperty(this, x => x.Priority, x => globalOptions.Provider.Tag.GetTagProviderOption(Name).Priority = x);
|
||||
SubscribeToProperty(this, x => x.Extensions, x => globalOptions.Provider.Tag.GetTagProviderOption(Name).Extensions = x.ToDictionary(pair => pair.Key, pair => pair.Value));
|
||||
}
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
[Reactive] public int Priority { get; set; }
|
||||
|
||||
[Reactive] public ObservableCollection<ObservableKeyValuePair> Extensions { get; set; }
|
||||
}
|
||||
|
||||
public class ObservableKeyValuePair : ViewModelBase
|
||||
{
|
||||
[Reactive] public string Key { get; set; }
|
||||
|
||||
[Reactive] public string Value { get; set; }
|
||||
|
||||
public ObservableKeyValuePair(string key, string value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels.Settings;
|
||||
|
||||
public class TagInfoViewModel : ViewModelBase
|
||||
{
|
||||
public TagInfoViewModel(GlobalOptions options)
|
||||
{
|
||||
BlockWord = new BlockWordViewModel(options);
|
||||
TagInfoProviders = new ObservableCollection<TagInfoProviderViewModel>(options.Provider.Tag.Plugin.Select(p => new TagInfoProviderViewModel(p)));
|
||||
}
|
||||
|
||||
public BlockWordViewModel BlockWord { get; }
|
||||
|
||||
public ObservableCollection<TagInfoProviderViewModel> TagInfoProviders { get; }
|
||||
}
|
@@ -1,7 +1,18 @@
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
public class ViewModelBase : ReactiveObject
|
||||
public abstract class ViewModelBase : ReactiveObject
|
||||
{
|
||||
protected void SubscribeToProperty<TSender, TRet>(TSender sender, Expression<Func<TSender, TRet>> property, Action<TRet> updateGlobalConfigAction)
|
||||
{
|
||||
sender.WhenAnyValue(property)
|
||||
.Subscribe(x =>
|
||||
{
|
||||
updateGlobalConfigAction(x);
|
||||
App.Current.UpdateConfiguration();
|
||||
});
|
||||
}
|
||||
}
|
74
src/ZonyLrcTools.Desktop/Views/MainView.axaml
Normal file
74
src/ZonyLrcTools.Desktop/Views/MainView.axaml
Normal file
@@ -0,0 +1,74 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:fluent="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Views.MainView">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid Name="TitleBarHost"
|
||||
Background="Transparent"
|
||||
ColumnDefinitions="Auto,Auto,*,Auto,0">
|
||||
<Grid.Styles>
|
||||
<Style Selector="Button.AppBarButton">
|
||||
<Setter Property="Padding" Value="8" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Theme" Value="{StaticResource TransparentButton}" />
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<Image Width="20" Height="20" Margin="14"
|
||||
Source="/Assets/logo.ico" IsHitTestVisible="False"
|
||||
IsVisible="{Binding !#FrameView.CanGoBack}"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality" />
|
||||
|
||||
<Button Grid.Column="0" Margin="6"
|
||||
Classes="AppBarButton" Command="{Binding #FrameView.GoBack}"
|
||||
IsEnabled="{Binding #FrameView.CanGoBack}"
|
||||
IsVisible="{Binding #FrameView.CanGoBack}">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="ArrowLeft" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>Back</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="ZonyLrcToolsX" IsHitTestVisible="False" />
|
||||
|
||||
<StackPanel Grid.Column="3"
|
||||
Orientation="Horizontal" Margin="6" Spacing="6">
|
||||
<Button Name="OpenFolderButton" Classes="AppBarButton">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="FolderAdd" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>扫描文件夹</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
<Button Name="DownloadButton" Classes="AppBarButton">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="ArrowDownload" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>下载歌词</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
<Button Name="SettingsButton" Classes="AppBarButton">
|
||||
<Button.Content>
|
||||
<fluent:SymbolIcon Symbol="Settings" />
|
||||
</Button.Content>
|
||||
<ToolTip.Tip>软件设置</ToolTip.Tip>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="1" Grid.ColumnSpan="3"
|
||||
Background="{DynamicResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
CornerRadius="8 8 0 0">
|
||||
<ui:Frame Name="FrameView" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
102
src/ZonyLrcTools.Desktop/Views/MainView.axaml.cs
Normal file
102
src/ZonyLrcTools.Desktop/Views/MainView.axaml.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using DynamicData;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ZonyLrcTools.Common;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
using ZonyLrcTools.Desktop.Pages;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Views;
|
||||
|
||||
public partial class MainView : UserControl
|
||||
{
|
||||
private Window? _window;
|
||||
|
||||
private Frame? _frameView;
|
||||
private Button? _settingsButton;
|
||||
private Button? _openFolderButton;
|
||||
private Button? _downloadButton;
|
||||
|
||||
public MainView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
_window = e.Root as Window;
|
||||
|
||||
_frameView = this.FindControl<Frame>("FrameView");
|
||||
_frameView?.Navigate(typeof(HomePage));
|
||||
|
||||
_settingsButton = this.FindControl<Button>("SettingsButton");
|
||||
if (_settingsButton != null) _settingsButton.Click += OnSettingsButtonClick;
|
||||
|
||||
_openFolderButton = this.FindControl<Button>("OpenFolderButton");
|
||||
if (_openFolderButton != null) _openFolderButton.Click += OnOpenFolderButtonClick;
|
||||
_downloadButton = this.FindControl<Button>("DownloadButton");
|
||||
if (_downloadButton != null) _downloadButton.Click += OnDownloadButtonClick;
|
||||
}
|
||||
|
||||
private async void OnDownloadButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var downloader = App.Current.Services.GetRequiredService<ILyricsDownloader>();
|
||||
if (DataContext is HomeViewModel vm)
|
||||
{
|
||||
var needDownloadMusicInfos = vm.Songs
|
||||
.Where(x => x.DownloadStatus == DownloadStatus.NotStarted)
|
||||
.Select(x => x.Info)
|
||||
.ToList();
|
||||
|
||||
vm.DownloadProgress = 0;
|
||||
vm.MaxProgress = needDownloadMusicInfos.Count;
|
||||
|
||||
await downloader.DownloadAsync(needDownloadMusicInfos, callback: info =>
|
||||
{
|
||||
var song = vm.Songs.FirstOrDefault(x => x.Info == info);
|
||||
if (song != null)
|
||||
{
|
||||
song.DownloadStatus = DownloadStatus.Completed;
|
||||
}
|
||||
|
||||
vm.DownloadProgress++;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnOpenFolderButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var storage = _window?.StorageProvider;
|
||||
var musicInfoLoader = App.Current.Services.GetRequiredService<IMusicInfoLoader>();
|
||||
|
||||
if (storage?.CanOpen == true && DataContext is HomeViewModel vm)
|
||||
{
|
||||
var options = new FolderPickerOpenOptions
|
||||
{
|
||||
SuggestedStartLocation = await storage.TryGetWellKnownFolderAsync(WellKnownFolder.Music)
|
||||
};
|
||||
var folders = await storage.OpenFolderPickerAsync(options);
|
||||
if (folders.Count == 0) return;
|
||||
var folderPath = folders[0].Path.LocalPath;
|
||||
var musicInfos = await musicInfoLoader.LoadAsync(folderPath);
|
||||
|
||||
vm.Songs.Clear();
|
||||
vm.Songs.AddRange(musicInfos.Select(x => new SongInfoViewModel(x!)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSettingsButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_frameView?.CurrentSourcePageType != typeof(SettingsPage))
|
||||
_frameView?.Navigate(typeof(SettingsPage));
|
||||
}
|
||||
}
|
@@ -1,20 +1,16 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:ZonyLrcTools.Desktop.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:views="clr-namespace:ZonyLrcTools.Desktop.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="ZonyLrcTools.Desktop.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
Background="{DynamicResource AcrylicBackgroundFillColorDefaultBrush}"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
Title="ZonyLrcTools.Desktop">
|
||||
|
||||
<Design.DataContext>
|
||||
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||
<vm:MainWindowViewModel/>
|
||||
</Design.DataContext>
|
||||
<Panel>
|
||||
<views:MainView />
|
||||
</Panel>
|
||||
|
||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
|
||||
</Window>
|
||||
</Window>
|
@@ -1,4 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using ZonyLrcTools.Desktop.ViewModels;
|
||||
|
||||
namespace ZonyLrcTools.Desktop.Views;
|
||||
|
||||
@@ -6,6 +7,7 @@ public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
DataContext = new HomeViewModel();
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
@@ -9,7 +9,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\"/>
|
||||
<AvaloniaResource Include="Assets\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -21,5 +20,20 @@
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI"/>
|
||||
<PackageReference Include="FluentAvaloniaUI" />
|
||||
<PackageReference Include="FluentIcons.Avalonia.Fluent" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting"/>
|
||||
<PackageReference Include="ReactiveUI.Fody" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="config.yaml"/>
|
||||
<Content Include="config.yaml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
61
src/ZonyLrcTools.Desktop/config.yaml
Normal file
61
src/ZonyLrcTools.Desktop/config.yaml
Normal file
@@ -0,0 +1,61 @@
|
||||
# 允许扫描的歌曲文件后缀名。
|
||||
supportFileExtensions:
|
||||
- '*.mp3'
|
||||
- '*.flac'
|
||||
- '*.wav'
|
||||
- '*.m4a'
|
||||
- '*.ogg'
|
||||
- '*.opus'
|
||||
|
||||
# 网络代理服务设置,仅支持 HTTP 代理。
|
||||
networkOptions:
|
||||
isEnable: false # 是否启用代理。
|
||||
ip: 127.0.0.1 # 代理服务 IP 地址。
|
||||
port: 4780 # 代理服务端口号。
|
||||
updateUrl: https://api.myzony.com/lrc-tools/update # 更新检查地址。
|
||||
|
||||
# 下载器的相关参数配置。
|
||||
provider:
|
||||
# 标签扫描器的相关参数配置。
|
||||
tag:
|
||||
# 支持的标签扫描器。
|
||||
plugin:
|
||||
- name: Taglib # 基于 Taglib 库的标签扫描器。
|
||||
priority: 1 # 优先级,升序排列。
|
||||
- name: FileName # 基于文件名的标签扫描器。
|
||||
priority: 2
|
||||
# 基于文件名扫描器的扩展参数。
|
||||
extensions:
|
||||
# 正则表达式,用于匹配文件名中的作者信息和歌曲信息,可根据
|
||||
# 自己的需求进行调整。
|
||||
regularExpressions: "(?'artist'.+)\\s-\\s(?'name'.+)"
|
||||
# 歌曲标签屏蔽字典替换功能。
|
||||
blockWord:
|
||||
isEnable: false # 是否启用屏蔽字典。
|
||||
filePath: 'BlockWords.json' # 屏蔽字典的路径。
|
||||
# 歌词下载器的相关参数配置。
|
||||
lyric:
|
||||
# 支持的歌词下载器。
|
||||
plugin:
|
||||
- name: NetEase # 基于网易云音乐的歌词下载器。
|
||||
priority: 1 # 优先级,升序排列,改为 -1 时禁用。
|
||||
depth: 10 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
|
||||
additional:
|
||||
isEnableRomanOutput: false # 是否启用罗马音输出,本参数仅当对应歌曲有罗马音歌词时有效。
|
||||
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
||||
priority: 2
|
||||
# depth: 10 # 暂时不支持。
|
||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||
priority: 3
|
||||
depth: 10
|
||||
- name: KuWo # 基于酷我音乐的歌词下载器。
|
||||
priority: 4
|
||||
depth: 10
|
||||
# 歌词下载的一些共有配置参数。
|
||||
config:
|
||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||
isEnableTranslation: true # 是否启用翻译歌词。
|
||||
isOnlyOutputTranslation: false # 是否只输出翻译歌词。
|
||||
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||
fileEncoding: 'utf-8' # 歌词文件的编码格式。
|
@@ -1,7 +0,0 @@
|
||||
namespace ZonyLrcTools.LocalServer.Contract.Dtos;
|
||||
|
||||
public class PagedListRequestDto
|
||||
{
|
||||
public int PageIndex { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
namespace ZonyLrcTools.LocalServer.Contract.Dtos;
|
||||
|
||||
public class PagedListResultDto<T>
|
||||
{
|
||||
public int TotalCount { get; set; }
|
||||
public int PageIndex { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public List<T> Items { get; set; } = new List<T>();
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
using ZonyLrcTools.LocalServer.Contract.Dtos;
|
||||
using ZonyLrcTools.LocalServer.Services.MusicInfo;
|
||||
using ZonyLrcTools.LocalServer.Services.MusicInfo.Dtos;
|
||||
|
||||
namespace ZonyLrcTools.LocalServer.Controllers;
|
||||
|
||||
[Route("api/music-infos")]
|
||||
public class MusicInfoController : Controller, IMusicInfoService, ITransientDependency
|
||||
{
|
||||
private readonly IMusicInfoService _musicInfoService;
|
||||
|
||||
public MusicInfoController(IMusicInfoService musicInfoService)
|
||||
{
|
||||
_musicInfoService = musicInfoService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public Task<PagedListResultDto<MusicInfoListItemDto>> GetMusicInfoListAsync(MusicInfoListInput input)
|
||||
{
|
||||
return _musicInfoService.GetMusicInfoListAsync(input);
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
using SuperSocket.WebSocket.Server;
|
||||
|
||||
namespace ZonyLrcTools.LocalServer.EventBus;
|
||||
|
||||
public class SuperSocketListener
|
||||
{
|
||||
public async Task ListenAsync()
|
||||
{
|
||||
var host = WebSocketHostBuilder.Create()
|
||||
.UseWebSocketMessageHandler(async (session, message) => { await session.SendAsync(message); })
|
||||
.Build();
|
||||
|
||||
await host.StartAsync();
|
||||
}
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
using ZonyLrcTools.LocalServer.EventBus;
|
||||
|
||||
#region Main Flow
|
||||
|
||||
var app = RegisterAndConfigureServices();
|
||||
await ListenServices();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configure Services
|
||||
|
||||
async Task ListenServices()
|
||||
{
|
||||
await new SuperSocketListener().ListenAsync();
|
||||
await app?.RunAsync()!;
|
||||
}
|
||||
|
||||
WebApplication? RegisterAndConfigureServices()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.WebHost.ConfigureKestrel(k => k.ListenAnyIP(50002));
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.BeginAutoDependencyInject<Program>();
|
||||
|
||||
var insideApp = builder.Build();
|
||||
insideApp.UseSpaStaticFiles(new StaticFileOptions
|
||||
{
|
||||
RequestPath = "",
|
||||
FileProvider = new Microsoft.Extensions.FileProviders
|
||||
.ManifestEmbeddedFileProvider(
|
||||
Assembly.GetExecutingAssembly(), "UiStaticResources"
|
||||
)
|
||||
});
|
||||
|
||||
insideApp.MapControllers();
|
||||
#if !DEBUG
|
||||
insideApp.Lifetime.ApplicationStarted.Register(OpenBrowser);
|
||||
#endif
|
||||
|
||||
return insideApp;
|
||||
}
|
||||
|
||||
void OpenBrowser()
|
||||
{
|
||||
const string url = "http://localhost:50002/index.html";
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
Process.Start("explorer.exe", url);
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
Process.Start("open", url);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
Process.Start("xdg-open", url);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"ZonyLrcTools.LocalServer": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:50002",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
using ZonyLrcTools.LocalServer.Contract.Dtos;
|
||||
|
||||
namespace ZonyLrcTools.LocalServer.Services.MusicInfo.Dtos;
|
||||
|
||||
public class MusicInfoListItemDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
|
||||
public int Status { get; set; }
|
||||
}
|
||||
|
||||
public class MusicInfoListInput : PagedListRequestDto
|
||||
{
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
using ZonyLrcTools.LocalServer.Contract.Dtos;
|
||||
using ZonyLrcTools.LocalServer.Services.MusicInfo.Dtos;
|
||||
|
||||
namespace ZonyLrcTools.LocalServer.Services.MusicInfo;
|
||||
|
||||
public interface IMusicInfoService
|
||||
{
|
||||
Task<PagedListResultDto<MusicInfoListItemDto>> GetMusicInfoListAsync(MusicInfoListInput input);
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
using ZonyLrcTools.LocalServer.Contract.Dtos;
|
||||
using ZonyLrcTools.LocalServer.Services.MusicInfo.Dtos;
|
||||
|
||||
namespace ZonyLrcTools.LocalServer.Services.MusicInfo;
|
||||
|
||||
public class MusicInfoService : ITransientDependency, IMusicInfoService
|
||||
{
|
||||
public async Task<PagedListResultDto<MusicInfoListItemDto>> GetMusicInfoListAsync(MusicInfoListInput input)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
return new PagedListResultDto<MusicInfoListItemDto>
|
||||
{
|
||||
Items = new List<MusicInfoListItemDto>
|
||||
{
|
||||
new MusicInfoListItemDto
|
||||
{
|
||||
Name = "测试歌曲",
|
||||
Size = 1024,
|
||||
Status = 1
|
||||
},
|
||||
new MusicInfoListItemDto
|
||||
{
|
||||
Name = "测试歌曲2",
|
||||
Size = 1024,
|
||||
Status = 1
|
||||
},
|
||||
new MusicInfoListItemDto
|
||||
{
|
||||
Name = "测试歌曲3",
|
||||
Size = 1024,
|
||||
Status = 1
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper"/>
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions"/>
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
|
||||
<PackageReference Include="Serilog.Extensions.Hosting"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console"/>
|
||||
<PackageReference Include="Serilog.Sinks.File"/>
|
||||
<PackageReference Include="SuperSocket.WebSocket"/>
|
||||
<PackageReference Include="SuperSocket.WebSocket.Server"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="UiStaticResources\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- <ItemGroup>-->
|
||||
<!-- <ProjectReference Include="..\ZonyLrcTools.Cli\ZonyLrcTools.Cli.csproj" />-->
|
||||
<!-- </ItemGroup>-->
|
||||
|
||||
</Project>
|
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"serverOptions": {
|
||||
"name": "ZonyLRcToolsServer",
|
||||
"listeners": [
|
||||
{
|
||||
"ip": "Any",
|
||||
"port": 50001
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@@ -5,12 +5,14 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZonyLrcTools.Common.Album;
|
||||
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Album
|
||||
{
|
||||
public class QQMusicAlbumDownloaderTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public async Task DownloadDataAsync_Test()
|
||||
{
|
||||
var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumProvider>>()
|
||||
|
@@ -17,7 +17,7 @@ public class KuWoLyricsProviderTests : TestBase
|
||||
.FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.KuWo);
|
||||
}
|
||||
|
||||
[Fact(Skip = "Not Working")]
|
||||
[Fact]
|
||||
[Trait("LyricsProvider ", "KuGou")]
|
||||
public async Task DownloadAsync_Test()
|
||||
{
|
||||
@@ -26,7 +26,7 @@ public class KuWoLyricsProviderTests : TestBase
|
||||
lyric.IsPruneMusic.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact(Skip = "Not Working")]
|
||||
[Fact]
|
||||
public async Task DownloadAsync_Source_Null_Test()
|
||||
{
|
||||
var lyric = await _kuwoLyricsProvider.DownloadAsync("Concerto for Piano and Orchestra No. 12 in A major, K414 - 1. Allegro",
|
||||
|
@@ -4,5 +4,4 @@ Enhancement: None
|
||||
|
||||
Fixed Bugs:
|
||||
|
||||
- 修复了网易云音歌词下载功能无效的问题。- [243a0e2](https://github.com/real-zony/ZonyLrcToolsX/commit/243a0e2559d3d9925dfcb96d11e2c1b1372363e6)
|
||||
- 修复了日志不正常显示的问题。
|
||||
- 修复了酷我音乐下载歌词失败的问题。- [fea5e11](https://github.com/real-zony/ZonyLrcToolsX/commit/fea5e1124ffde5f6cfe0b993e96fdfe04e1b7892)
|
||||
|
Reference in New Issue
Block a user