mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-09-05 13:07:26 +00:00
Compare commits
58 Commits
Alpha.2022
...
ZonyLrcToo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ba91108ca4 | ||
![]() |
f597e24ff7 | ||
![]() |
d453d520d4 | ||
![]() |
9f254aae8e | ||
![]() |
c9fa0de95f | ||
![]() |
6d7ee04b74 | ||
![]() |
f519eb1251 | ||
![]() |
85f325b300 | ||
![]() |
140043db79 | ||
![]() |
c583a9c278 | ||
![]() |
8385f6f118 | ||
![]() |
be4380c744 | ||
![]() |
87f6a98668 | ||
![]() |
83fcc91fb8 | ||
![]() |
b4c229ccca | ||
![]() |
71302c7262 | ||
![]() |
1bec50e409 | ||
![]() |
f7cae8062c | ||
![]() |
c73f0d8eea | ||
![]() |
fe7c9115d5 | ||
![]() |
e13fb47aeb | ||
![]() |
32891d4208 | ||
![]() |
770d246787 | ||
![]() |
589151a77d | ||
![]() |
65af048f72 | ||
![]() |
99fd3b5021 | ||
![]() |
e4420e749a | ||
![]() |
5f22dafb1e | ||
![]() |
c2d8ee4341 | ||
![]() |
6a4435eb0d | ||
![]() |
346e07e9e7 | ||
![]() |
efdb3e8441 | ||
![]() |
0438433284 | ||
![]() |
d34a830660 | ||
![]() |
975f61a8e7 | ||
![]() |
8ba78c2681 | ||
![]() |
019bdb571c | ||
![]() |
a2aeed021d | ||
![]() |
0e8d361ed9 | ||
![]() |
c3b381a5f7 | ||
![]() |
8acba586b0 | ||
![]() |
5905d4b384 | ||
![]() |
2ad771e62e | ||
![]() |
403d7b151d | ||
![]() |
5c8c9af9ab | ||
![]() |
bfd23fc14e | ||
![]() |
81271a5aa5 | ||
![]() |
80a0bcdcee | ||
![]() |
172174d9e3 | ||
![]() |
d4a6a46078 | ||
![]() |
0772f5888f | ||
![]() |
950652c040 | ||
![]() |
55f720c1a1 | ||
![]() |
7463709a39 | ||
![]() |
07e660e13b | ||
![]() |
a51b399a6c | ||
![]() |
279eba48f8 | ||
![]() |
f3b1dacb0c |
62
.github/workflows/dotnet.yml
vendored
62
.github/workflows/dotnet.yml
vendored
@@ -3,23 +3,59 @@ name: .NET
|
||||
on:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
# paths:
|
||||
# - "versions/**"
|
||||
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
|
||||
- name: Get build version
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date +'%Y%m%d')${{github.run_number}}"
|
||||
- name: Checkout Code
|
||||
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: 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
|
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using McMaster.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NAudio.Wave;
|
||||
using ZonyLrcTools.Cli.Config;
|
||||
using ZonyLrcTools.Cli.Infrastructure;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Album;
|
||||
@@ -16,6 +18,7 @@ using ZonyLrcTools.Cli.Infrastructure.IO;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Lyric;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Tag;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Threading;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
{
|
||||
@@ -49,7 +52,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
[Option("-d|--dir", CommandOptionType.SingleValue, Description = "指定需要扫描的目录。")]
|
||||
[DirectoryExists]
|
||||
public string Directory { get; set; }
|
||||
public string SongsDirectory { get; set; }
|
||||
|
||||
[Option("-l|--lyric", CommandOptionType.NoValue, Description = "指定程序需要下载歌词文件。")]
|
||||
public bool DownloadLyric { get; set; }
|
||||
@@ -60,21 +63,25 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
[Option("-n|--number", CommandOptionType.SingleValue, Description = "指定下载时候的线程数量。(默认值 2)")]
|
||||
public int ParallelNumber { get; set; } = 2;
|
||||
|
||||
[Option] public string ErrorMessage { get; set; } = Path.Combine(Directory.GetCurrentDirectory(), "error.log");
|
||||
|
||||
#endregion
|
||||
|
||||
protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
|
||||
{
|
||||
var files = await ScanMusicFilesAsync();
|
||||
var musicInfos = await LoadMusicInfoAsync(files);
|
||||
|
||||
if (DownloadLyric)
|
||||
{
|
||||
await DownloadLyricFilesAsync(musicInfos);
|
||||
await DownloadLyricFilesAsync(
|
||||
await LoadMusicInfoAsync(
|
||||
RemoveExistLyricFiles(
|
||||
await ScanMusicFilesAsync())));
|
||||
}
|
||||
|
||||
if (DownloadAlbum)
|
||||
{
|
||||
await DownloadAlbumAsync(musicInfos);
|
||||
await DownloadAlbumAsync(
|
||||
await LoadMusicInfoAsync(
|
||||
await ScanMusicFilesAsync()));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -82,7 +89,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
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)
|
||||
.ToList();
|
||||
|
||||
@@ -92,8 +99,6 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
throw new ErrorCodeException(ErrorCodes.NoFilesWereScanned);
|
||||
}
|
||||
|
||||
files = RemoveExistLyricFiles(files);
|
||||
|
||||
_logger.LogInformation($"已经扫描到了 {files.Count} 个音乐文件。");
|
||||
|
||||
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 result = (await Task.WhenAll(warpTaskList))
|
||||
.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} 个音乐文件的标签信息。");
|
||||
|
||||
@@ -161,7 +170,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
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)
|
||||
@@ -170,7 +179,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
{
|
||||
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)!,
|
||||
$"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");
|
||||
|
||||
@@ -179,22 +188,22 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
File.Delete(lyricFilePath);
|
||||
}
|
||||
|
||||
info.IsSuccessful = true;
|
||||
|
||||
if (lyric.IsPruneMusic)
|
||||
{
|
||||
info.IsSuccessful = true;
|
||||
return;
|
||||
}
|
||||
|
||||
await using var stream = new FileStream(lyricFilePath, FileMode.Create);
|
||||
await using var sw = new StreamWriter(stream);
|
||||
await sw.WriteAsync(lyric.ToString());
|
||||
await sw.FlushAsync();
|
||||
await using var sw = new BinaryWriter(stream);
|
||||
|
||||
sw.Write(EncodingConvert(lyric));
|
||||
await stream.FlushAsync();
|
||||
}
|
||||
catch (ErrorCodeException ex)
|
||||
{
|
||||
if (ex.ErrorCode == ErrorCodes.NoMatchingSong)
|
||||
{
|
||||
info.IsSuccessful = false;
|
||||
}
|
||||
info.IsSuccessful = ex.ErrorCode == ErrorCodes.NoMatchingSong;
|
||||
|
||||
_logger.LogWarningInfo(ex);
|
||||
}
|
||||
@@ -203,10 +212,6 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
_logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。");
|
||||
info.IsSuccessful = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
info.IsSuccessful = true;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#region > Ablum image download logic <
|
||||
@@ -236,7 +252,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
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)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Lyric;
|
||||
|
||||
namespace ZonyLrcTools.Cli.Config;
|
||||
@@ -8,6 +9,11 @@ public class LyricOption
|
||||
public IEnumerable<LyricProviderOption> Plugin { get; set; }
|
||||
|
||||
public LyricConfigOption Config { get; set; }
|
||||
|
||||
public LyricProviderOption GetLyricProviderOption(string name)
|
||||
{
|
||||
return Plugin.FirstOrDefault(x => x.Name == name);
|
||||
}
|
||||
}
|
||||
|
||||
public class LyricConfigOption
|
||||
@@ -31,4 +37,14 @@ public class LyricConfigOption
|
||||
/// 如果歌词文件已经存在,是否跳过这些文件
|
||||
/// </summary>
|
||||
public bool IsSkipExistLyricFiles { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 歌词文件的编码格式。
|
||||
/// </summary>
|
||||
public string FileEncoding { get; set; } = "utf-8";
|
||||
|
||||
/// <summary>
|
||||
/// 是否只输出翻译歌词。
|
||||
/// </summary>
|
||||
public bool IsOnlyOutputTranslation { get; set; } = false;
|
||||
}
|
@@ -11,5 +11,10 @@
|
||||
/// 歌词下载时的优先级,当值为 -1 时是禁用。
|
||||
/// </summary>
|
||||
public int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 搜索深度,值越大搜索结果越多,但搜索时间越长。
|
||||
/// </summary>
|
||||
public int Depth { get; set; }
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
using ZonyLrcTools.Cli.Infrastructure.Lyric;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Tag;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Tag;
|
||||
|
||||
namespace ZonyLrcTools.Cli.Config;
|
||||
|
||||
|
@@ -51,7 +51,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Album.NetEase
|
||||
|
||||
var songDetailJsonStr = await _warpHttpClient.GetAsync(
|
||||
GetMusicInfoApi,
|
||||
new GetSongDetailsRequest(searchResult.GetFirstMatchSongId(songName)),
|
||||
new GetSongDetailsRequest(searchResult.GetFirstMatchSongId(songName, null)),
|
||||
_defaultOption);
|
||||
|
||||
var url = JObject.Parse(songDetailJsonStr).SelectToken("$.songs[0].album.picUrl")?.Value<string>();
|
||||
|
@@ -27,6 +27,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Exceptions
|
||||
/// </summary>
|
||||
public const int NoFilesWereScanned = 10004;
|
||||
|
||||
/// <summary>
|
||||
/// 文本: 指定的编码不受支持,请检查配置,所有受支持的编码名称。
|
||||
/// </summary>
|
||||
public const int NotSupportedFileEncoding = 10005;
|
||||
|
||||
#endregion
|
||||
|
||||
#region > 警告信息 <
|
||||
|
@@ -12,8 +12,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
||||
/// </summary>
|
||||
/// <param name="songName">歌曲的名称。</param>
|
||||
/// <param name="artist">歌曲的作者。</param>
|
||||
/// <param name="duration">歌曲的时长。</param>
|
||||
/// <returns>歌曲的歌词数据对象。</returns>
|
||||
ValueTask<LyricItemCollection> DownloadAsync(string songName, string artist);
|
||||
ValueTask<LyricItemCollection> DownloadAsync(string songName, string artist, long? duration = null);
|
||||
|
||||
/// <summary>
|
||||
/// 下载器的名称。
|
||||
|
@@ -12,11 +12,17 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.KuGou.JsonModel
|
||||
|
||||
[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;
|
||||
Platform = "WebFilter";
|
||||
Keyword = HttpUtility.UrlEncode($"{musicName}+{artistName}", Encoding.UTF8);
|
||||
PageSize = pageSize;
|
||||
Page = 1;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ZonyLrcTools.Cli.Config;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Exceptions;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Lyric.KuGou.JsonModel;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Network;
|
||||
@@ -14,22 +16,25 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.KuGou
|
||||
|
||||
private readonly IWarpHttpClient _warpHttpClient;
|
||||
private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory;
|
||||
private readonly ToolOptions _options;
|
||||
|
||||
private const string KuGouSearchMusicUrl = @"https://songsearch.kugou.com/song_search_v2";
|
||||
private const string KuGouGetLyricAccessKeyUrl = @"http://lyrics.kugou.com/search";
|
||||
private const string KuGouGetLyricUrl = @"http://lyrics.kugou.com/download";
|
||||
|
||||
public KuGourLyricDownloader(IWarpHttpClient warpHttpClient,
|
||||
ILyricItemCollectionFactory lyricItemCollectionFactory)
|
||||
ILyricItemCollectionFactory lyricItemCollectionFactory,
|
||||
IOptions<ToolOptions> options)
|
||||
{
|
||||
_warpHttpClient = warpHttpClient;
|
||||
_lyricItemCollectionFactory = lyricItemCollectionFactory;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
protected override async ValueTask<byte[]> DownloadDataAsync(LyricDownloaderArgs args)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@@ -16,10 +16,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
||||
/// </summary>
|
||||
/// <param name="songName">歌曲名称。</param>
|
||||
/// <param name="artist">歌曲作者/艺术家。</param>
|
||||
/// <param name="duration">歌曲的时长。</param>
|
||||
/// <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);
|
||||
var downloadDataBytes = await DownloadDataAsync(args);
|
||||
return await GenerateLyricAsync(downloadDataBytes, args);
|
||||
|
@@ -6,10 +6,13 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
||||
|
||||
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;
|
||||
Artist = artist;
|
||||
Duration = duration;
|
||||
}
|
||||
}
|
||||
}
|
@@ -107,5 +107,10 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
||||
ForEach(lyric => lyricBuilder.Append(lyric).Append(Option.LineBreak));
|
||||
return lyricBuilder.ToString().TrimEnd(Option.LineBreak);
|
||||
}
|
||||
|
||||
public byte[] GetUtf8Bytes()
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(ToString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -43,6 +43,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
|
||||
if (_options.Provider.Lyric.Config.IsEnableTranslation && !string.IsNullOrEmpty(translationLyric))
|
||||
{
|
||||
var translatedLyric = InternalBuildLyricObject(new LyricItemCollection(_options.Provider.Lyric.Config), translationLyric);
|
||||
if (_options.Provider.Lyric.Config.IsOnlyOutputTranslation)
|
||||
{
|
||||
return translatedLyric;
|
||||
}
|
||||
|
||||
return lyric + translatedLyric;
|
||||
}
|
||||
|
||||
|
@@ -8,9 +8,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
|
||||
{
|
||||
public GetLyricRequest(long songId)
|
||||
{
|
||||
OS = "osx";
|
||||
OS = "ios";
|
||||
Id = songId;
|
||||
Lv = Kv = Tv = -1;
|
||||
Lv = Kv = Tv = Rv = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -30,5 +30,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
|
||||
[JsonProperty("kv")] public int Kv { get; }
|
||||
|
||||
[JsonProperty("tv")] public int Tv { get; }
|
||||
|
||||
[JsonProperty("rv")] public int Rv { get; set; }
|
||||
|
||||
[JsonProperty("crypto")] public string Protocol { get; set; } = "api";
|
||||
}
|
||||
}
|
@@ -22,6 +22,12 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
|
||||
[JsonProperty("tlyric")]
|
||||
public InnerLyric TranslationLyric { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 如果存在罗马音歌词,则本项内容为罗马音歌词。
|
||||
/// </summary>
|
||||
[JsonProperty("romalrc")]
|
||||
public InnerLyric RomaLyric { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态码。
|
||||
/// </summary>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@@ -42,18 +43,25 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
|
||||
[JsonProperty("limit")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[JsonProperty("crypto")] public string Crypto { get; set; } = "weapi";
|
||||
|
||||
public SongSearchRequest()
|
||||
{
|
||||
CsrfToken = string.Empty;
|
||||
Type = 1;
|
||||
Offset = 0;
|
||||
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);
|
||||
Limit = limit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
@@ -10,10 +11,20 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel
|
||||
|
||||
[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);
|
||||
return item?.Id ?? Items.SongItems[0].Id;
|
||||
var perfectMatch = Items.SongItems.FirstOrDefault(x => x.Name == songName);
|
||||
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>
|
||||
[JsonProperty("album")]
|
||||
public SongAlbumModel Album { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 歌曲的实际长度。
|
||||
/// </summary>
|
||||
[JsonProperty("duration")]
|
||||
public long Duration { get; set; }
|
||||
}
|
||||
|
||||
public class SongArtistModel
|
||||
|
@@ -2,7 +2,9 @@ using System;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using ZonyLrcTools.Cli.Config;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Exceptions;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase.JsonModel;
|
||||
using ZonyLrcTools.Cli.Infrastructure.Network;
|
||||
@@ -15,6 +17,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
|
||||
|
||||
private readonly IWarpHttpClient _warpHttpClient;
|
||||
private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory;
|
||||
private readonly ToolOptions _options;
|
||||
|
||||
private const string NetEaseSearchMusicUrl = @"https://music.163.com/api/search/get/web";
|
||||
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";
|
||||
|
||||
public NetEaseLyricDownloader(IWarpHttpClient warpHttpClient,
|
||||
ILyricItemCollectionFactory lyricItemCollectionFactory)
|
||||
ILyricItemCollectionFactory lyricItemCollectionFactory,
|
||||
IOptions<ToolOptions> options)
|
||||
{
|
||||
_warpHttpClient = warpHttpClient;
|
||||
_lyricItemCollectionFactory = lyricItemCollectionFactory;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
protected override async ValueTask<byte[]> DownloadDataAsync(LyricDownloaderArgs args)
|
||||
{
|
||||
var searchResult = await _warpHttpClient.PostAsync<SongSearchResponse>(
|
||||
NetEaseSearchMusicUrl,
|
||||
new SongSearchRequest(args.SongName, args.Artist),
|
||||
new SongSearchRequest(args.SongName, args.Artist, _options.Provider.Lyric.GetLyricProviderOption(DownloaderName).Depth),
|
||||
true,
|
||||
msg =>
|
||||
{
|
||||
@@ -48,7 +53,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
|
||||
|
||||
var lyricResponse = await _warpHttpClient.GetAsync(
|
||||
NetEaseGetLyricUrl,
|
||||
new GetLyricRequest(searchResult.GetFirstMatchSongId(args.SongName)),
|
||||
new GetLyricRequest(searchResult.GetFirstMatchSongId(args.SongName, args.Duration)),
|
||||
msg => msg.Headers.Referrer = new Uri(NetEaseRequestReferer));
|
||||
|
||||
return Encoding.UTF8.GetBytes(lyricResponse);
|
||||
@@ -70,8 +75,8 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
|
||||
}
|
||||
|
||||
return _lyricItemCollectionFactory.Build(
|
||||
json.OriginalLyric.Text,
|
||||
json.TranslationLyric.Text);
|
||||
json.OriginalLyric?.Text,
|
||||
json.TranslationLyric?.Text);
|
||||
}
|
||||
|
||||
protected virtual void ValidateSongSearchResponse(SongSearchResponse response, LyricDownloaderArgs args)
|
||||
|
@@ -6,32 +6,26 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
|
||||
{
|
||||
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()
|
||||
{
|
||||
RemotePlace = "txt.yqq.song";
|
||||
Page = 1;
|
||||
Limit = 5;
|
||||
ResultFormat = "json";
|
||||
InCharset = "utf8";
|
||||
OutCharset = "utf8";
|
||||
Platform = "yqq";
|
||||
Format = "json";
|
||||
InCharset = OutCharset = "utf-8";
|
||||
Platform = "yqq.json";
|
||||
}
|
||||
|
||||
public SongSearchRequest(string musicName, string artistName) : this()
|
||||
|
@@ -17,11 +17,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
|
||||
|
||||
public class QQMusicInnerSongModel
|
||||
{
|
||||
[JsonProperty("list")] public List<QQMusicInnerSongItem> SongItems { get; set; }
|
||||
[JsonProperty("itemlist")] public List<QQMusicInnerSongItem> SongItems { get; set; }
|
||||
}
|
||||
|
||||
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 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 QQMusicRequestReferer = @"https://y.qq.com/";
|
||||
|
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace ZonyLrcTools.Cli.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
@@ -10,6 +12,11 @@ namespace ZonyLrcTools.Cli.Infrastructure
|
||||
/// </summary>
|
||||
public string FilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 歌曲的实际歌曲长度。
|
||||
/// </summary>
|
||||
public long? TotalTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 歌曲的名称。
|
||||
/// </summary>
|
||||
|
@@ -43,7 +43,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
|
||||
try
|
||||
{
|
||||
var info = await provider.LoadAsync(filePath);
|
||||
if (info != null)
|
||||
if (info != null && !string.IsNullOrEmpty(info.Name))
|
||||
{
|
||||
HandleBlockWord(info);
|
||||
return info;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using McMaster.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -23,6 +24,8 @@ namespace ZonyLrcTools.Cli
|
||||
{
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
ConfigureLogger();
|
||||
ConfigureErrorMessage();
|
||||
|
||||
@@ -94,7 +97,8 @@ namespace ZonyLrcTools.Cli
|
||||
switch (ex)
|
||||
{
|
||||
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);
|
||||
return exception.ErrorCode;
|
||||
case { } unknownException:
|
||||
|
@@ -3,7 +3,8 @@
|
||||
"10001": "待搜索的后缀不能为空。",
|
||||
"10002": "需要扫描的目录不存在,请确认路径是否正确。",
|
||||
"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": {
|
||||
"50001": "扫描文件时出现了错误。",
|
||||
|
@@ -10,16 +10,18 @@
|
||||
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<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="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Refit" Version="6.3.2" />
|
||||
<PackageReference Include="Refit.HttpClientFactory" 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.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="TagLibSharp" Version="2.2.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -35,13 +35,18 @@ globalOption:
|
||||
plugin:
|
||||
- name: NetEase # 基于网易云音乐的歌词下载器。
|
||||
priority: 1 # 优先级,升序排列,改为 -1 时禁用。
|
||||
depth: 30 # 搜索深度,值越大搜索结果越多,但搜索时间越长。
|
||||
- name: QQ # 基于 QQ 音乐的歌词下载器。
|
||||
priority: 2
|
||||
# depth: 10 # 暂时不支持。
|
||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||
priority: 3
|
||||
depth: 10
|
||||
# 歌词下载的一些共有配置参数。
|
||||
config:
|
||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||
isEnableTranslation: true # 是否启用翻译歌词。
|
||||
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
|
||||
isEnableTranslation: true # 是否启用翻译歌词。
|
||||
isOnlyOutputTranslation: false # 是否只输出翻译歌词。
|
||||
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
|
||||
fileEncoding: 'utf-8' # 歌词文件的编码格式。
|
@@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
read -r -p "请输入版本号:" Version
|
||||
Platforms=('win-x64' 'linux-x64' 'osx-x64')
|
||||
|
||||
if ! [ -d './TempFiles' ];
|
||||
@@ -11,11 +10,11 @@ rm -rf ./TempFiles/*
|
||||
|
||||
for platform in "${Platforms[@]}"
|
||||
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 ../../../../../
|
||||
cd ./bin/Release/net6.0/"$platform"/publish/ || exit 1
|
||||
zip -r ./ZonyLrcTools_"$platform"_"${PUBLISH_VERSION}".zip ./ || exit 1
|
||||
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
|
@@ -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"
|
||||
]
|
||||
}
|
@@ -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>
|
@@ -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
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?
|
@@ -1,4 +1,4 @@
|
||||
# app-ui
|
||||
# ui
|
||||
|
||||
## Project setup
|
||||
```
|
||||
@@ -15,10 +15,5 @@ yarn serve
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
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"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
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>
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
@@ -1,8 +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({
|
||||
render: h => h(App),
|
||||
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>
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
@@ -1,8 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZonyLrcTools.Cli.Config;
|
||||
using ZonyLrcTools.Cli.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");
|
||||
lyric.ShouldNotBeNull();
|
||||
lyric.IsPruneMusic.ShouldBe(true);
|
||||
lyric.IsPruneMusic.ShouldBe(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -69,5 +72,30 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,10 +7,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="Shouldly" Version="4.0.3" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
|
||||
<PackageReference Include="Shouldly" Version="4.1.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
16
versions/release.md
Normal file
16
versions/release.md
Normal 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)] 修复某些极端情况下网易云歌词下载器报错的问题。
|
Reference in New Issue
Block a user