refactor: Refactor lyrics download logic and abstract a new logger.

This commit is contained in:
real-zony
2022-10-23 13:38:51 +08:00
parent 3e27e18098
commit 7d17fc0b97
18 changed files with 245 additions and 163 deletions

View File

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