mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-09-06 05:36:53 +00:00
refactor: Refactor lyrics download logic and abstract a new logger.
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
using System;
|
||||
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 ZonyLrcTools.Cli.Infrastructure.Tag;
|
||||
using ZonyLrcTools.Common;
|
||||
@@ -15,6 +12,7 @@ using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Common.Infrastructure.Exceptions;
|
||||
using ZonyLrcTools.Common.Infrastructure.Extensions;
|
||||
using ZonyLrcTools.Common.Infrastructure.IO;
|
||||
using ZonyLrcTools.Common.Infrastructure.Logging;
|
||||
using ZonyLrcTools.Common.Infrastructure.Threading;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
using File = System.IO.File;
|
||||
@@ -24,26 +22,25 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
[Command("download", Description = "下载歌词文件或专辑图像。")]
|
||||
public class DownloadCommand : ToolCommandBase
|
||||
{
|
||||
private readonly ILogger<DownloadCommand> _logger;
|
||||
private readonly ILyricsDownloader _lyricsDownloader;
|
||||
private readonly IFileScanner _fileScanner;
|
||||
private readonly ITagLoader _tagLoader;
|
||||
private readonly IEnumerable<ILyricsProvider> _lyricDownloaderList;
|
||||
private readonly IEnumerable<IAlbumDownloader> _albumDownloaderList;
|
||||
private readonly ITagLoader _tagLoader;
|
||||
private readonly IWarpLogger _logger;
|
||||
|
||||
private readonly GlobalOptions _options;
|
||||
|
||||
public DownloadCommand(ILogger<DownloadCommand> logger,
|
||||
IFileScanner fileScanner,
|
||||
public DownloadCommand(IFileScanner fileScanner,
|
||||
IOptions<GlobalOptions> options,
|
||||
IEnumerable<IAlbumDownloader> albumDownloaderList,
|
||||
ITagLoader tagLoader,
|
||||
IEnumerable<ILyricsProvider> lyricDownloaderList,
|
||||
IEnumerable<IAlbumDownloader> albumDownloaderList)
|
||||
ILyricsDownloader lyricsDownloader, IWarpLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_fileScanner = fileScanner;
|
||||
_tagLoader = tagLoader;
|
||||
_lyricDownloaderList = lyricDownloaderList;
|
||||
_albumDownloaderList = albumDownloaderList;
|
||||
_tagLoader = tagLoader;
|
||||
_lyricsDownloader = lyricsDownloader;
|
||||
_logger = logger;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
@@ -62,18 +59,14 @@ 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)
|
||||
{
|
||||
if (DownloadLyric)
|
||||
{
|
||||
await DownloadLyricFilesAsync(
|
||||
await LoadMusicInfoAsync(
|
||||
RemoveExistLyricFiles(
|
||||
await ScanMusicFilesAsync())));
|
||||
var musicInfos = await LoadMusicInfoAsync(RemoveExistLyricFiles(await ScanMusicFilesAsync()));
|
||||
await _lyricsDownloader.DownloadAsync(musicInfos.ToList(), ParallelNumber);
|
||||
}
|
||||
|
||||
if (DownloadAlbum)
|
||||
@@ -94,11 +87,11 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
if (files.Count == 0)
|
||||
{
|
||||
_logger.LogError("没有找到任何音乐文件。");
|
||||
await _logger.ErrorAsync("没有找到任何音乐文件。");
|
||||
throw new ErrorCodeException(ErrorCodes.NoFilesWereScanned);
|
||||
}
|
||||
|
||||
_logger.LogInformation($"已经扫描到了 {files.Count} 个音乐文件。");
|
||||
await _logger.InfoAsync($"已经扫描到了 {files.Count} 个音乐文件。");
|
||||
|
||||
return files;
|
||||
}
|
||||
@@ -118,7 +111,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.LogWarning($"已经存在歌词文件 {path},跳过。");
|
||||
_logger.WarnAsync($"已经存在歌词文件 {path},跳过。").GetAwaiter().GetResult();
|
||||
return false;
|
||||
})
|
||||
.ToList();
|
||||
@@ -126,7 +119,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
private async Task<ImmutableList<MusicInfo>> LoadMusicInfoAsync(IReadOnlyCollection<string> files)
|
||||
{
|
||||
_logger.LogInformation("开始加载音乐文件的标签信息...");
|
||||
await _logger.InfoAsync("开始加载音乐文件的标签信息...");
|
||||
|
||||
var warpTask = new WarpTask(ParallelNumber);
|
||||
var warpTaskList = files.Select(file => warpTask.RunAsync(() => Task.Run(async () => await _tagLoader.LoadTagAsync(file))));
|
||||
@@ -138,111 +131,16 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
// Load music total time info.
|
||||
// result.Foreach(m => { m.TotalTime = (long?)new AudioFileReader(m.FilePath).TotalTime.TotalMilliseconds; });
|
||||
|
||||
_logger.LogInformation($"已成功加载 {files.Count} 个音乐文件的标签信息。");
|
||||
await _logger.InfoAsync($"已成功加载 {files.Count} 个音乐文件的标签信息。");
|
||||
|
||||
return result.ToImmutableList();
|
||||
}
|
||||
|
||||
private IEnumerable<ILyricsProvider> GetLyricDownloaderList()
|
||||
{
|
||||
var downloader = _options.Provider.Lyric.Plugin
|
||||
.Where(op => op.Priority != -1)
|
||||
.OrderBy(op => op.Priority)
|
||||
.Join(_lyricDownloaderList,
|
||||
op => op.Name,
|
||||
loader => loader.DownloaderName,
|
||||
(op, loader) => loader);
|
||||
|
||||
return downloader;
|
||||
}
|
||||
|
||||
#region > Lyric download logic <
|
||||
|
||||
private async ValueTask DownloadLyricFilesAsync(ImmutableList<MusicInfo> musicInfos)
|
||||
{
|
||||
_logger.LogInformation("开始下载歌词文件数据...");
|
||||
|
||||
var downloaderList = GetLyricDownloaderList();
|
||||
var warpTask = new WarpTask(ParallelNumber);
|
||||
var warpTaskList = musicInfos.Select(info =>
|
||||
warpTask.RunAsync(() => Task.Run(async () => await DownloadLyricTaskLogicAsync(downloaderList, info))));
|
||||
|
||||
await Task.WhenAll(warpTaskList);
|
||||
|
||||
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
|
||||
}
|
||||
|
||||
private async Task DownloadLyricTaskLogicAsync(IEnumerable<ILyricsProvider> downloaderList, MusicInfo info)
|
||||
{
|
||||
async Task InternalDownloadLogicAsync(ILyricsProvider downloader)
|
||||
{
|
||||
try
|
||||
{
|
||||
var lyric = await downloader.DownloadAsync(info.Name, info.Artist, info.TotalTime);
|
||||
var lyricFilePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!,
|
||||
$"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");
|
||||
|
||||
if (File.Exists(lyricFilePath))
|
||||
{
|
||||
File.Delete(lyricFilePath);
|
||||
}
|
||||
|
||||
info.IsSuccessful = true;
|
||||
|
||||
if (lyric.IsPruneMusic)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var stream = new FileStream(lyricFilePath, FileMode.Create);
|
||||
await using var sw = new BinaryWriter(stream);
|
||||
|
||||
sw.Write(EncodingConvert(lyric));
|
||||
await stream.FlushAsync();
|
||||
}
|
||||
catch (ErrorCodeException ex)
|
||||
{
|
||||
info.IsSuccessful = ex.ErrorCode == ErrorCodes.NoMatchingSong;
|
||||
|
||||
_logger.LogWarningInfo(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。");
|
||||
info.IsSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var downloader in downloaderList)
|
||||
{
|
||||
await InternalDownloadLogicAsync(downloader);
|
||||
|
||||
if (info.IsSuccessful)
|
||||
{
|
||||
_logger.LogSuccessful(info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] EncodingConvert(LyricItemCollection lyric)
|
||||
{
|
||||
var supportEncodings = Encoding.GetEncodings();
|
||||
if (supportEncodings.All(x => x.Name != _options.Provider.Lyric.Config.FileEncoding))
|
||||
{
|
||||
throw new ErrorCodeException(ErrorCodes.NotSupportedFileEncoding);
|
||||
}
|
||||
|
||||
return Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(_options.Provider.Lyric.Config.FileEncoding), lyric.GetUtf8Bytes());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region > Ablum image download logic <
|
||||
|
||||
private async ValueTask DownloadAlbumAsync(ImmutableList<MusicInfo> musicInfos)
|
||||
{
|
||||
_logger.LogInformation("开始下载专辑图像数据...");
|
||||
await _logger.InfoAsync("开始下载专辑图像数据...");
|
||||
|
||||
var downloader = _albumDownloaderList.FirstOrDefault(d => d.DownloaderName == InternalAlbumDownloaderNames.NetEase);
|
||||
var warpTask = new WarpTask(ParallelNumber);
|
||||
@@ -251,7 +149,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
await Task.WhenAll(warpTaskList);
|
||||
|
||||
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
|
||||
await _logger.InfoAsync($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
|
||||
}
|
||||
|
||||
private async Task DownloadAlbumTaskLogicAsync(IAlbumDownloader downloader, MusicInfo info)
|
||||
|
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
|
||||
using ZonyLrcTools.Common.Infrastructure.Logging;
|
||||
|
||||
namespace ZonyLrcTools.Cli.Infrastructure.Logging;
|
||||
|
||||
public class SerilogWarpLogger : IWarpLogger, ITransientDependency
|
||||
{
|
||||
private readonly ILogger<SerilogWarpLogger> _logger;
|
||||
|
||||
public SerilogWarpLogger(ILogger<SerilogWarpLogger> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task DebugAsync(string message, Exception exception = null)
|
||||
{
|
||||
_logger.LogDebug(message, exception);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task InfoAsync(string message, Exception exception = null)
|
||||
{
|
||||
_logger.LogInformation(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task WarnAsync(string message, Exception exception = null)
|
||||
{
|
||||
_logger.LogWarning(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ErrorAsync(string message, Exception exception = null)
|
||||
{
|
||||
_logger.LogError(message, exception);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user