20 Commits

Author SHA1 Message Date
real-zony
2ce7b00d14 ci: Release new version. 2023-12-12 22:47:19 +08:00
real-zony
ad6ea732f8 Merge remote-tracking branch 'origin/dev' into dev 2023-12-12 22:46:20 +08:00
real-zony
06bf733ae9 fix: Fixed the issue where logs were not displaying. 2023-12-12 22:45:37 +08:00
Zony
558cca3c0e Update release.md 2023-12-12 22:07:20 +08:00
Zony
93cc31b1e2 Update release.md 2023-12-12 22:06:56 +08:00
real-zony
dfe18d8541 ci: Fixed the script error. 2023-12-12 22:05:12 +08:00
real-zony
40b73c3dfa chore: Triggered the release of a new version. 2023-12-12 22:02:45 +08:00
real-zony
243a0e2559 fix: Fixed the issue where NetEase Cloud Music could not download lyrics. 2023-12-12 21:56:49 +08:00
real-zony
d83bff1516 build: Upgrade all packages. 2023-12-12 21:55:35 +08:00
real-zony
8d946ebbd5 build: Upgraded the dependency framework to .NET 8.0. 2023-12-12 19:22:50 +08:00
Zony
4c3d554885 Merge pull request #147 from travislee89/dev
Support for more file extensions; Building arm64 binaries
2023-12-11 20:45:28 +08:00
Travis Lee
14c78f9a83 feat: build arm64 binaries 2023-12-11 15:36:12 +08:00
Travis Lee
b9d6da5f72 feat: support file extensions m4a, ogg and opus 2023-12-11 15:35:55 +08:00
real-zony
c8ffbc55af fix: Fixed the issue mentioned in #145.
Added a new line break.('\n')
2023-09-11 09:49:02 +08:00
real-zony
b0835dcf01 docs: Adjusted README.md . 2023-08-13 22:31:35 +08:00
real-zony
4e1c4d1519 chore: Corrected description information. 2023-08-13 21:25:43 +08:00
real-zony
3d96b96edf build: Fixed NuGet package reference issue. 2023-08-13 21:24:11 +08:00
real-zony
7f81a3edea refactor: Managed dependencies with unified package management. 2023-07-14 21:20:01 +08:00
real-zony
24624a6d21 feat: Added Avalonia UI project. 2023-07-14 19:01:53 +08:00
real-zony
d860632327 chore: Removed the frontend web page code. 2023-07-14 19:01:25 +08:00
51 changed files with 313 additions and 6684 deletions

46
Directory.Packages.props Normal file
View File

@@ -0,0 +1,46 @@
<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>
</Project>

193
README.md
View File

@@ -15,194 +15,11 @@ ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具,**QQ 群:337
# 下载 # 下载
工具会执行每日构建动作,请访问 **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** 页面进行下载。 如果你要获取最新版本,请访问 **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** 页面进行下载。
# 用法 # 用法
Windows 用户请在软件目录当中,按住 Shift + 右键呼出菜单,然后选择 PowerShell/命令提示符/Windows 终端,根据下述说明执行命令即可 软件的具体使用方法已经迁移到了全新的文档站点,请跳转到 [https://docs.myzony.com](https://docs.myzony.com),里面包含完整的说明文档
macOS 和 Linux 用户请打开终端,切换到软件目录,一样执行命令即可。
## 子命令
### 下载功能
子命令为 `download`,可用于下载歌词数据和专辑图像,支持多个下载器进行下载。
```shell
.\ZonyLrcTools.Cli.exe download -d|dir <WAIT_SCAN_DIRECTORY> [-l|--lyric] [-a|--album] [-n|--number]
.\ZonyLrcTools.Cli.exe download -h|--help
```
**例子**
```shell
# 下载歌词
.\ZonyLrcTools.Cli.exe download -d "C:\歌曲目录" -l -n 2
# 下载专辑封面
.\ZonyLrcTools.Cli.exe download -d "C:\歌曲目录" -a -n 2
```
#### 指定歌曲文件源
目前程序支持从本地目录、CSV 文件、网易云歌单获取歌曲数据,然后下载 LRC 歌词数据。
指定歌词源的参数是 `-sc|--scanner`,可以通过这个参数指定歌曲信息的来源。它拥有以下选值,默认情况下是 `local`
```shell
-sc local # 从目录获取歌曲信息。
-sc netease # 从网易云歌单获取歌曲信息。
-sc csv # 从 CSV 文件获取歌曲信息。
```
**从网易云歌单获取歌曲数据**
完整的命令如下:
```shell
.\ZonyLrcTools.Cli.exe download -sc netease -o "D:\TempFiles" -s "7224428149" -l
```
其中 `-sc` 参数用于指定歌词信息的来源是网易云歌单; `-o` 参数指定的是歌词文件的输出目录,请尽量使用绝对路径; `-s` 参数指定的的是歌单的 ID该 ID 可以从网页版的网易云音乐获得。
例如获取地址 [https://music.163.com/#/playlist?id=158010361](https://music.163.com/#/playlist?id=158010361) 的歌单信息,那么歌单 ID 就应该传递 158010361。
如果你需要获取多个歌单的歌曲数据,请在传递歌单 ID 时使用分号 `;` 指定多个 ID例如下面的命令就是下载两个歌单的歌曲数据。
```shell
.\ZonyLrcTools.Cli.exe download -sc netease -o "D:\TempFiles" -s "7224428149;158010361" -l
```
由于网易云音乐的限制,要想获取完整的歌单信息,必须扫码登录程序,还是以最上面的为例,我需要下载歌单内的歌词数据,就必须扫码之后程序才会执行。
![image-20230328223155280](./README.assets/image-20230328223155280.png)
**从 CSV 文件获取歌曲数据**
应 [Issue 126](https://github.com/real-zony/ZonyLrcToolsX/issues/126) 的请求,增加了从 CSV 获取歌曲信息的方式。这样可以在没有原始歌曲的情况下载歌词数据。
示例命令:
```shell
.\ZonyLrcTools.Cli.exe download -sc csv -o "D:\TempFiles" -f "D:\TempFiles\demo.csv" -l
```
其中 `-f` 参数用于指定 csv 文件的路径csv 文件的格式应该如下所示,保证第一行是列信息,一共要包含两列。
```csv
Song,Artist
刀马旦,李玟
发如雪,周杰伦
说书人,寅子
爱的供养,张国荣
七里香,周杰伦
```
### 加密格式转换
子命令为 `util`,可用于转换部分加密歌曲,**仅供个人研究学习使用,思路与源码都来自于网络**。
具体支持的格式请参考项目 [MusicDecrypto](https://github.com/davidxuang/MusicDecrypto/blob/master/MusicDecrypto.Library/DecryptoFactory.cs#L23),本工具仅做一个集成,替换掉原本自己的一些实现。现在不需要指定对应的类型参数,程序会自动根据文件后缀选择适合的解密算法。
命令只需要一个参数 `-s`,指定需要转换的文件夹或者是文件路径。
```shell
.\ZonyLrcTools.Cli.exe util -s D:\CloudMusic
```
## 配置文件
程序的所有的配置信息,都在 `config.yaml` 进行更改,下面标注了各个配置的说明。
其中是否开启的可选项为 `true` 或者 `false`,等同于中文的是和否。
```yaml
globalOption:
# 允许扫描的歌曲文件后缀名。
supportFileExtensions:
- '*.mp3'
- '*.flac'
- '*.wav'
# 网络代理服务设置,仅支持 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 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
- 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' # 歌词文件的编码格式。
```
### 支持的编码格式
详细信息请参考: [MSDN Encoding 列表](https://learn.microsoft.com/en-us/dotnet/api/System.Text.Encoding.GetEncodings?view=net-6.0#examples),使用 `identifier and name` 作为参数值填入 `config.yaml` 文件当中的 `fileEncoding`
> 针对 UTF-8 BOM 格式,程序进行了特殊处理,请在 `fileEncoding` 里面填写 "utf-8-bom" 以支持。
### 支持的歌词源
| 歌词源 | 默认优先级 |
| ---------- | ---------- |
| 网易云音乐 | 1 |
| QQ 音乐 | 2 |
| 酷狗音乐 | 3 |
| 酷我音乐 | 4 |
## 屏蔽字典
屏蔽字典适用于网易云音乐歌词下载,针对某些单词,网易云音乐使用了 * 号进行屏蔽,这个时候可以使用屏蔽字典,设置歌曲名的关键词替换。例如有一首歌曲叫做 *Fucking ABC* ,这个时候网易云实际的名字是 *Fu****ing* ,用户只需要在屏蔽字典加入替换逻辑即可,例如:
```json
{
"Fuckking": "Fu****ing"
}
```
屏蔽字典默认路径为程序所在目录的 *BlockWords.json* 文件,用户可以在 *appsettings.json* 文件中配置其他路径。
# 捐赠 # 捐赠
@@ -211,9 +28,3 @@ globalOption:
# Star History # Star History
[![Star History Chart](https://api.star-history.com/svg?repos=real-zony/ZonyLrcToolsX&type=Timeline)](https://star-history.com/#real-zony/ZonyLrcToolsX&Timeline) [![Star History Chart](https://api.star-history.com/svg?repos=real-zony/ZonyLrcToolsX&type=Timeline)](https://star-history.com/#real-zony/ZonyLrcToolsX&Timeline)
# 路线图
- [x] 支持跨平台的 CLI 工具。
- [x] 基于 Web GUI 的操作站点。
- [ ] 支持插件系统(Lua 引擎)。

View File

@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.LocalServer",
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Common", "src\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj", "{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Common", "src\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj", "{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Desktop", "src\ZonyLrcTools.Desktop\ZonyLrcTools.Desktop.csproj", "{DC92902B-4303-4E43-AFB3-3F93FD3986AD}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -37,6 +39,10 @@ Global
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.Build.0 = Release|Any CPU {9B42E4CA-61AA-4798-8D2B-2D8A7035EB67}.Release|Any CPU.Build.0 = Release|Any CPU
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC92902B-4303-4E43-AFB3-3F93FD3986AD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -49,5 +55,6 @@ Global
{FFBD3200-568F-455B-8390-5E76C51D522C} = {AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B} {FFBD3200-568F-455B-8390-5E76C51D522C} = {AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}
{2875A08A-FFD6-4863-B815-5384DCFE88FC} = {C29FB05C-54B1-4020-94D2-87E192EB2F98} {2875A08A-FFD6-4863-B815-5384DCFE88FC} = {C29FB05C-54B1-4020-94D2-87E192EB2F98}
{9B42E4CA-61AA-4798-8D2B-2D8A7035EB67} = {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 EndGlobalSection
EndGlobal EndGlobal

View File

@@ -46,7 +46,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
[Option("-a|--album", CommandOptionType.NoValue, Description = "指定程序需要下载专辑图像。")] [Option("-a|--album", CommandOptionType.NoValue, Description = "指定程序需要下载专辑图像。")]
public bool DownloadAlbum { get; set; } public bool DownloadAlbum { get; set; }
[Option("-n|--number", CommandOptionType.SingleValue, Description = "指定下载时候的线程数量。(默认值 2)")] [Option("-n|--number", CommandOptionType.SingleValue, Description = "指定下载时候的线程数量。(默认值 1)")]
public int ParallelNumber { get; set; } = 1; public int ParallelNumber { get; set; } = 1;
#endregion #endregion

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils; using McMaster.Extensions.CommandLineUtils;
@@ -79,7 +78,7 @@ namespace ZonyLrcTools.Cli
private static Task<int> BuildHostedService(string[] args) private static Task<int> BuildHostedService(string[] args)
{ {
return new HostBuilder() return new HostBuilder()
.ConfigureLogging(builder => builder.AddSerilog()) .ConfigureLogging(l => l.AddSerilog())
.ConfigureHostConfiguration(builder => .ConfigureHostConfiguration(builder =>
{ {
builder builder

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<AssemblyVersion>0.0.0.1</AssemblyVersion> <AssemblyVersion>0.0.0.1</AssemblyVersion>
<FileVersion>0.0.0.1</FileVersion> <FileVersion>0.0.0.1</FileVersion>
@@ -10,37 +10,34 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.2" /> <PackageReference Include="McMaster.Extensions.CommandLineUtils"/>
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine" Version="4.0.2" /> <PackageReference Include="McMaster.Extensions.Hosting.CommandLine"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> <PackageReference Include="Serilog.Extensions.Hosting"/>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.Async"/>
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="Serilog.Sinks.Console"/>
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.File"/>
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" /> <PackageReference Include="System.Text.Encoding.CodePages"/>
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="appsettings.json" /> <None Remove="appsettings.json"/>
<None Remove="Resources\error_msg.json" /> <None Remove="Resources\error_msg.json"/>
<Content Include="Resources\error_msg.json"> <Content Include="Resources\error_msg.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="BlockWords.json" /> <None Remove="BlockWords.json"/>
<Content Include="BlockWords.json"> <Content Include="BlockWords.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="config.yaml" /> <None Remove="config.yaml"/>
<Content Include="config.yaml"> <Content Include="config.yaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj" /> <ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -4,6 +4,9 @@ globalOption:
- '*.mp3' - '*.mp3'
- '*.flac' - '*.flac'
- '*.wav' - '*.wav'
- '*.m4a'
- '*.ogg'
- '*.opus'
# 网络代理服务设置,仅支持 HTTP 代理。 # 网络代理服务设置,仅支持 HTTP 代理。
networkOptions: networkOptions:
isEnable: false # 是否启用代理。 isEnable: false # 是否启用代理。

View File

@@ -1,4 +1,4 @@
$Platforms = @('win-x64', 'linux-x64', 'osx-x64') $Platforms = @('win-x64', 'linux-x64', 'osx-x64', 'win-arm64', 'linux-arm64', 'osx-arm64')
if (-not (Test-Path ./TempFiles)) { if (-not (Test-Path ./TempFiles)) {
New-Item -ItemType Directory -Path ./TempFiles | Out-Null New-Item -ItemType Directory -Path ./TempFiles | Out-Null

View File

@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
Platforms=('win-x64' 'linux-x64' 'osx-x64') Platforms=('win-x64' 'linux-x64' 'osx-x64' 'win-arm64' 'linux-arm64' 'osx-arm64')
if ! [ -d './TempFiles' ]; if ! [ -d './TempFiles' ];
then then
@@ -12,9 +12,9 @@ for platform in "${Platforms[@]}"
do do
dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:DebugType=none --self-contained true || exit 1 dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:DebugType=none --self-contained true || exit 1
cd ./bin/Release/net7.0/"$platform"/publish/ || exit 1 cd ./bin/Release/net8.0/"$platform"/publish/ || exit 1
zip -r ./ZonyLrcTools_"$platform"_"${PUBLISH_VERSION}".zip ./ || exit 1 zip -r ./ZonyLrcTools_"$platform"_"${PUBLISH_VERSION}".zip ./ || exit 1
cd ../../../../../ || exit 1 cd ../../../../../ || exit 1
mv ./bin/Release/net7.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$PUBLISH_VERSION".zip ./TempFiles mv ./bin/Release/net8.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$PUBLISH_VERSION".zip ./TempFiles
done done

View File

@@ -1,10 +1,22 @@
namespace ZonyLrcTools.Common.Lyrics; namespace ZonyLrcTools.Common.Lyrics;
/// <summary>
/// 歌词下载核心逻辑的接口定义。
/// </summary>
public interface ILyricsDownloader public interface ILyricsDownloader
{ {
/// <summary>
/// 使用给定的歌词信息下载歌词,并输出文件到指定的路径。
/// </summary>
/// <param name="needDownloadMusicInfos">需要下载的歌词信息。</param>
/// <param name="parallelCount">下载线程/并发量。</param>
/// <param name="cancellationToken">任务取消标记。</param>
Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos, Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
int parallelCount = 2, int parallelCount = 2,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
/// <summary>
/// 获取目前可用的歌词下载器。
/// </summary>
IEnumerable<ILyricsProvider> AvailableProviders { get; } IEnumerable<ILyricsProvider> AvailableProviders { get; }
} }

View File

@@ -17,7 +17,7 @@ namespace ZonyLrcTools.Common.Lyrics.Providers.NetEase
private readonly ILyricsItemCollectionFactory _lyricsItemCollectionFactory; private readonly ILyricsItemCollectionFactory _lyricsItemCollectionFactory;
private readonly GlobalOptions _options; private readonly GlobalOptions _options;
private const string NetEaseSearchMusicUrl = @"https://music.163.com/weapi/cloudsearch/get/web"; private const string NetEaseSearchMusicUrl = @"https://music.163.com/weapi/search/get";
private const string NetEaseGetLyricUrl = @"https://music.163.com/weapi/song/lyric?csrf_token="; private const string NetEaseGetLyricUrl = @"https://music.163.com/weapi/song/lyric?csrf_token=";
private const string NetEaseRequestReferer = @"https://music.163.com"; private const string NetEaseRequestReferer = @"https://music.163.com";

View File

@@ -139,7 +139,7 @@ public class NetEaseMusicSongListMusicScanner : ISingletonDependency
var asciiQrCodeString = qrCode.GetGraphic(1, drawQuietZones: false); var asciiQrCodeString = qrCode.GetGraphic(1, drawQuietZones: false);
_logger.LogInformation("请使用网易云 APP 扫码登录:"); _logger.LogInformation("请使用网易云 APP 扫码登录:");
_logger.LogInformation(asciiQrCodeString); _logger.LogInformation("\n{AsciiQrCodeString}", asciiQrCodeString);
// Wait for login success. // Wait for login success.
var isLogin = false; var isLogin = false;

View File

@@ -1,26 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>4.0.0.55</Version> <Version>4.0.0.57</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http"/>
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" /> <PackageReference Include="MusicDecrypto.Library"/>
<PackageReference Include="MusicDecrypto.Library" Version="2.2.0" /> <PackageReference Include="Newtonsoft.Json"/>
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" /> <PackageReference Include="NetEscapades.Configuration.Yaml"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Polly"/>
<PackageReference Include="Polly" Version="7.2.3" /> <PackageReference Include="QRCoder"/>
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="TagLibSharp"/>
<PackageReference Include="TagLibSharp" Version="2.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Lyrics\Providers\Kugeci\KugeciDownloader.cs" /> <Compile Remove="Lyrics\Providers\Kugeci\KugeciDownloader.cs"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,15 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ZonyLrcTools.Desktop.App"
xmlns:local="using:ZonyLrcTools.Desktop"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

View File

@@ -0,0 +1,28 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using ZonyLrcTools.Desktop.ViewModels;
using ZonyLrcTools.Desktop.Views;
namespace ZonyLrcTools.Desktop;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -0,0 +1,23 @@
using Avalonia;
using Avalonia.ReactiveUI;
using System;
namespace ZonyLrcTools.Desktop;
class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
.UseReactiveUI();
}

View File

@@ -0,0 +1,27 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using ZonyLrcTools.Desktop.ViewModels;
namespace ZonyLrcTools.Desktop;
public class ViewLocator : IDataTemplate
{
public Control Build(object data)
{
var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object data)
{
return data is ViewModelBase;
}
}

View File

@@ -0,0 +1,6 @@
namespace ZonyLrcTools.Desktop.ViewModels;
public class MainWindowViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia!";
}

View File

@@ -0,0 +1,7 @@
using ReactiveUI;
namespace ZonyLrcTools.Desktop.ViewModels;
public class ViewModelBase : ReactiveObject
{
}

View File

@@ -0,0 +1,20 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ZonyLrcTools.Desktop.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
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>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Window>

View File

@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace ZonyLrcTools.Desktop.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<Folder Include="Models\"/>
<AvaloniaResource Include="Assets\**"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia"/>
<PackageReference Include="Avalonia.Desktop"/>
<PackageReference Include="Avalonia.Themes.Fluent"/>
<PackageReference Include="Avalonia.Fonts.Inter"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics"/>
<PackageReference Include="Avalonia.ReactiveUI"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="ZonyLrcTools.Desktop.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

View File

@@ -1,37 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest> <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" /> <PackageReference Include="AutoMapper"/>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection"/>
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="7.0.5" /> <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded"/>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="7.0.5" /> <PackageReference Include="Microsoft.Extensions.Hosting"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" /> <PackageReference Include="Serilog.Extensions.Hosting"/>
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.Console"/>
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.File"/>
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="SuperSocket.WebSocket"/>
<PackageReference Include="SuperSocket.WebSocket" Version="2.0.0-beta.11" /> <PackageReference Include="SuperSocket.WebSocket.Server"/>
<PackageReference Include="SuperSocket.WebSocket.Server" Version="2.0.0-beta.11" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="UiStaticResources\**" /> <EmbeddedResource Include="UiStaticResources\**"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj" /> <ProjectReference Include="..\ZonyLrcTools.Common\ZonyLrcTools.Common.csproj"/>
</ItemGroup> </ItemGroup>
<!-- <ItemGroup>--> <!-- <ItemGroup>-->
<!-- <ProjectReference Include="..\ZonyLrcTools.Cli\ZonyLrcTools.Cli.csproj" />--> <!-- <ProjectReference Include="..\ZonyLrcTools.Cli\ZonyLrcTools.Cli.csproj" />-->
<!-- </ItemGroup>--> <!-- </ItemGroup>-->
</Project> </Project>

View File

@@ -1,3 +0,0 @@
> 1%
last 2 versions
not dead

213
src/ui/.gitignore vendored
View File

@@ -1,213 +0,0 @@
### VisualStudioCode template
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Vue template
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
# TODO: where does this rule come from?
docs/_book
# TODO: where does this rule come from?
test/

View File

@@ -1,19 +0,0 @@
# ui
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@@ -1,5 +0,0 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View File

@@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

View File

@@ -1,28 +0,0 @@
{
"name": "ui",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"axios": "^1.1.3",
"core-js": "^3.8.3",
"vue": "^2.6.14",
"vue-router": "^3.5.1",
"vuetify": "^2.6.0",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"sass": "^1.32.7",
"sass-loader": "^12.0.0",
"vue-cli-plugin-vuetify": "~2.5.8",
"vue-template-compiler": "^2.6.14",
"vuetify-loader": "^1.7.0"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -1,80 +0,0 @@
<template>
<v-app id="inspire">
<v-navigation-drawer
v-model="drawer"
app
>
<v-sheet
color="grey lighten-4"
class="pa-4"
>
<div>ZonyLrcTools-X</div>
</v-sheet>
<v-divider></v-divider>
<v-list>
<v-list-item
v-for="[icon, text, to] in links"
:key="icon"
:to="to"
link
>
<v-list-item-icon>
<v-icon>{{ icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-app-bar app>
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
<v-toolbar-title>ZonyLrcToolsX</v-toolbar-title>
</v-app-bar>
<v-main>
<v-container
class="py-8 px-6"
fluid
>
<router-view></router-view>
</v-container>
</v-main>
</v-app>
</template>
<script>
import Socket from "@/communication/socket";
import {mapMutations} from "vuex"
export default {
data: () => ({
drawer: null,
links: [
['mdi-play-circle', '开始', '/'],
['mdi-multiplication-box', '设置', '/setting'],
['mdi-information', '关于', '/about'],
],
}),
created() {
Socket.$on("message", this.handleGetMessage);
},
beforeDestroy() {
Socket.$off("message", this.handleGetMessage);
},
methods: {
...mapMutations({
setWsRes: "ws/setWsRes",
}),
handleGetMessage(msg) {
this.setWsRes(JSON.parse(msg));
}
}
}
</script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>

Before

Width:  |  Height:  |  Size: 539 B

View File

@@ -1,5 +0,0 @@
import Vue from "vue";
const eventBus = new Vue();
export default eventBus;

View File

@@ -1,31 +0,0 @@
import Vue from 'vue'
import eventBus from "@/communication/eventbus";
// const wsUrl = process.env.VUE_APP_WS_URL;
const wsUrl = "ws://127.0.0.1:50001"
let socket = new WebSocket(wsUrl);
const emitter = new Vue({
methods: {
send(message) {
if (socket.readyState === 1) {
socket.send(JSON.stringify(message));
}
}, connect() {
socket = new WebSocket(wsUrl);
socket.onmessage = (msg) => {
let event = JSON.parse(msg.data);
eventBus.$emit(event.action, event);
};
socket.onerror = (err) => {
eventBus.$emit('error', err);
};
socket.onclose = () => {
emitter.connect()
};
}
}
})
emitter.connect();
export default emitter;

View File

@@ -1,13 +0,0 @@
<template>
</template>
<script>
export default {
name: "setting"
}
</script>
<style scoped>
</style>

View File

@@ -1,14 +0,0 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify'
Vue.config.productionTip = false
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app')

View File

@@ -1,21 +0,0 @@
import Vue from 'vue';
import Vuetify from 'vuetify/lib/framework';
Vue.use(Vuetify);
export default new Vuetify({
theme: {
dark: false,
themes: {
light: {
primary: '#03a9f4',
secondary: '#8bc34a',
accent: '#cddc39',
error: '#f44336',
warning: '#ffc107',
info: '#607d8b',
success: '#4caf50'
}
},
}
});

View File

@@ -1,33 +0,0 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'
import SettingView from "@/views/SettingView"
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: AboutView
},
{
path: '/setting',
name: 'setting',
component: SettingView
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router

View File

@@ -1,12 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
})

View File

@@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@@ -1,90 +0,0 @@
<template>
<div class="container">
<!-- Operation Buttons Group -->
<div class="button-group">
<v-btn @click="openDir">
扫描文件
</v-btn>
<input type="file" class="d-none">
<v-btn class="ml-5" disabled>开始下载</v-btn>
</div>
<div class="file-list mt-5">
<v-data-table
:headers="headers"
:items="items"
class="elevation-1"
>
<template v-slot:item.status="{ item }">
<v-icon v-if="item.status === 'success'" color="success">mdi-check</v-icon>
<v-icon v-else-if="item.status === 'error'" color="error">mdi-close</v-icon>
<v-icon v-else>mdi-minus</v-icon>
</template>
</v-data-table>
<div class="output mt-8">
<v-textarea disabled outlined label="日志信息" :value='outputText'/>
</div>
</div>
</div>
</template>
<script>
import Socket from '@/communication/socket'
import eventBus from "@/communication/eventbus";
export default {
name: 'Home',
data() {
return {
headers: [
{
text: '文件名',
align: 'start',
sortable: false,
value: 'name',
},
{text: '大小', value: 'size'},
{text: '状态', value: 'status'},
{text: '操作', value: 'action'},
],
items: [],
outputText: ''
}
},
methods: {
openDir() {
Socket.send({
type: 'openDir',
data: {},
})
},
loadItems() {
this.items = []
axios.get()
}
},
computed: {},
mounted() {
// eventBus.$on('getFile', (msgData) => {
// console.log(msgData);
// });
// eventBus.$on('getFileInfo', (msgData) => {
// this.items = [...this.items, ...msgData.data.map(item => {
// return {
// name: item.name,
// size: item.size,
// status: 'success',
// action: 4.0,
// }
// })]
// });
//
eventBus.$on('output', (msgData) => {
this.outputText = this.outputText.concat(msgData.data.text).concat('\n');
});
}
}
</script>

View File

@@ -1,148 +0,0 @@
<template>
<v-form>
<v-list subheader flat two-line>
<v-subheader>网络代理</v-subheader>
<v-list-item>
<v-switch label="启用代理" @change="proxyEnabledChange" v-model="isProxyEnabled"/>
</v-list-item>
<v-list-item>
<v-text-field :disabled="proxyEnabled" v-model="proxyAddress" label="代理地址"
placeholder="请输入代理服务器的地址,例如 127.0.0.1:1080。"/>
</v-list-item>
<v-list-item>
<v-text-field :disabled="proxyEnabled" v-model="updateAddress" label="更新检查地址"
placeholder="请输入更新检查地址,默认情况下无需变更。"/>
</v-list-item>
<v-divider></v-divider>
<v-subheader>全局下载设置</v-subheader>
<v-list-item>
<v-switch label="是否启用翻译歌词"/>
</v-list-item>
<v-list-item>
<v-switch label="仅输出翻译歌词"/>
</v-list-item>
<v-list-item>
<v-switch label="跳过歌词文件已存在的歌曲"/>
</v-list-item>
<v-list-item>
<v-switch label="双语歌词合并展示" v-model="mergeLrc"/>
</v-list-item>
<v-list-item>
<!--文件编码-->
<v-select v-model="lrcEncoding" :items="lrcEncodings" label="歌词文件编码" placeholder="请选择歌词文件编码"/>
</v-list-item>
<v-list-item>
<!--换行符下拉选择-->
<v-row>
<v-col>
<v-select label="换行符" v-model="lineBreak" :items="lineBreakOptions" item-text="label"
item-value="value"/>
</v-col>
</v-row>
</v-list-item>
<v-divider></v-divider>
<v-list-item>
<v-row>
<v-col cols="6">
<v-subheader>歌词下载器设置</v-subheader>
<v-list-item>
<v-data-table hide-default-footer :headers="lyricDownloaderHeaders" :items="lyricDownloader"
class="elevation-1" disable-sort>
<template v-slot:item.enabled="{ item }">
<v-simple-checkbox v-model="item.enabled" color="primary"/>
</template>
</v-data-table>
</v-list-item>
</v-col>
<v-col cols="6">
<v-subheader>标签解析器设置</v-subheader>
<v-list-item>
<v-data-table hide-default-footer :headers="tagParserHeaders" :items="tagParser" class="elevation-1"
disable-sort>
<template v-slot:item.enabled="{ item }">
<v-simple-checkbox v-model="item.enabled" color="primary"></v-simple-checkbox>
</template>
</v-data-table>
</v-list-item>
</v-col>
</v-row>
</v-list-item>
<v-divider class="mt-5"></v-divider>
<v-subheader>屏蔽字典</v-subheader>
<v-list-item>
<v-switch label="启用屏蔽字典" v-model="isBlockDictEnabled" @change="blockDictEnabledChange"/>
</v-list-item>
<v-list-item>
<v-text-field :disabled="blockDictEnabled" v-model="blockDictPath" label="屏蔽字典路径"
placeholder="请选择屏蔽字典的文件路径。"/>
<v-btn :disabled="blockDictEnabled" icon @click="openBlockDict">
<v-icon>mdi-folder</v-icon>
</v-btn>
</v-list-item>
<v-divider></v-divider>
<v-subheader>扫描设置</v-subheader>
<v-list-item>
<v-text-field v-model="fileSearchSuffix" label="文件搜索后缀"
placeholder="请输入文件搜索后缀,多个后缀以英文逗号分隔。"/>
</v-list-item>
</v-list>
</v-form>
</template>
<script>
export default {
name: "Setting",
data: () => {
return {
isProxyEnabled: true,
proxyEnabled: false,
isBlockDictEnabled: true,
blockDictEnabled: false,
blockDictPath: "",
proxyAddress: "",
updateAddress: "",
mergeLrc: false,
lineBreak: "lf",
lineBreakOptions: [
{label: "LF", value: "lf"},
{label: "CRLF", value: "crlf"},
],
lrcEncoding: "utf-8",
lyricDownloaderHeaders: [
{text: "启用", value: "enabled"},
{text: "歌词源", value: "name"},
{text: "搜索深度", value: "searchDepth"},
{text: "优先级", value: "priority"},
],
lyricDownloader: [
{enabled: true, name: "网易云音乐", searchDepth: 10, priority: 1},
],
tagParserHeaders: [
{text: "启用", value: "enabled", sortable: false, width: "10%"},
{text: "标签解析器", value: "name", sortable: false, width: "90%"},
],
tagParser: [
{name: '正则表达式解析器', enabled: true},
],
}
},
computed: {},
methods: {
proxyEnabledChange() {
this.proxyEnabled = !this.proxyEnabled;
},
blockDictEnabledChange() {
this.blockDictEnabled = !this.blockDictEnabled;
}
},
};
</script>

View File

@@ -1,6 +0,0 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: [
'vuetify'
]
})

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ public class KuWoLyricsProviderTests : TestBase
.FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.KuWo); .FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.KuWo);
} }
[Fact] [Fact(Skip = "Not Working")]
[Trait("LyricsProvider ", "KuGou")] [Trait("LyricsProvider ", "KuGou")]
public async Task DownloadAsync_Test() public async Task DownloadAsync_Test()
{ {
@@ -26,7 +26,7 @@ public class KuWoLyricsProviderTests : TestBase
lyric.IsPruneMusic.ShouldBeFalse(); lyric.IsPruneMusic.ShouldBeFalse();
} }
[Fact] [Fact(Skip = "Not Working")]
public async Task DownloadAsync_Source_Null_Test() 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", var lyric = await _kuwoLyricsProvider.DownloadAsync("Concerto for Piano and Orchestra No. 12 in A major, K414 - 1. Allegro",

View File

@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="Shouldly" Version="4.2.1" /> <PackageReference Include="Shouldly"/>
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@@ -1,13 +1,8 @@
New Features: None New Features: None
Breaking Changes: None Breaking Changes: None
Enhancement: : Enhancement: None
- 网易云歌单下载歌词时,支持使用 `;` 传递多个歌单 ID。(`15445646;468435123;4131357`) - [`41cba02`](https://github.com/real-zony/ZonyLrcToolsX/commit/41cba028333d0cf65f7dc3ee466660cc20e558fc)
- 优化了结果提示信息,更加明确。- [`1e5c418`](https://github.com/real-zony/ZonyLrcToolsX/commit/1e5c41852f70df4b824d2795f175e57c82a0d104)
- 优化了程序的启动时间,不会每次启动都去检测更新了。- [`b240564`](https://github.com/real-zony/ZonyLrcToolsX/commit/b240564cf7ac3432715dd54c280d73f9f985c4fc)
Fixed Bugs: Fixed Bugs:
- 更新了依赖的 NuGet 程序包版本。- [`1f312c7`](https://github.com/real-zony/ZonyLrcToolsX/commit/1f312c749d8e7784e7670ec3821db0b4b430ce2e) - 修复了网易云音歌词下载功能无效的问题。- [243a0e2](https://github.com/real-zony/ZonyLrcToolsX/commit/243a0e2559d3d9925dfcb96d11e2c1b1372363e6)
- 过滤了包含特殊符号的文件路径,避免报错。 - [`62d08df`](https://github.com/real-zony/ZonyLrcToolsX/commit/62d08df7353343711741b23a6ecb9c3908754075) - 修复了日志不正常显示的问题。
- 修复了从网易云歌单下载歌曲时,将专辑名称当作歌手名称进行取值。 - [`98c935e`](https://github.com/real-zony/ZonyLrcToolsX/commit/98c935ed93f9278e8606c056868824ea8335ee5d)