53 Commits

Author SHA1 Message Date
real-zony
6c25483f1e ci: Release new version。 2022-09-24 19:20:19 +08:00
real-zony
b64f83d56b fix: Removed NAudio related code. 2022-09-24 19:19:14 +08:00
real-zony
6ad154a62b feat: Add update check logic. 2022-09-24 19:13:57 +08:00
real-zony
32adab68b6 docs: Update README.md file.
Added start history section.
2022-09-23 10:44:05 +08:00
real-zony
ba91108ca4 fix: Fixed jobs variable share problem. 2022-09-22 19:45:34 +08:00
real-zony
f597e24ff7 ci: Fixed path issue. 2022-09-22 19:17:21 +08:00
real-zony
d453d520d4 ci: debug workflow. 2022-09-22 18:50:27 +08:00
real-zony
9f254aae8e ci: debug workflow. 2022-09-22 18:50:11 +08:00
real-zony
c9fa0de95f ci: Build new version. 2022-09-22 18:44:42 +08:00
real-zony
6d7ee04b74 feat: Support song depth search. 2022-09-22 18:43:42 +08:00
real-zony
f519eb1251 feat(Test NetEaseMusic's Romance lyrics.): 2022-09-22 18:08:26 +08:00
real-zony
85f325b300 fix: Fixed null reference exception. 2022-09-22 17:53:19 +08:00
real-zony
140043db79 feat: Enhanced song matching logic (NetEaseMusic). 2022-09-22 17:49:54 +08:00
real-zony
c583a9c278 build: Upgrade dependency nuget packages. 2022-09-22 16:11:53 +08:00
real-zony
8385f6f118 feat: Provide the 'isOnlyOutputTranslation option to output only translated lyrics. 2022-09-22 15:48:20 +08:00
real-zony
be4380c744 fix: Fixed issue #106. 2022-09-22 15:30:28 +08:00
real-zony
87f6a98668 fix: Fixed the issue with #82.
Reason may be that the song name has brackets, causing the search to
failure.
2022-09-22 15:06:54 +08:00
real-zony
83fcc91fb8 ci: Fixed github workflow file errors. 2022-09-22 14:51:17 +08:00
real-zony
b4c229ccca ci: test release. 2022-09-22 14:45:36 +08:00
real-zony
71302c7262 Merge branch 'dev' of https://github.com/real-zony/ZonyLrcToolsX into dev 2022-09-22 14:43:13 +08:00
real-zony
1bec50e409 ci: update workflow. 2022-09-22 14:42:48 +08:00
Zony
f7cae8062c Update release.md 2022-09-22 14:28:37 +08:00
real-zony
c73f0d8eea ci: debug workflow. 2022-09-22 14:28:15 +08:00
real-zony
fe7c9115d5 ci: debug workflow. 2022-09-22 14:24:45 +08:00
real-zony
e13fb47aeb ci: debug workflow. 2022-09-22 13:30:34 +08:00
real-zony
32891d4208 ci: debug workflow. 2022-09-22 13:27:08 +08:00
real-zony
770d246787 ci: debug workflow. 2022-09-22 13:22:59 +08:00
real-zony
589151a77d ci: debug workflow. 2022-09-22 13:21:07 +08:00
real-zony
65af048f72 ci: debug workflow. 2022-09-22 13:12:49 +08:00
real-zony
99fd3b5021 ci: debug workflow. 2022-09-22 13:09:41 +08:00
real-zony
e4420e749a ci: debug workflow. 2022-09-22 13:02:33 +08:00
real-zony
5f22dafb1e ci: debug workflow. 2022-09-22 13:01:03 +08:00
real-zony
c2d8ee4341 ci: debug workflow. 2022-09-22 12:57:14 +08:00
real-zony
6a4435eb0d ci: debug workflow. 2022-09-22 12:51:50 +08:00
real-zony
346e07e9e7 ci: debug workflow. 2022-09-22 12:49:42 +08:00
real-zony
efdb3e8441 ci: debug workflow. 2022-09-22 12:45:25 +08:00
real-zony
0438433284 ci: debug workflow. 2022-09-22 12:40:52 +08:00
real-zony
d34a830660 ci: debug workflow. 2022-09-22 12:39:44 +08:00
real-zony
975f61a8e7 ci: debug workflow. 2022-09-22 12:37:37 +08:00
real-zony
8ba78c2681 ci: debug workflows. 2022-09-22 12:35:16 +08:00
real-zony
019bdb571c ci: debug workflow. 2022-09-22 12:31:23 +08:00
real-zony
a2aeed021d ci: debug workflow. 2022-09-22 12:28:15 +08:00
real-zony
0e8d361ed9 ci: debug workflow. 2022-09-22 12:24:27 +08:00
real-zony
c3b381a5f7 ci: debug workflow. 2022-09-22 12:23:09 +08:00
real-zony
8acba586b0 ci: debug workflow. 2022-09-22 12:12:16 +08:00
real-zony
5905d4b384 ci: debug workflow. 2022-09-22 12:03:06 +08:00
real-zony
2ad771e62e ci: debug workflow. 2022-09-22 11:49:11 +08:00
real-zony
403d7b151d ci: debug workflow. 2022-09-22 11:47:03 +08:00
real-zony
5c8c9af9ab ci: debug workflow. 2022-09-22 11:40:09 +08:00
real-zony
bfd23fc14e ci: debug workflow. 2022-09-22 11:39:01 +08:00
real-zony
81271a5aa5 ci: debug workflow. 2022-09-22 11:36:17 +08:00
real-zony
80a0bcdcee ci: debug workflow. 2022-09-22 11:34:14 +08:00
real-zony
172174d9e3 ci: debug github actions. 2022-09-21 23:00:29 +08:00
29 changed files with 357 additions and 93 deletions

View File

@@ -3,23 +3,59 @@ name: .NET
on: on:
push: push:
branches: [ dev ] branches: [ dev ]
paths:
- "versions/**"
pull_request: pull_request:
branches: [ dev ] branches: [ dev ]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - name: Get build version
- name: Setup .NET id: date
uses: actions/setup-dotnet@v2 run: echo "::set-output name=date::$(date +'%Y%m%d')${{github.run_number}}"
with: - name: Checkout Code
dotnet-version: 6.0.x uses: actions/checkout@v3
- name: Restore dependencies - name: Setup .NET
run: dotnet restore uses: actions/setup-dotnet@v2
- name: Build with:
run: dotnet build --no-restore dotnet-version: 6.0.x
# - name: Test - name: Restore dependencies
# run: dotnet test --no-build --verbosity normal run: dotnet restore
- name: Publish
working-directory: ./src/ZonyLrcTools.Cli
run: |
ls -a
chmod +x ./publish.sh
./publish.sh
mv ./TempFiles ../../
shell: bash
env:
PUBLISH_VERSION: ${{ steps.date.outputs.date }}
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: release-files
path: |
./TempFiles
./versions/release.md
outputs:
version: ${{ steps.date.outputs.date }}
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: release-files
path: .
- name: Upload Release
uses: ncipollo/release-action@v1
with:
artifacts: "./TempFiles/*.zip"
token: ${{ secrets.GITHUBACTIONS }}
tag: ZonyLrcToolsX_Alpha.${{ needs.build.outputs.version }}
commit: dev
bodyFile: ./versions/release.md

View File

@@ -57,6 +57,8 @@ macOS 和 Linux 用户请打开终端,切换到软件目录,一样执行命
程序的所有的配置信息,都在 `config.yaml` 进行更改,下面标注了各个配置的说明。 程序的所有的配置信息,都在 `config.yaml` 进行更改,下面标注了各个配置的说明。
其中是否开启的可选项为 `true` 或者 `false`,等同于中文的是和否。
```yaml ```yaml
globalOption: globalOption:
# 允许扫描的歌曲文件后缀名。 # 允许扫描的歌曲文件后缀名。
@@ -95,18 +97,27 @@ globalOption:
plugin: plugin:
- name: NetEase # 基于网易云音乐的歌词下载器。 - name: NetEase # 基于网易云音乐的歌词下载器。
priority: 1 # 优先级,升序排列,改为 -1 时禁用。 priority: 1 # 优先级,升序排列,改为 -1 时禁用。
depth: 30 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
- name: QQ # 基于 QQ 音乐的歌词下载器。 - name: QQ # 基于 QQ 音乐的歌词下载器。
priority: 2 priority: 2
# depth: 10 # 暂时不支持。
- name: KuGou # 基于酷狗音乐的歌词下载器。 - name: KuGou # 基于酷狗音乐的歌词下载器。
priority: 3 priority: 3
depth: 10
# 歌词下载的一些共有配置参数。 # 歌词下载的一些共有配置参数。
config: config:
isOneLine: true # 双语歌词是否合并为一行展示。 isOneLine: true # 双语歌词是否合并为一行展示。
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。 lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
isEnableTranslation: true # 是否启用翻译歌词。 isEnableTranslation: true # 是否启用翻译歌词。
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件 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`
### 屏蔽字典 ### 屏蔽字典
屏蔽字典适用于网易云音乐歌词下载,针对某些单词,网易云音乐使用了 * 号进行屏蔽,这个时候可以使用屏蔽字典,设置歌曲名的关键词替换。例如有一首歌曲叫做 *Fucking ABC* ,这个时候网易云实际的名字是 *Fu****ing* ,用户只需要在屏蔽字典加入替换逻辑即可,例如: 屏蔽字典适用于网易云音乐歌词下载,针对某些单词,网易云音乐使用了 * 号进行屏蔽,这个时候可以使用屏蔽字典,设置歌曲名的关键词替换。例如有一首歌曲叫做 *Fucking ABC* ,这个时候网易云实际的名字是 *Fu****ing* ,用户只需要在屏蔽字典加入替换逻辑即可,例如:
@@ -123,6 +134,10 @@ globalOption:
<img src="./docs/img/alipay.jpg" width="200"/><img src="./docs/img/wechat.jpg" width="200"/> <img src="./docs/img/alipay.jpg" width="200"/><img src="./docs/img/wechat.jpg" width="200"/>
## 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)
## 路线图 ## 路线图
- [x] 支持跨平台的 CLI 工具。 - [x] 支持跨平台的 CLI 工具。

View File

@@ -17,6 +17,7 @@ using ZonyLrcTools.Cli.Infrastructure.IO;
using ZonyLrcTools.Cli.Infrastructure.Lyric; using ZonyLrcTools.Cli.Infrastructure.Lyric;
using ZonyLrcTools.Cli.Infrastructure.Tag; using ZonyLrcTools.Cli.Infrastructure.Tag;
using ZonyLrcTools.Cli.Infrastructure.Threading; using ZonyLrcTools.Cli.Infrastructure.Threading;
using File = System.IO.File;
namespace ZonyLrcTools.Cli.Commands.SubCommand namespace ZonyLrcTools.Cli.Commands.SubCommand
{ {
@@ -67,17 +68,19 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
protected override async Task<int> OnExecuteAsync(CommandLineApplication app) protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
{ {
var files = await ScanMusicFilesAsync();
var musicInfos = await LoadMusicInfoAsync(files);
if (DownloadLyric) if (DownloadLyric)
{ {
await DownloadLyricFilesAsync(musicInfos); await DownloadLyricFilesAsync(
await LoadMusicInfoAsync(
RemoveExistLyricFiles(
await ScanMusicFilesAsync())));
} }
if (DownloadAlbum) if (DownloadAlbum)
{ {
await DownloadAlbumAsync(musicInfos); await DownloadAlbumAsync(
await LoadMusicInfoAsync(
await ScanMusicFilesAsync()));
} }
return 0; return 0;
@@ -95,8 +98,6 @@ 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;
@@ -131,7 +132,11 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
var warpTaskList = files.Select(file => warpTask.RunAsync(() => Task.Run(async () => await _tagLoader.LoadTagAsync(file)))); var warpTaskList = files.Select(file => warpTask.RunAsync(() => Task.Run(async () => await _tagLoader.LoadTagAsync(file))));
var result = (await Task.WhenAll(warpTaskList)) var result = (await Task.WhenAll(warpTaskList))
.Where(m => m != null) .Where(m => m != null)
.Where(m => !string.IsNullOrEmpty(m.Name) || !string.IsNullOrEmpty(m.Artist)); .Where(m => !string.IsNullOrEmpty(m.Name) || !string.IsNullOrEmpty(m.Artist))
.ToList();
// Load music total time info.
// result.Foreach(m => { m.TotalTime = (long?)new AudioFileReader(m.FilePath).TotalTime.TotalMilliseconds; });
_logger.LogInformation($"已成功加载 {files.Count} 个音乐文件的标签信息。"); _logger.LogInformation($"已成功加载 {files.Count} 个音乐文件的标签信息。");
@@ -173,7 +178,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
{ {
try try
{ {
var lyric = await downloader.DownloadAsync(info.Name, info.Artist); var lyric = await downloader.DownloadAsync(info.Name, info.Artist, info.TotalTime);
var lyricFilePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!, var lyricFilePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!,
$"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc"); $"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ZonyLrcTools.Cli.Infrastructure.Lyric; using ZonyLrcTools.Cli.Infrastructure.Lyric;
namespace ZonyLrcTools.Cli.Config; namespace ZonyLrcTools.Cli.Config;
@@ -8,6 +9,11 @@ public class LyricOption
public IEnumerable<LyricProviderOption> Plugin { get; set; } public IEnumerable<LyricProviderOption> Plugin { get; set; }
public LyricConfigOption Config { get; set; } public LyricConfigOption Config { get; set; }
public LyricProviderOption GetLyricProviderOption(string name)
{
return Plugin.FirstOrDefault(x => x.Name == name);
}
} }
public class LyricConfigOption public class LyricConfigOption
@@ -36,4 +42,9 @@ public class LyricConfigOption
/// 歌词文件的编码格式。 /// 歌词文件的编码格式。
/// </summary> /// </summary>
public string FileEncoding { get; set; } = "utf-8"; public string FileEncoding { get; set; } = "utf-8";
/// <summary>
/// 是否只输出翻译歌词。
/// </summary>
public bool IsOnlyOutputTranslation { get; set; } = false;
} }

View File

@@ -11,5 +11,10 @@
/// 歌词下载时的优先级,当值为 -1 时是禁用。 /// 歌词下载时的优先级,当值为 -1 时是禁用。
/// </summary> /// </summary>
public int Priority { get; set; } public int Priority { get; set; }
/// <summary>
/// 搜索深度,值越大搜索结果越多,但搜索时间越长。
/// </summary>
public int Depth { get; set; }
} }
} }

View File

@@ -1,5 +1,4 @@
using ZonyLrcTools.Cli.Infrastructure.Lyric; using ZonyLrcTools.Cli.Infrastructure.Tag;
using ZonyLrcTools.Cli.Infrastructure.Tag;
namespace ZonyLrcTools.Cli.Config; namespace ZonyLrcTools.Cli.Config;

View File

@@ -51,7 +51,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Album.NetEase
var songDetailJsonStr = await _warpHttpClient.GetAsync( var songDetailJsonStr = await _warpHttpClient.GetAsync(
GetMusicInfoApi, GetMusicInfoApi,
new GetSongDetailsRequest(searchResult.GetFirstMatchSongId(songName)), new GetSongDetailsRequest(searchResult.GetFirstMatchSongId(songName, null)),
_defaultOption); _defaultOption);
var url = JObject.Parse(songDetailJsonStr).SelectToken("$.songs[0].album.picUrl")?.Value<string>(); var url = JObject.Parse(songDetailJsonStr).SelectToken("$.songs[0].album.picUrl")?.Value<string>();

View File

@@ -12,8 +12,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
/// </summary> /// </summary>
/// <param name="songName">歌曲的名称。</param> /// <param name="songName">歌曲的名称。</param>
/// <param name="artist">歌曲的作者。</param> /// <param name="artist">歌曲的作者。</param>
/// <param name="duration">歌曲的时长。</param>
/// <returns>歌曲的歌词数据对象。</returns> /// <returns>歌曲的歌词数据对象。</returns>
ValueTask<LyricItemCollection> DownloadAsync(string songName, string artist); ValueTask<LyricItemCollection> DownloadAsync(string songName, string artist, long? duration = null);
/// <summary> /// <summary>
/// 下载器的名称。 /// 下载器的名称。

View File

@@ -12,11 +12,17 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.KuGou.JsonModel
[JsonProperty("keyword")] public string Keyword { get; } [JsonProperty("keyword")] public string Keyword { get; }
public SongSearchRequest(string musicName, string artistName) [JsonProperty("pagesize")] public int PageSize { get; }
[JsonProperty("page")] public int Page { get; }
public SongSearchRequest(string musicName, string artistName, int pageSize = 30)
{ {
Filter = 2; Filter = 2;
Platform = "WebFilter"; Platform = "WebFilter";
Keyword = HttpUtility.UrlEncode($"{musicName}+{artistName}", Encoding.UTF8); Keyword = HttpUtility.UrlEncode($"{musicName}+{artistName}", Encoding.UTF8);
PageSize = pageSize;
Page = 1;
} }
} }
} }

View File

@@ -1,7 +1,9 @@
using System; using System;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using ZonyLrcTools.Cli.Config;
using ZonyLrcTools.Cli.Infrastructure.Exceptions; using ZonyLrcTools.Cli.Infrastructure.Exceptions;
using ZonyLrcTools.Cli.Infrastructure.Lyric.KuGou.JsonModel; using ZonyLrcTools.Cli.Infrastructure.Lyric.KuGou.JsonModel;
using ZonyLrcTools.Cli.Infrastructure.Network; using ZonyLrcTools.Cli.Infrastructure.Network;
@@ -14,22 +16,25 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.KuGou
private readonly IWarpHttpClient _warpHttpClient; private readonly IWarpHttpClient _warpHttpClient;
private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory; private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory;
private readonly ToolOptions _options;
private const string KuGouSearchMusicUrl = @"https://songsearch.kugou.com/song_search_v2"; private const string KuGouSearchMusicUrl = @"https://songsearch.kugou.com/song_search_v2";
private const string KuGouGetLyricAccessKeyUrl = @"http://lyrics.kugou.com/search"; private const string KuGouGetLyricAccessKeyUrl = @"http://lyrics.kugou.com/search";
private const string KuGouGetLyricUrl = @"http://lyrics.kugou.com/download"; private const string KuGouGetLyricUrl = @"http://lyrics.kugou.com/download";
public KuGourLyricDownloader(IWarpHttpClient warpHttpClient, public KuGourLyricDownloader(IWarpHttpClient warpHttpClient,
ILyricItemCollectionFactory lyricItemCollectionFactory) ILyricItemCollectionFactory lyricItemCollectionFactory,
IOptions<ToolOptions> options)
{ {
_warpHttpClient = warpHttpClient; _warpHttpClient = warpHttpClient;
_lyricItemCollectionFactory = lyricItemCollectionFactory; _lyricItemCollectionFactory = lyricItemCollectionFactory;
_options = options.Value;
} }
protected override async ValueTask<byte[]> DownloadDataAsync(LyricDownloaderArgs args) protected override async ValueTask<byte[]> DownloadDataAsync(LyricDownloaderArgs args)
{ {
var searchResult = await _warpHttpClient.GetAsync<SongSearchResponse>(KuGouSearchMusicUrl, var searchResult = await _warpHttpClient.GetAsync<SongSearchResponse>(KuGouSearchMusicUrl,
new SongSearchRequest(args.SongName, args.Artist)); new SongSearchRequest(args.SongName, args.Artist, _options.Provider.Lyric.GetLyricProviderOption(DownloaderName).Depth));
ValidateSongSearchResponse(searchResult, args); ValidateSongSearchResponse(searchResult, args);

View File

@@ -16,10 +16,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
/// </summary> /// </summary>
/// <param name="songName">歌曲名称。</param> /// <param name="songName">歌曲名称。</param>
/// <param name="artist">歌曲作者/艺术家。</param> /// <param name="artist">歌曲作者/艺术家。</param>
/// <param name="duration">歌曲的时长。</param>
/// <returns>下载完成的歌曲数据。</returns> /// <returns>下载完成的歌曲数据。</returns>
public virtual async ValueTask<LyricItemCollection> DownloadAsync(string songName, string artist) public virtual async ValueTask<LyricItemCollection> DownloadAsync(string songName, string artist, long? duration = null)
{ {
var args = new LyricDownloaderArgs(songName, artist); var args = new LyricDownloaderArgs(songName, artist, duration ?? 0);
await ValidateAsync(args); await ValidateAsync(args);
var downloadDataBytes = await DownloadDataAsync(args); var downloadDataBytes = await DownloadDataAsync(args);
return await GenerateLyricAsync(downloadDataBytes, args); return await GenerateLyricAsync(downloadDataBytes, args);

View File

@@ -6,10 +6,13 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
public string Artist { get; set; } public string Artist { get; set; }
public LyricDownloaderArgs(string songName, string artist) public long Duration { get; set; }
public LyricDownloaderArgs(string songName, string artist, long duration)
{ {
SongName = songName; SongName = songName;
Artist = artist; Artist = artist;
Duration = duration;
} }
} }
} }

View File

@@ -43,6 +43,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
if (_options.Provider.Lyric.Config.IsEnableTranslation && !string.IsNullOrEmpty(translationLyric)) if (_options.Provider.Lyric.Config.IsEnableTranslation && !string.IsNullOrEmpty(translationLyric))
{ {
var translatedLyric = InternalBuildLyricObject(new LyricItemCollection(_options.Provider.Lyric.Config), translationLyric); var translatedLyric = InternalBuildLyricObject(new LyricItemCollection(_options.Provider.Lyric.Config), translationLyric);
if (_options.Provider.Lyric.Config.IsOnlyOutputTranslation)
{
return translatedLyric;
}
return lyric + translatedLyric; return lyric + translatedLyric;
} }

View File

@@ -8,9 +8,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
{ {
public GetLyricRequest(long songId) public GetLyricRequest(long songId)
{ {
OS = "osx"; OS = "ios";
Id = songId; Id = songId;
Lv = Kv = Tv = -1; Lv = Kv = Tv = Rv = -1;
} }
/// <summary> /// <summary>
@@ -30,5 +30,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
[JsonProperty("kv")] public int Kv { get; } [JsonProperty("kv")] public int Kv { get; }
[JsonProperty("tv")] public int Tv { get; } [JsonProperty("tv")] public int Tv { get; }
[JsonProperty("rv")] public int Rv { get; set; }
[JsonProperty("crypto")] public string Protocol { get; set; } = "api";
} }
} }

View File

@@ -22,6 +22,12 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
[JsonProperty("tlyric")] [JsonProperty("tlyric")]
public InnerLyric TranslationLyric { get; set; } public InnerLyric TranslationLyric { get; set; }
/// <summary>
/// 如果存在罗马音歌词,则本项内容为罗马音歌词。
/// </summary>
[JsonProperty("romalrc")]
public InnerLyric RomaLyric { get; set; }
/// <summary> /// <summary>
/// 状态码。 /// 状态码。
/// </summary> /// </summary>

View File

@@ -1,4 +1,5 @@
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Web; using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -42,18 +43,25 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
[JsonProperty("limit")] [JsonProperty("limit")]
public int Limit { get; set; } public int Limit { get; set; }
[JsonProperty("crypto")] public string Crypto { get; set; } = "weapi";
public SongSearchRequest() public SongSearchRequest()
{ {
CsrfToken = string.Empty; CsrfToken = string.Empty;
Type = 1; Type = 1;
Offset = 0; Offset = 0;
IsTotal = true; IsTotal = true;
Limit = 5; Limit = 10;
} }
public SongSearchRequest(string musicName, string artistName) : this() public SongSearchRequest(string musicName, string artistName, int limit = 10) : this()
{ {
// Remove all the brackets and the content inside them.
var regex = new Regex(@"\([^)]*\)");
musicName = regex.Replace(musicName, string.Empty);
SearchKey = HttpUtility.UrlEncode($"{musicName}+{artistName}", Encoding.UTF8); SearchKey = HttpUtility.UrlEncode($"{musicName}+{artistName}", Encoding.UTF8);
Limit = limit;
} }
} }
} }

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -10,10 +11,20 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
[JsonProperty("code")] public int StatusCode { get; set; } [JsonProperty("code")] public int StatusCode { get; set; }
public int GetFirstMatchSongId(string songName) public int GetFirstMatchSongId(string songName, long? duration)
{ {
var item = Items.SongItems.FirstOrDefault(x => x.Name == songName); var perfectMatch = Items.SongItems.FirstOrDefault(x => x.Name == songName);
return item?.Id ?? Items.SongItems[0].Id; if (perfectMatch != null)
{
return perfectMatch.Id;
}
if (duration is null or 0)
{
return Items.SongItems.First().Id;
}
return Items.SongItems.OrderBy(t => Math.Abs(t.Duration - duration.Value)).First().Id;
} }
} }
@@ -49,6 +60,12 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
/// </summary> /// </summary>
[JsonProperty("album")] [JsonProperty("album")]
public SongAlbumModel Album { get; set; } public SongAlbumModel Album { get; set; }
/// <summary>
/// 歌曲的实际长度。
/// </summary>
[JsonProperty("duration")]
public long Duration { get; set; }
} }
public class SongArtistModel public class SongArtistModel

View File

@@ -2,7 +2,9 @@ using System;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using ZonyLrcTools.Cli.Config;
using ZonyLrcTools.Cli.Infrastructure.Exceptions; using ZonyLrcTools.Cli.Infrastructure.Exceptions;
using ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel; using ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel;
using ZonyLrcTools.Cli.Infrastructure.Network; using ZonyLrcTools.Cli.Infrastructure.Network;
@@ -15,6 +17,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
private readonly IWarpHttpClient _warpHttpClient; private readonly IWarpHttpClient _warpHttpClient;
private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory; private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory;
private readonly ToolOptions _options;
private const string NetEaseSearchMusicUrl = @"https://music.163.com/api/search/get/web"; private const string NetEaseSearchMusicUrl = @"https://music.163.com/api/search/get/web";
private const string NetEaseGetLyricUrl = @"https://music.163.com/api/song/lyric"; private const string NetEaseGetLyricUrl = @"https://music.163.com/api/song/lyric";
@@ -23,17 +26,19 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
private const string NetEaseRequestContentType = @"application/x-www-form-urlencoded"; private const string NetEaseRequestContentType = @"application/x-www-form-urlencoded";
public NetEaseLyricDownloader(IWarpHttpClient warpHttpClient, public NetEaseLyricDownloader(IWarpHttpClient warpHttpClient,
ILyricItemCollectionFactory lyricItemCollectionFactory) ILyricItemCollectionFactory lyricItemCollectionFactory,
IOptions<ToolOptions> options)
{ {
_warpHttpClient = warpHttpClient; _warpHttpClient = warpHttpClient;
_lyricItemCollectionFactory = lyricItemCollectionFactory; _lyricItemCollectionFactory = lyricItemCollectionFactory;
_options = options.Value;
} }
protected override async ValueTask<byte[]> DownloadDataAsync(LyricDownloaderArgs args) protected override async ValueTask<byte[]> DownloadDataAsync(LyricDownloaderArgs args)
{ {
var searchResult = await _warpHttpClient.PostAsync<SongSearchResponse>( var searchResult = await _warpHttpClient.PostAsync<SongSearchResponse>(
NetEaseSearchMusicUrl, NetEaseSearchMusicUrl,
new SongSearchRequest(args.SongName, args.Artist), new SongSearchRequest(args.SongName, args.Artist, _options.Provider.Lyric.GetLyricProviderOption(DownloaderName).Depth),
true, true,
msg => msg =>
{ {
@@ -48,7 +53,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
var lyricResponse = await _warpHttpClient.GetAsync( var lyricResponse = await _warpHttpClient.GetAsync(
NetEaseGetLyricUrl, NetEaseGetLyricUrl,
new GetLyricRequest(searchResult.GetFirstMatchSongId(args.SongName)), new GetLyricRequest(searchResult.GetFirstMatchSongId(args.SongName, args.Duration)),
msg => msg.Headers.Referrer = new Uri(NetEaseRequestReferer)); msg => msg.Headers.Referrer = new Uri(NetEaseRequestReferer));
return Encoding.UTF8.GetBytes(lyricResponse); return Encoding.UTF8.GetBytes(lyricResponse);
@@ -70,8 +75,8 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
} }
return _lyricItemCollectionFactory.Build( return _lyricItemCollectionFactory.Build(
json.OriginalLyric.Text, json.OriginalLyric?.Text,
json.TranslationLyric.Text); json.TranslationLyric?.Text);
} }
protected virtual void ValidateSongSearchResponse(SongSearchResponse response, LyricDownloaderArgs args) protected virtual void ValidateSongSearchResponse(SongSearchResponse response, LyricDownloaderArgs args)

View File

@@ -1,3 +1,5 @@
using System;
namespace ZonyLrcTools.Cli.Infrastructure namespace ZonyLrcTools.Cli.Infrastructure
{ {
/// <summary> /// <summary>
@@ -10,6 +12,11 @@ namespace ZonyLrcTools.Cli.Infrastructure
/// </summary> /// </summary>
public string FilePath { get; } public string FilePath { get; }
/// <summary>
/// 歌曲的实际歌曲长度。
/// </summary>
public long? TotalTime { get; set; }
/// <summary> /// <summary>
/// 歌曲的名称。 /// 歌曲的名称。
/// </summary> /// </summary>

View File

@@ -0,0 +1,62 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ZonyLrcTools.Cli.Infrastructure.DependencyInject;
using ZonyLrcTools.Cli.Infrastructure.Network;
using ZonyLrcTools.Cli.Infrastructure.Updater.JsonModel;
namespace ZonyLrcTools.Cli.Infrastructure.Updater;
public class DefaultUpdater : ISingletonDependency
{
public const string UpdateUrl = "https://api.zony.me/lrc-tools/update";
private readonly IWarpHttpClient _warpHttpClient;
private readonly ILogger<DefaultUpdater> _logger;
public DefaultUpdater(IWarpHttpClient warpHttpClient,
ILogger<DefaultUpdater> logger)
{
_warpHttpClient = warpHttpClient;
_logger = logger;
}
public async Task CheckUpdateAsync()
{
var response = await _warpHttpClient.GetAsync<NewVersionResponse>(UpdateUrl);
if (response == null)
{
return;
}
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
if (response.NewVersion <= currentVersion)
{
return;
}
var importantItem = response.Items.FirstOrDefault(x => x.ItemType == NewVersionItemType.Important);
if (importantItem != null)
{
_logger.LogWarning($"发现了新版本,请点击下面的链接进行更新:{importantItem.Url}");
_logger.LogWarning($"最新版本号:{response.NewVersion},当前版本号: ${currentVersion}");
_logger.LogWarning($"更新内容:{response.NewVersionDescription}");
if (OperatingSystem.IsWindows())
{
Process.Start("explorer.exe", importantItem.Url);
}
else if (OperatingSystem.IsMacOS())
{
Process.Start("open", importantItem.Url);
}
else if (OperatingSystem.IsLinux())
{
Process.Start("xdg-open", importantItem.Url);
}
}
}
}

View File

@@ -0,0 +1,8 @@
namespace ZonyLrcTools.Cli.Infrastructure.Updater.JsonModel;
public class NewVersionItem
{
public string Url { get; set; }
public NewVersionItemType ItemType { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace ZonyLrcTools.Cli.Infrastructure.Updater.JsonModel;
public enum NewVersionItemType
{
Important,
NormalBinaryFile
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace ZonyLrcTools.Cli.Infrastructure.Updater.JsonModel;
public class NewVersionResponse
{
public Version NewVersion { get; set; }
public string NewVersionDescription { get; set; }
public List<NewVersionItem> Items { get; set; }
public DateTime UpdateTime { get; set; }
}

View File

@@ -3,40 +3,44 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType> <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> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.1" /> <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.1" />
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine" Version="4.0.1" /> <PackageReference Include="McMaster.Extensions.Hosting.CommandLine" Version="4.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.2.0" /> <PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Refit" Version="6.3.2" /> <PackageReference Include="Refit" Version="6.3.2" />
<PackageReference Include="Refit.HttpClientFactory" Version="6.3.2" /> <PackageReference Include="Refit.HttpClientFactory" Version="6.3.2" />
<PackageReference Include="Refit.Newtonsoft.Json" Version="6.3.2" /> <PackageReference Include="Refit.Newtonsoft.Json" Version="6.3.2" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.2.0" /> <PackageReference Include="Serilog.Extensions.Hosting" Version="5.0.1" />
<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.1.0" />
<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="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="TagLibSharp" Version="2.2.0" /> <PackageReference Include="TagLibSharp" Version="2.3.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>
</Project> </Project>

View File

@@ -9,6 +9,7 @@ globalOption:
isEnable: false # 是否启用代理。 isEnable: false # 是否启用代理。
ip: 127.0.0.1 # 代理服务 IP 地址。 ip: 127.0.0.1 # 代理服务 IP 地址。
port: 4780 # 代理服务端口号。 port: 4780 # 代理服务端口号。
updateUrl: https://api.zony.me/lrc-tools/update # 更新检查地址。
# 下载器的相关参数配置。 # 下载器的相关参数配置。
provider: provider:
@@ -35,14 +36,18 @@ globalOption:
plugin: plugin:
- name: NetEase # 基于网易云音乐的歌词下载器。 - name: NetEase # 基于网易云音乐的歌词下载器。
priority: 1 # 优先级,升序排列,改为 -1 时禁用。 priority: 1 # 优先级,升序排列,改为 -1 时禁用。
depth: 30 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
- name: QQ # 基于 QQ 音乐的歌词下载器。 - name: QQ # 基于 QQ 音乐的歌词下载器。
priority: 2 priority: 2
# depth: 10 # 暂时不支持。
- name: KuGou # 基于酷狗音乐的歌词下载器。 - name: KuGou # 基于酷狗音乐的歌词下载器。
priority: 3 priority: 3
depth: 10
# 歌词下载的一些共有配置参数。 # 歌词下载的一些共有配置参数。
config: config:
isOneLine: true # 双语歌词是否合并为一行展示。 isOneLine: true # 双语歌词是否合并为一行展示。
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。 lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
isEnableTranslation: true # 是否启用翻译歌词。 isEnableTranslation: true # 是否启用翻译歌词。
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件 isOnlyOutputTranslation: false # 是否只输出翻译歌词
fileEncoding: 'utf-8' # 歌词文件的编码格式 isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件
fileEncoding: 'utf-8' # 歌词文件的编码格式。

View File

@@ -1,5 +1,4 @@
#!/bin/bash #!/bin/bash
read -r -p "请输入版本号:" Version
Platforms=('win-x64' 'linux-x64' 'osx-x64') Platforms=('win-x64' 'linux-x64' 'osx-x64')
if ! [ -d './TempFiles' ]; if ! [ -d './TempFiles' ];
@@ -11,11 +10,11 @@ rm -rf ./TempFiles/*
for platform in "${Platforms[@]}" for platform in "${Platforms[@]}"
do do
dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true dotnet publish -r "$platform" -c Release -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true || exit 1
cd ./bin/Release/net5.0/"$platform"/publish/
zip -r ./ZonyLrcTools_"$platform"_"$Version".zip ./
cd ../../../../../
mv ./bin/Release/net5.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$Version".zip ./TempFiles cd ./bin/Release/net6.0/"$platform"/publish/ || exit 1
zip -r ./ZonyLrcTools_"$platform"_"${PUBLISH_VERSION}".zip ./ || exit 1
cd ../../../../../ || exit 1
mv ./bin/Release/net6.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$PUBLISH_VERSION".zip ./TempFiles
done done

View File

@@ -1,8 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
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
@@ -39,7 +42,7 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
{ {
var lyric = await _lyricDownloader.DownloadAsync("シンデレラ (Giga First Night Remix)", "DECO 27 ギガP"); var lyric = await _lyricDownloader.DownloadAsync("シンデレラ (Giga First Night Remix)", "DECO 27 ギガP");
lyric.ShouldNotBeNull(); lyric.ShouldNotBeNull();
lyric.IsPruneMusic.ShouldBe(true); lyric.IsPruneMusic.ShouldBe(false);
} }
[Fact] [Fact]
@@ -77,5 +80,22 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
lyric.ShouldNotBeNull(); lyric.ShouldNotBeNull();
} }
[Fact]
public async Task DownloaderAsync_Issue101_Test()
{
var lyric = await _lyricDownloader.DownloadAsync("君への嘘", "VALSHE");
lyric.ShouldNotBeEmpty();
}
[Fact]
public async Task DownloadAsync_Issue114_Test()
{
var options = ServiceProvider.GetRequiredService<IOptions<ToolOptions>>();
options.Value.Provider.Lyric.Config.IsOnlyOutputTranslation = true;
var lyric = await _lyricDownloader.DownloadAsync("Bones", "Image Dragons");
lyric.ToString().ShouldNotContain("Gimme, gimme, gimme some time to think");
}
} }
} }

View File

@@ -7,10 +7,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Shouldly" Version="4.0.3" /> <PackageReference Include="Shouldly" Version="4.1.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

5
versions/release.md Normal file
View File

@@ -0,0 +1,5 @@
New Features: None
Breaking Changes: None
Enhancement: None
Fixed Bugs:
- [[#115](https://github.com/real-zony/ZonyLrcToolsX/issues/115)] NAudio 有问题,暂时屏蔽该功能。