mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-09-05 21:16:52 +00:00
Compare commits
26 Commits
Alpha.2022
...
Alpha.2022
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d4a6a46078 | ||
![]() |
0772f5888f | ||
![]() |
950652c040 | ||
![]() |
55f720c1a1 | ||
![]() |
7463709a39 | ||
![]() |
07e660e13b | ||
![]() |
a51b399a6c | ||
![]() |
279eba48f8 | ||
![]() |
f3b1dacb0c | ||
![]() |
559efff928 | ||
![]() |
8370172aef | ||
![]() |
cb8572e599 | ||
![]() |
3b0f9fa89b | ||
![]() |
c655ac4cbb | ||
![]() |
893b1e7918 | ||
![]() |
e900a92f37 | ||
![]() |
0f84621919 | ||
![]() |
61ca863770 | ||
![]() |
2044a0b8fa | ||
![]() |
ffd76f5f2b | ||
![]() |
3705c6c8b5 | ||
![]() |
f8531e82ff | ||
![]() |
969d5e9e19 | ||
![]() |
88ed247430 | ||
![]() |
be68b3474c | ||
![]() |
363d104dbe |
25
.github/workflows/dotnet.yml
vendored
Normal file
25
.github/workflows/dotnet.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: .NET
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ dev ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ dev ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v2
|
||||||
|
with:
|
||||||
|
dotnet-version: 6.0.x
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
- name: Build
|
||||||
|
run: dotnet build --no-restore
|
||||||
|
# - name: Test
|
||||||
|
# run: dotnet test --no-build --verbosity normal
|
18
README.md
18
README.md
@@ -1,5 +1,10 @@
|
|||||||
简体中文 | [English](./docs/en_US.md)
|
简体中文 | [English](./docs/en_US.md)
|
||||||
|
|
||||||
|
## 免责声明
|
||||||
|
- 本工具仅作个人学习研究使用,可运行的二进制文件仅用于演示功能,不得将源码及其产物用于商业用途,否则由此造成的相关法律问题,[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||||
|
- 任何单位或个人因下载使用软件所产生的任何意外、疏忽、合约毁坏、诽谤、版权或知识产权侵犯及其造成的损失 (包括但不限于直接、间接、附带或衍生的损失等),[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||||
|
- 用户明确并同意本声明条款列举的全部内容,对使用本工具可能存在的风险和相关后果将完全由用户自行承担,[本人](https://github.com/real-zony) 不承担任何法律责任。
|
||||||
|
|
||||||
## 简介
|
## 简介
|
||||||
|
|
||||||
ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具。
|
ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具。
|
||||||
@@ -7,6 +12,10 @@ ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具。
|
|||||||
🚧 当前版本正在开发当中。
|
🚧 当前版本正在开发当中。
|
||||||
🚧 如果你想查看可以工作的代码,请切换到 dev 分支。
|
🚧 如果你想查看可以工作的代码,请切换到 dev 分支。
|
||||||
|
|
||||||
|
## 下载
|
||||||
|
|
||||||
|
工具会执行每日构建动作,请访问 **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** 页面进行下载。
|
||||||
|
|
||||||
## 用法
|
## 用法
|
||||||
|
|
||||||
Windows 用户请在软件目录当中,按住 Shift + 右键呼出菜单,然后选择 PowerShell/命令提示符/Windows 终端,根据下述说明执行命令即可。
|
Windows 用户请在软件目录当中,按住 Shift + 右键呼出菜单,然后选择 PowerShell/命令提示符/Windows 终端,根据下述说明执行命令即可。
|
||||||
@@ -85,16 +94,17 @@ globalOption:
|
|||||||
# 支持的歌词下载器。
|
# 支持的歌词下载器。
|
||||||
plugin:
|
plugin:
|
||||||
- name: NetEase # 基于网易云音乐的歌词下载器。
|
- name: NetEase # 基于网易云音乐的歌词下载器。
|
||||||
priority: 1 # 优先级,升序排列。
|
priority: 1 # 优先级,升序排列,改为 -1 时禁用。
|
||||||
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
||||||
priority: 2
|
priority: 2
|
||||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||||
priority: 3
|
priority: 3
|
||||||
# 歌词下载的一些共有配置参数。
|
# 歌词下载的一些共有配置参数。
|
||||||
config:
|
config:
|
||||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||||
lineBreak: '\n' # 换行符的类型。
|
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||||
isEnableTranslation: false # 是否启用翻译歌词。
|
isEnableTranslation: true # 是否启用翻译歌词。
|
||||||
|
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||||
```
|
```
|
||||||
|
|
||||||
### 屏蔽字典
|
### 屏蔽字典
|
||||||
|
@@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C29FB05C-54B
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ui", "ui", "{D6E0DAF5-8171-44C0-817E-2FF9CF574E4F}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Cli", "src\ZonyLrcTools.Cli\ZonyLrcTools.Cli.csproj", "{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Cli", "src\ZonyLrcTools.Cli\ZonyLrcTools.Cli.csproj", "{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Tests", "tests\ZonyLrcTools.Tests\ZonyLrcTools.Tests.csproj", "{FFBD3200-568F-455B-8390-5E76C51D522C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Tests", "tests\ZonyLrcTools.Tests\ZonyLrcTools.Tests.csproj", "{FFBD3200-568F-455B-8390-5E76C51D522C}"
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using McMaster.Extensions.CommandLineUtils;
|
using McMaster.Extensions.CommandLineUtils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -49,7 +50,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
|||||||
|
|
||||||
[Option("-d|--dir", CommandOptionType.SingleValue, Description = "指定需要扫描的目录。")]
|
[Option("-d|--dir", CommandOptionType.SingleValue, Description = "指定需要扫描的目录。")]
|
||||||
[DirectoryExists]
|
[DirectoryExists]
|
||||||
public string Directory { get; set; }
|
public string SongsDirectory { get; set; }
|
||||||
|
|
||||||
[Option("-l|--lyric", CommandOptionType.NoValue, Description = "指定程序需要下载歌词文件。")]
|
[Option("-l|--lyric", CommandOptionType.NoValue, Description = "指定程序需要下载歌词文件。")]
|
||||||
public bool DownloadLyric { get; set; }
|
public bool DownloadLyric { get; set; }
|
||||||
@@ -60,6 +61,8 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
|||||||
[Option("-n|--number", CommandOptionType.SingleValue, Description = "指定下载时候的线程数量。(默认值 2)")]
|
[Option("-n|--number", CommandOptionType.SingleValue, Description = "指定下载时候的线程数量。(默认值 2)")]
|
||||||
public int ParallelNumber { get; set; } = 2;
|
public int ParallelNumber { get; set; } = 2;
|
||||||
|
|
||||||
|
[Option] public string ErrorMessage { get; set; } = Path.Combine(Directory.GetCurrentDirectory(), "error.log");
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
|
protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
|
||||||
@@ -82,7 +85,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
|||||||
|
|
||||||
private async Task<List<string>> ScanMusicFilesAsync()
|
private async Task<List<string>> ScanMusicFilesAsync()
|
||||||
{
|
{
|
||||||
var files = (await _fileScanner.ScanAsync(Directory, _options.SupportFileExtensions))
|
var files = (await _fileScanner.ScanAsync(SongsDirectory, _options.SupportFileExtensions))
|
||||||
.SelectMany(t => t.FilePaths)
|
.SelectMany(t => t.FilePaths)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -92,11 +95,34 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
|||||||
throw new ErrorCodeException(ErrorCodes.NoFilesWereScanned);
|
throw new ErrorCodeException(ErrorCodes.NoFilesWereScanned);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
files = RemoveExistLyricFiles(files);
|
||||||
|
|
||||||
_logger.LogInformation($"已经扫描到了 {files.Count} 个音乐文件。");
|
_logger.LogInformation($"已经扫描到了 {files.Count} 个音乐文件。");
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<string> RemoveExistLyricFiles(List<string> filePaths)
|
||||||
|
{
|
||||||
|
if (!_options.Provider.Lyric.Config.IsSkipExistLyricFiles)
|
||||||
|
{
|
||||||
|
return filePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePaths
|
||||||
|
.Where(path =>
|
||||||
|
{
|
||||||
|
if (!File.Exists(Path.ChangeExtension(path, ".lrc")))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogWarning($"已经存在歌词文件 {path},跳过。");
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<ImmutableList<MusicInfo>> LoadMusicInfoAsync(IReadOnlyCollection<string> files)
|
private async Task<ImmutableList<MusicInfo>> LoadMusicInfoAsync(IReadOnlyCollection<string> files)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("开始加载音乐文件的标签信息...");
|
_logger.LogInformation("开始加载音乐文件的标签信息...");
|
||||||
@@ -125,7 +151,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
|||||||
return downloader;
|
return downloader;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region > 歌词下载逻辑 <
|
#region > Lyric download logic <
|
||||||
|
|
||||||
private async ValueTask DownloadLyricFilesAsync(ImmutableList<MusicInfo> musicInfos)
|
private async ValueTask DownloadLyricFilesAsync(ImmutableList<MusicInfo> musicInfos)
|
||||||
{
|
{
|
||||||
@@ -138,63 +164,76 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
|||||||
|
|
||||||
await Task.WhenAll(warpTaskList);
|
await Task.WhenAll(warpTaskList);
|
||||||
|
|
||||||
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count} 失败{0}。");
|
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadLyricTaskLogicAsync(IEnumerable<ILyricDownloader> downloaderList, MusicInfo info)
|
private async Task DownloadLyricTaskLogicAsync(IEnumerable<ILyricDownloader> downloaderList, MusicInfo info)
|
||||||
{
|
{
|
||||||
async Task<bool> InternalDownloadLogicAsync(ILyricDownloader downloader)
|
async Task InternalDownloadLogicAsync(ILyricDownloader downloader)
|
||||||
{
|
{
|
||||||
_logger.LogMusicInfoWithInformation(info);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var lyric = await downloader.DownloadAsync(info.Name, info.Artist);
|
var lyric = await downloader.DownloadAsync(info.Name, info.Artist);
|
||||||
var filePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!, $"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");
|
var lyricFilePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!,
|
||||||
if (File.Exists(filePath))
|
$"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");
|
||||||
|
|
||||||
|
if (File.Exists(lyricFilePath))
|
||||||
{
|
{
|
||||||
return true;
|
File.Delete(lyricFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.IsSuccessful = true;
|
||||||
|
|
||||||
if (lyric.IsPruneMusic)
|
if (lyric.IsPruneMusic)
|
||||||
{
|
{
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await using var stream = new FileStream(filePath, FileMode.Create);
|
await using var stream = new FileStream(lyricFilePath, FileMode.Create);
|
||||||
await using var sw = new StreamWriter(stream);
|
await using var sw = new BinaryWriter(stream);
|
||||||
await sw.WriteAsync(lyric.ToString());
|
|
||||||
await sw.FlushAsync();
|
sw.Write(EncodingConvert(lyric));
|
||||||
|
await stream.FlushAsync();
|
||||||
}
|
}
|
||||||
catch (ErrorCodeException ex)
|
catch (ErrorCodeException ex)
|
||||||
{
|
{
|
||||||
if (ex.ErrorCode == ErrorCodes.NoMatchingSong)
|
info.IsSuccessful = ex.ErrorCode == ErrorCodes.NoMatchingSong;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogWarningInfo(ex);
|
_logger.LogWarningInfo(ex);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。");
|
_logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。");
|
||||||
|
info.IsSuccessful = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var downloader in downloaderList)
|
foreach (var downloader in downloaderList)
|
||||||
{
|
{
|
||||||
if (await InternalDownloadLogicAsync(downloader))
|
await InternalDownloadLogicAsync(downloader);
|
||||||
|
|
||||||
|
if (info.IsSuccessful)
|
||||||
{
|
{
|
||||||
break;
|
_logger.LogSuccessful(info);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] EncodingConvert(LyricItemCollection lyric)
|
||||||
|
{
|
||||||
|
var supportEncodings = Encoding.GetEncodings();
|
||||||
|
if (supportEncodings.All(x => x.Name != _options.Provider.Lyric.Config.FileEncoding))
|
||||||
|
{
|
||||||
|
throw new ErrorCodeException(ErrorCodes.NotSupportedFileEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(_options.Provider.Lyric.Config.FileEncoding), lyric.GetUtf8Bytes());
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region > 专辑图像下载逻辑 <
|
#region > Ablum image download logic <
|
||||||
|
|
||||||
private async ValueTask DownloadAlbumAsync(ImmutableList<MusicInfo> musicInfos)
|
private async ValueTask DownloadAlbumAsync(ImmutableList<MusicInfo> musicInfos)
|
||||||
{
|
{
|
||||||
@@ -207,12 +246,12 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
|||||||
|
|
||||||
await Task.WhenAll(warpTaskList);
|
await Task.WhenAll(warpTaskList);
|
||||||
|
|
||||||
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count} 失败{0}。");
|
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadAlbumTaskLogicAsync(IAlbumDownloader downloader, MusicInfo info)
|
private async Task DownloadAlbumTaskLogicAsync(IAlbumDownloader downloader, MusicInfo info)
|
||||||
{
|
{
|
||||||
_logger.LogMusicInfoWithInformation(info);
|
_logger.LogSuccessful(info);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace ZonyLrcTools.Cli.Infrastructure.Tag
|
namespace ZonyLrcTools.Cli.Config
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 屏蔽词选项类。
|
/// 屏蔽词选项类。
|
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using ZonyLrcTools.Cli.Infrastructure.Lyric;
|
||||||
|
|
||||||
namespace ZonyLrcTools.Cli.Infrastructure.Lyric;
|
namespace ZonyLrcTools.Cli.Config;
|
||||||
|
|
||||||
public class LyricOption
|
public class LyricOption
|
||||||
{
|
{
|
||||||
@@ -24,5 +25,15 @@ public class LyricConfigOption
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否启用歌词翻译功能。
|
/// 是否启用歌词翻译功能。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsEnableTranslation { get; set; } = false;
|
public bool IsEnableTranslation { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 如果歌词文件已经存在,是否跳过这些文件
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSkipExistLyricFiles { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 歌词文件的编码格式。
|
||||||
|
/// </summary>
|
||||||
|
public string FileEncoding { get; set; } = "utf-8";
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
namespace ZonyLrcTools.Cli.Config
|
||||||
{
|
{
|
||||||
public class LyricProviderOption
|
public class LyricProviderOption
|
||||||
{
|
{
|
@@ -27,6 +27,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Exceptions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const int NoFilesWereScanned = 10004;
|
public const int NoFilesWereScanned = 10004;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文本: 指定的编码不受支持,请检查配置,所有受支持的编码名称。
|
||||||
|
/// </summary>
|
||||||
|
public const int NotSupportedFileEncoding = 10005;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region > 警告信息 <
|
#region > 警告信息 <
|
||||||
|
@@ -41,13 +41,13 @@ namespace ZonyLrcTools.Cli.Infrastructure.Extensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用 <see cref="LogLevel.Information"/> 级别打印歌曲信息。
|
/// 使用 <see cref="LogLevel.Information"/> 级别打印歌曲下载成功信息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger">日志记录器的实例。</param>
|
/// <param name="logger">日志记录器的实例。</param>
|
||||||
/// <param name="musicInfo">需要打印的歌曲信息。</param>
|
/// <param name="musicInfo">需要打印的歌曲信息。</param>
|
||||||
public static void LogMusicInfoWithInformation(this ILogger logger, MusicInfo musicInfo)
|
public static void LogSuccessful(this ILogger logger, MusicInfo musicInfo)
|
||||||
{
|
{
|
||||||
logger.LogInformation($"歌曲名: {musicInfo.Name}, 艺术家: {musicInfo.Artist}, 歌曲路径: {musicInfo.FilePath}");
|
logger.LogInformation($"歌曲名: {musicInfo.Name}, 艺术家: {musicInfo.Artist}, 下载成功.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Serilog.Sinks.SystemConsole.Themes;
|
||||||
|
|
||||||
|
namespace ZonyLrcTools.Cli.Infrastructure.Logging;
|
||||||
|
|
||||||
|
public static class CustomConsoleTheme
|
||||||
|
{
|
||||||
|
public static AnsiConsoleTheme Code { get; } = new AnsiConsoleTheme(
|
||||||
|
new Dictionary<ConsoleThemeStyle, string>
|
||||||
|
{
|
||||||
|
[ConsoleThemeStyle.Text] = "\x1b[38;5;0253m",
|
||||||
|
[ConsoleThemeStyle.SecondaryText] = "\x1b[38;5;0246m",
|
||||||
|
[ConsoleThemeStyle.TertiaryText] = "\x1b[38;5;0242m",
|
||||||
|
[ConsoleThemeStyle.Invalid] = "\x1b[33;1m",
|
||||||
|
[ConsoleThemeStyle.Null] = "\x1b[38;5;0038m",
|
||||||
|
[ConsoleThemeStyle.Name] = "\x1b[38;5;0081m",
|
||||||
|
[ConsoleThemeStyle.String] = "\x1b[38;5;0216m",
|
||||||
|
[ConsoleThemeStyle.Number] = "\x1b[38;5;151m",
|
||||||
|
[ConsoleThemeStyle.Boolean] = "\x1b[38;5;0038m",
|
||||||
|
[ConsoleThemeStyle.Scalar] = "\x1b[38;5;0079m",
|
||||||
|
[ConsoleThemeStyle.LevelVerbose] = "\x1b[37m",
|
||||||
|
[ConsoleThemeStyle.LevelDebug] = "\x1b[37m",
|
||||||
|
[ConsoleThemeStyle.LevelInformation] = "\x1b[32m\x1b[48;5;0238m",
|
||||||
|
[ConsoleThemeStyle.LevelWarning] = "\x1b[38;5;0229m",
|
||||||
|
[ConsoleThemeStyle.LevelError] = "\x1b[38;5;0197m\x1b[48;5;0238m",
|
||||||
|
[ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0197m\x1b[48;5;0238m",
|
||||||
|
});
|
||||||
|
}
|
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using ZonyLrcTools.Cli.Config;
|
||||||
using ZonyLrcTools.Cli.Infrastructure.Extensions;
|
using ZonyLrcTools.Cli.Infrastructure.Extensions;
|
||||||
|
|
||||||
namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
||||||
@@ -106,5 +107,10 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
|||||||
ForEach(lyric => lyricBuilder.Append(lyric).Append(Option.LineBreak));
|
ForEach(lyric => lyricBuilder.Append(lyric).Append(Option.LineBreak));
|
||||||
return lyricBuilder.ToString().TrimEnd(Option.LineBreak);
|
return lyricBuilder.ToString().TrimEnd(Option.LineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] GetUtf8Bytes()
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetBytes(ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -59,7 +59,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
|
|||||||
await ValueTask.CompletedTask;
|
await ValueTask.CompletedTask;
|
||||||
|
|
||||||
var json = JsonConvert.DeserializeObject<GetLyricResponse>(Encoding.UTF8.GetString(data));
|
var json = JsonConvert.DeserializeObject<GetLyricResponse>(Encoding.UTF8.GetString(data));
|
||||||
if (json?.OriginalLyric == null)
|
if (json?.OriginalLyric == null || string.IsNullOrEmpty(json.OriginalLyric.Text))
|
||||||
{
|
{
|
||||||
return new LyricItemCollection(null);
|
return new LyricItemCollection(null);
|
||||||
}
|
}
|
||||||
|
@@ -6,32 +6,26 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
|
|||||||
{
|
{
|
||||||
public class SongSearchRequest
|
public class SongSearchRequest
|
||||||
{
|
{
|
||||||
[JsonProperty("remoteplace")] public string RemotePlace { get; set; }
|
[JsonProperty("format")]
|
||||||
|
public string Format { get; protected set; }
|
||||||
|
|
||||||
[JsonProperty("p")] public int Page { get; set; }
|
[JsonProperty("inCharset")]
|
||||||
|
public string InCharset { get; protected set; }
|
||||||
|
|
||||||
[JsonProperty("n")] public int Limit { get; set; }
|
[JsonProperty("outCharset")]
|
||||||
|
public string OutCharset { get; protected set; }
|
||||||
[JsonProperty("w")] public string Keyword { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("format")] public string ResultFormat { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("inCharset")] public string InCharset { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("outCharset")] public string OutCharset { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("platform")] public string Platform { get; set; }
|
|
||||||
|
|
||||||
|
[JsonProperty("platform")]
|
||||||
|
public string Platform { get; protected set; }
|
||||||
|
|
||||||
|
[JsonProperty("key")]
|
||||||
|
public string Keyword { get; protected set; }
|
||||||
|
|
||||||
protected SongSearchRequest()
|
protected SongSearchRequest()
|
||||||
{
|
{
|
||||||
RemotePlace = "txt.yqq.song";
|
Format = "json";
|
||||||
Page = 1;
|
InCharset = OutCharset = "utf-8";
|
||||||
Limit = 5;
|
Platform = "yqq.json";
|
||||||
ResultFormat = "json";
|
|
||||||
InCharset = "utf8";
|
|
||||||
OutCharset = "utf8";
|
|
||||||
Platform = "yqq";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SongSearchRequest(string musicName, string artistName) : this()
|
public SongSearchRequest(string musicName, string artistName) : this()
|
||||||
|
@@ -17,11 +17,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
|
|||||||
|
|
||||||
public class QQMusicInnerSongModel
|
public class QQMusicInnerSongModel
|
||||||
{
|
{
|
||||||
[JsonProperty("list")] public List<QQMusicInnerSongItem> SongItems { get; set; }
|
[JsonProperty("itemlist")] public List<QQMusicInnerSongItem> SongItems { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QQMusicInnerSongItem
|
public class QQMusicInnerSongItem
|
||||||
{
|
{
|
||||||
[JsonProperty("songmid")] public string SongId { get; set; }
|
[JsonProperty("mid")] public string SongId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -17,7 +17,8 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic
|
|||||||
private readonly IWarpHttpClient _warpHttpClient;
|
private readonly IWarpHttpClient _warpHttpClient;
|
||||||
private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory;
|
private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory;
|
||||||
|
|
||||||
private const string QQSearchMusicUrl = @"https://c.y.qq.com/soso/fcgi-bin/client_search_cp";
|
// private const string QQSearchMusicUrl = @"https://c.y.qq.com/soso/fcgi-bin/client_search_cp";
|
||||||
|
private const string QQSearchMusicUrl = @"https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg";
|
||||||
private const string QQGetLyricUrl = @"https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg";
|
private const string QQGetLyricUrl = @"https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg";
|
||||||
|
|
||||||
private const string QQMusicRequestReferer = @"https://y.qq.com/";
|
private const string QQMusicRequestReferer = @"https://y.qq.com/";
|
||||||
|
@@ -20,6 +20,11 @@ namespace ZonyLrcTools.Cli.Infrastructure
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Artist { get; set; }
|
public string Artist { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否下载成功?
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSuccessful { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建一个新的 <see cref="MusicInfo"/> 对象。
|
/// 构建一个新的 <see cref="MusicInfo"/> 对象。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -16,10 +17,12 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
|
|||||||
{
|
{
|
||||||
protected readonly IEnumerable<ITagInfoProvider> TagInfoProviders;
|
protected readonly IEnumerable<ITagInfoProvider> TagInfoProviders;
|
||||||
protected readonly IBlockWordDictionary BlockWordDictionary;
|
protected readonly IBlockWordDictionary BlockWordDictionary;
|
||||||
protected readonly ILogger<DefaultTagLoader> _logger;
|
protected readonly ILogger<DefaultTagLoader> Logger;
|
||||||
|
|
||||||
protected ToolOptions Options;
|
protected ToolOptions Options;
|
||||||
|
|
||||||
|
private readonly IEnumerable<ITagInfoProvider> _sortedTagInfoProviders;
|
||||||
|
|
||||||
public DefaultTagLoader(IEnumerable<ITagInfoProvider> tagInfoProviders,
|
public DefaultTagLoader(IEnumerable<ITagInfoProvider> tagInfoProviders,
|
||||||
IBlockWordDictionary blockWordDictionary,
|
IBlockWordDictionary blockWordDictionary,
|
||||||
IOptions<ToolOptions> options,
|
IOptions<ToolOptions> options,
|
||||||
@@ -27,30 +30,46 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
|
|||||||
{
|
{
|
||||||
TagInfoProviders = tagInfoProviders;
|
TagInfoProviders = tagInfoProviders;
|
||||||
BlockWordDictionary = blockWordDictionary;
|
BlockWordDictionary = blockWordDictionary;
|
||||||
_logger = logger;
|
Logger = logger;
|
||||||
Options = options.Value;
|
Options = options.Value;
|
||||||
|
|
||||||
|
_sortedTagInfoProviders = GetTagInfoProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async ValueTask<MusicInfo> LoadTagAsync(string filePath)
|
public virtual async ValueTask<MusicInfo> LoadTagAsync(string filePath)
|
||||||
|
{
|
||||||
|
foreach (var provider in _sortedTagInfoProviders)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var info = await provider.LoadAsync(filePath);
|
||||||
|
if (info != null && !string.IsNullOrEmpty(info.Name))
|
||||||
|
{
|
||||||
|
HandleBlockWord(info);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogWarning($"{filePath} 没有找到正确的标签信息,请考虑调整正则表达式。");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<ITagInfoProvider> GetTagInfoProviders()
|
||||||
{
|
{
|
||||||
if (!TagInfoProviders.Any())
|
if (!TagInfoProviders.Any())
|
||||||
{
|
{
|
||||||
throw new ErrorCodeException(ErrorCodes.LoadTagInfoProviderError);
|
throw new ErrorCodeException(ErrorCodes.LoadTagInfoProviderError);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var provider in TagInfoProviders)
|
return Options.Provider.Tag.Plugin
|
||||||
{
|
.Where(x => x.Priority != -1)
|
||||||
var info = await provider.LoadAsync(filePath);
|
.OrderBy(x => x.Priority)
|
||||||
if (info != null)
|
.Join(TagInfoProviders, x => x.Name, y => y.Name, (x, y) => y);
|
||||||
{
|
|
||||||
HandleBlockWord(info);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogWarning($"{filePath} 没有找到正确的标签信息,请考虑调整正则表达式。");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void HandleBlockWord(MusicInfo info)
|
protected void HandleBlockWord(MusicInfo info)
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
|
using ZonyLrcTools.Cli.Config;
|
||||||
|
|
||||||
namespace ZonyLrcTools.Cli.Infrastructure.Tag
|
namespace ZonyLrcTools.Cli.Infrastructure.Tag
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using McMaster.Extensions.CommandLineUtils;
|
using McMaster.Extensions.CommandLineUtils;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
@@ -7,10 +8,12 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
using Serilog.Sinks.SystemConsole.Themes;
|
||||||
using ZonyLrcTools.Cli.Commands;
|
using ZonyLrcTools.Cli.Commands;
|
||||||
using ZonyLrcTools.Cli.Commands.SubCommand;
|
using ZonyLrcTools.Cli.Commands.SubCommand;
|
||||||
using ZonyLrcTools.Cli.Infrastructure.DependencyInject;
|
using ZonyLrcTools.Cli.Infrastructure.DependencyInject;
|
||||||
using ZonyLrcTools.Cli.Infrastructure.Exceptions;
|
using ZonyLrcTools.Cli.Infrastructure.Exceptions;
|
||||||
|
using ZonyLrcTools.Cli.Infrastructure.Logging;
|
||||||
|
|
||||||
namespace ZonyLrcTools.Cli
|
namespace ZonyLrcTools.Cli
|
||||||
{
|
{
|
||||||
@@ -21,6 +24,8 @@ namespace ZonyLrcTools.Cli
|
|||||||
{
|
{
|
||||||
public static async Task<int> Main(string[] args)
|
public static async Task<int> Main(string[] args)
|
||||||
{
|
{
|
||||||
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
|
||||||
ConfigureLogger();
|
ConfigureLogger();
|
||||||
ConfigureErrorMessage();
|
ConfigureErrorMessage();
|
||||||
|
|
||||||
@@ -53,7 +58,7 @@ namespace ZonyLrcTools.Cli
|
|||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||||
.MinimumLevel.Override("System.Net.Http.HttpClient", LogEventLevel.Error)
|
.MinimumLevel.Override("System.Net.Http.HttpClient", LogEventLevel.Error)
|
||||||
.Enrich.FromLogContext()
|
.Enrich.FromLogContext()
|
||||||
.WriteTo.Async(c => c.Console())
|
.WriteTo.Async(c => c.Console(theme: CustomConsoleTheme.Code))
|
||||||
.WriteTo.Logger(lc =>
|
.WriteTo.Logger(lc =>
|
||||||
{
|
{
|
||||||
lc.Filter.ByIncludingOnly(lc => lc.Level == LogEventLevel.Warning)
|
lc.Filter.ByIncludingOnly(lc => lc.Level == LogEventLevel.Warning)
|
||||||
@@ -92,7 +97,8 @@ namespace ZonyLrcTools.Cli
|
|||||||
switch (ex)
|
switch (ex)
|
||||||
{
|
{
|
||||||
case ErrorCodeException exception:
|
case ErrorCodeException exception:
|
||||||
Log.Logger.Error($"出现了未处理的异常,错误代码: {exception.ErrorCode},错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n调用栈:\n{exception.StackTrace}");
|
Log.Logger.Error(
|
||||||
|
$"出现了未处理的异常。\n错误代码: {exception.ErrorCode}\n错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n原始信息:{exception.Message}\n调用栈:{exception.StackTrace}");
|
||||||
Environment.Exit(exception.ErrorCode);
|
Environment.Exit(exception.ErrorCode);
|
||||||
return exception.ErrorCode;
|
return exception.ErrorCode;
|
||||||
case { } unknownException:
|
case { } unknownException:
|
||||||
|
@@ -3,7 +3,8 @@
|
|||||||
"10001": "待搜索的后缀不能为空。",
|
"10001": "待搜索的后缀不能为空。",
|
||||||
"10002": "需要扫描的目录不存在,请确认路径是否正确。",
|
"10002": "需要扫描的目录不存在,请确认路径是否正确。",
|
||||||
"10003": "不能获取文件的后缀信息。",
|
"10003": "不能获取文件的后缀信息。",
|
||||||
"10004": "没有扫描到任何音乐文件。"
|
"10004": "没有扫描到任何音乐文件。",
|
||||||
|
"10005": "指定的编码不受支持,请检查配置,所有受支持的编码名称,请参考: https://docs.microsoft.com/en-us/dotnet/api/system.text.encodinginfo.codepage?view=net-6.0#system-text-encodinginfo-codepage。"
|
||||||
},
|
},
|
||||||
"Warning": {
|
"Warning": {
|
||||||
"50001": "扫描文件时出现了错误。",
|
"50001": "扫描文件时出现了错误。",
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||||
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||||
<PackageReference Include="TagLibSharp" Version="2.2.0" />
|
<PackageReference Include="TagLibSharp" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@@ -34,13 +34,15 @@ globalOption:
|
|||||||
# 支持的歌词下载器。
|
# 支持的歌词下载器。
|
||||||
plugin:
|
plugin:
|
||||||
- name: NetEase # 基于网易云音乐的歌词下载器。
|
- name: NetEase # 基于网易云音乐的歌词下载器。
|
||||||
priority: 1 # 优先级,升序排列。
|
priority: 1 # 优先级,升序排列,改为 -1 时禁用。
|
||||||
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
||||||
priority: 2
|
priority: 2
|
||||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||||
priority: 3
|
priority: 3
|
||||||
# 歌词下载的一些共有配置参数。
|
# 歌词下载的一些共有配置参数。
|
||||||
config:
|
config:
|
||||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||||
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||||
isEnableTranslation: true # 是否启用翻译歌词。
|
isEnableTranslation: true # 是否启用翻译歌词。
|
||||||
|
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||||
|
fileEncoding: 'utf-8' # 歌词文件的编码格式。
|
3
src/ui/.browserslistrc
Normal file
3
src/ui/.browserslistrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
23
src/ui/.gitignore
vendored
Normal file
23
src/ui/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
19
src/ui/README.md
Normal file
19
src/ui/README.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 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/).
|
5
src/ui/babel.config.js
Normal file
5
src/ui/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
19
src/ui/jsconfig.json
Normal file
19
src/ui/jsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "esnext",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
23
src/ui/package.json
Normal file
23
src/ui/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "ui",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"buefy": "^0.9.20",
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"vue": "^2.6.14",
|
||||||
|
"vue-router": "^3.5.1",
|
||||||
|
"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",
|
||||||
|
"vue-template-compiler": "^2.6.14"
|
||||||
|
}
|
||||||
|
}
|
BIN
src/ui/public/favicon.ico
Normal file
BIN
src/ui/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
17
src/ui/public/index.html
Normal file
17
src/ui/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<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>
|
||||||
|
</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>
|
5
src/ui/src/App.vue
Normal file
5
src/ui/src/App.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view/>
|
||||||
|
</div>
|
||||||
|
</template>
|
BIN
src/ui/src/assets/logo.png
Normal file
BIN
src/ui/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
12
src/ui/src/main.js
Normal file
12
src/ui/src/main.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#app')
|
15
src/ui/src/router/index.js
Normal file
15
src/ui/src/router/index.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import VueRouter from 'vue-router'
|
||||||
|
import HomeView from '../views/HomeView.vue'
|
||||||
|
|
||||||
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
|
const routes = [{
|
||||||
|
path: '/', name: 'home', component: HomeView
|
||||||
|
}]
|
||||||
|
|
||||||
|
const router = new VueRouter({
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
17
src/ui/src/store/index.js
Normal file
17
src/ui/src/store/index.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
export default new Vuex.Store({
|
||||||
|
state: {
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
},
|
||||||
|
modules: {
|
||||||
|
}
|
||||||
|
})
|
12
src/ui/src/views/HomeView.vue
Normal file
12
src/ui/src/views/HomeView.vue
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// @ is an alias to /src
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HomeView'
|
||||||
|
}
|
||||||
|
</script>
|
4
src/ui/vue.config.js
Normal file
4
src/ui/vue.config.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
module.exports = defineConfig({
|
||||||
|
transpileDependencies: true
|
||||||
|
})
|
5518
src/ui/yarn.lock
Normal file
5518
src/ui/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using ZonyLrcTools.Cli.Config;
|
||||||
using ZonyLrcTools.Cli.Infrastructure.Lyric;
|
using ZonyLrcTools.Cli.Infrastructure.Lyric;
|
||||||
|
|
||||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
||||||
|
@@ -69,5 +69,13 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
|||||||
|
|
||||||
lyric.ShouldNotBeNull();
|
lyric.ShouldNotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UnknownIssue_Test()
|
||||||
|
{
|
||||||
|
var lyric = await _lyricDownloader.DownloadAsync("主題歌Arrietty's Song", "Cécile Corbel");
|
||||||
|
|
||||||
|
lyric.ShouldNotBeNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user