58 Commits

Author SHA1 Message Date
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
real-zony
d4a6a46078 feat: Restore config yaml file. 2022-07-25 20:12:19 +08:00
real-zony
0772f5888f fix: Fix the problem of QQ lyrics downloader prompting error.
The reason for this is that the old API has been deprecated.
2022-07-25 20:11:30 +08:00
real-zony
950652c040 fix: Fixed the issue of inaccurate download results. 2022-07-25 19:45:35 +08:00
real-zony
55f720c1a1 Fixed the null reference exception prompted when downloading pure music. 2022-07-24 23:48:46 +08:00
real-zony
7463709a39 feat: Deleted unused components. 2022-04-28 23:58:03 +08:00
real-zony
07e660e13b Add new frontend code. 2022-04-28 23:53:14 +08:00
real-zony
a51b399a6c Delete frontend code. 2022-04-28 23:50:18 +08:00
real-zony
279eba48f8 feat: Support configure lyrics file encoding. 2022-04-27 21:38:51 +08:00
real-zony
f3b1dacb0c fix: Improve tag loader conditions. 2022-04-27 21:25:15 +08:00
52 changed files with 641 additions and 869 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

@@ -3,10 +3,12 @@ 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;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using NAudio.Wave;
using ZonyLrcTools.Cli.Config; using ZonyLrcTools.Cli.Config;
using ZonyLrcTools.Cli.Infrastructure; using ZonyLrcTools.Cli.Infrastructure;
using ZonyLrcTools.Cli.Infrastructure.Album; using ZonyLrcTools.Cli.Infrastructure.Album;
@@ -16,6 +18,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
{ {
@@ -49,7 +52,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,21 +63,25 @@ 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)
{ {
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;
@@ -82,7 +89,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,8 +99,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;
@@ -128,7 +133,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} 个音乐文件的标签信息。");
@@ -161,7 +170,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
await Task.WhenAll(warpTaskList); await Task.WhenAll(warpTaskList);
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count} 失败{musicInfos.Count - musicInfos.Count(m => m.IsSuccessful)}。"); _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)
@@ -170,7 +179,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");
@@ -179,22 +188,22 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
File.Delete(lyricFilePath); File.Delete(lyricFilePath);
} }
info.IsSuccessful = true;
if (lyric.IsPruneMusic) if (lyric.IsPruneMusic)
{ {
info.IsSuccessful = true; return;
} }
await using var stream = new FileStream(lyricFilePath, 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;
{
info.IsSuccessful = false;
}
_logger.LogWarningInfo(ex); _logger.LogWarningInfo(ex);
} }
@@ -203,10 +212,6 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
_logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。"); _logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。");
info.IsSuccessful = false; info.IsSuccessful = false;
} }
finally
{
info.IsSuccessful = true;
}
} }
foreach (var downloader in downloaderList) foreach (var downloader in downloaderList)
@@ -221,6 +226,17 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
} }
} }
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 > Ablum image download logic < #region > Ablum image download logic <
@@ -236,7 +252,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
await Task.WhenAll(warpTaskList); await Task.WhenAll(warpTaskList);
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => !m.IsSuccessful)}。"); _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)

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
@@ -31,4 +37,14 @@ public class LyricConfigOption
/// 如果歌词文件已经存在,是否跳过这些文件 /// 如果歌词文件已经存在,是否跳过这些文件
/// </summary> /// </summary>
public bool IsSkipExistLyricFiles { get; set; } = false; public bool IsSkipExistLyricFiles { get; set; } = false;
/// <summary>
/// 歌词文件的编码格式。
/// </summary>
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

@@ -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 > <

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

@@ -107,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());
}
} }
} }

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

@@ -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("platform")]
public string Platform { get; protected 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("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()

View File

@@ -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; }
} }
} }

View File

@@ -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/";

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

@@ -43,7 +43,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
try try
{ {
var info = await provider.LoadAsync(filePath); var info = await provider.LoadAsync(filePath);
if (info != null) if (info != null && !string.IsNullOrEmpty(info.Name))
{ {
HandleBlockWord(info); HandleBlockWord(info);
return info; return info;

View File

@@ -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;
@@ -23,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();
@@ -94,7 +97,8 @@ namespace ZonyLrcTools.Cli
switch (ex) switch (ex)
{ {
case ErrorCodeException exception: case ErrorCodeException exception:
Log.Logger.Error($"出现了未处理的异常。\n错误代码: {exception.ErrorCode}\n错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n原始信息:{exception.Message}\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:

View File

@@ -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": "扫描文件时出现了错误。",

View File

@@ -10,16 +10,18 @@
<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="NAudio" Version="2.1.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="TagLibSharp" Version="2.2.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -35,13 +35,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 # 是否只输出翻译歌词
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/ cd ./bin/Release/net6.0/"$platform"/publish/ || exit 1
zip -r ./ZonyLrcTools_"$platform"_"$Version".zip ./ zip -r ./ZonyLrcTools_"$platform"_"${PUBLISH_VERSION}".zip ./ || exit 1
cd ../../../../../ cd ../../../../../ || exit 1
mv ./bin/Release/net5.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$Version".zip ./TempFiles mv ./bin/Release/net6.0/"$platform"/publish/ZonyLrcTools_"$platform"_"$PUBLISH_VERSION".zip ./TempFiles
done done

View File

@@ -1,43 +0,0 @@
{
"name": "app-ui",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^2.6.14"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

@@ -1,28 +0,0 @@
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

View File

@@ -1,58 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

3
src/ui/.browserslistrc Normal file
View File

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

23
src/ui/.gitignore vendored Normal file
View 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?

View File

@@ -1,4 +1,4 @@
# app-ui # ui
## Project setup ## Project setup
``` ```
@@ -15,10 +15,5 @@ yarn serve
yarn build yarn build
``` ```
### Lints and fixes files
```
yarn lint
```
### Customize configuration ### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/). See [Configuration Reference](https://cli.vuejs.org/config/).

23
src/ui/package.json Normal file
View 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"
}
}

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

5
src/ui/src/App.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div id="app">
<router-view/>
</div>
</template>

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

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

View 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
View 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: {
}
})

View File

@@ -0,0 +1,12 @@
<template>
<div class="home">
</div>
</template>
<script>
// @ is an alias to /src
export default {
name: 'HomeView'
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -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;

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]
@@ -69,5 +72,30 @@ 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();
}
[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>

16
versions/release.md Normal file
View File

@@ -0,0 +1,16 @@
New Features:
- [[#114](https://github.com/real-zony/ZonyLrcToolsX/issues/114)] 下载双语歌词的时候,可以控制是否仅输出翻译歌词。
- [[6d7ee04](https://github.com/real-zony/ZonyLrcToolsX/commit/6d7ee04b741191b5526075cdd3c3fc2c5737880f)] 提供深度搜索选项,以便更加准确地匹配歌曲歌词,值越大结果越准确,但是搜索速度越慢。
Breaking Changes: None
Enhancement:
- 关于网易云歌词下载器在搜索歌曲名带括号的歌曲时,会搜索不到的情况,现在默认会移除掉歌曲名中的括号进行搜索以增强准确性。
- [[#100](https://github.com/real-zony/ZonyLrcToolsX/issues/100)] 优化歌曲查找逻辑,优先匹配长度符合实际歌曲长度的歌词。
Fixed Bugs:
- [[#106](https://github.com/real-zony/ZonyLrcToolsX/issues/106)] 跳过已存在歌词的选项存在逻辑问题,会导致下载专辑图像的时候也会跳过带歌词的歌曲文件。
- [[85f325b](https://github.com/real-zony/ZonyLrcToolsX/commit/85f325b300dd4e6f54c783b2f9065ab4c566e3ad)] 修复某些极端情况下网易云歌词下载器报错的问题。