refactor: Refactor the code of the Download command.

This commit is contained in:
real-zony 2022-10-23 22:48:06 +08:00
parent 64d26cbc4c
commit 6b72f919b8
12 changed files with 151 additions and 127 deletions

View File

@ -1,42 +1,29 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils; using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Options;
using ZonyLrcTools.Common; using ZonyLrcTools.Common;
using ZonyLrcTools.Common.Album; using ZonyLrcTools.Common.Album;
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 ZonyLrcTools.Common.Lyrics;
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable MemberCanBePrivate.Global
namespace ZonyLrcTools.Cli.Commands.SubCommand namespace ZonyLrcTools.Cli.Commands.SubCommand
{ {
[Command("download", Description = "下载歌词文件或专辑图像。")] [Command("download", Description = "下载歌词文件或专辑图像。")]
public class DownloadCommand : ToolCommandBase public class DownloadCommand : ToolCommandBase
{ {
private readonly ILyricsDownloader _lyricsDownloader; private readonly ILyricsDownloader _lyricsDownloader;
private readonly IAlbumDownloader _albumDownloader;
private readonly IMusicInfoLoader _musicInfoLoader; private readonly IMusicInfoLoader _musicInfoLoader;
private readonly IEnumerable<IAlbumDownloader> _albumDownloaderList;
private readonly IWarpLogger _logger; public DownloadCommand(ILyricsDownloader lyricsDownloader,
private readonly GlobalOptions _options; IMusicInfoLoader musicInfoLoader,
IAlbumDownloader albumDownloader)
public DownloadCommand(IOptions<GlobalOptions> options,
IEnumerable<IAlbumDownloader> albumDownloaderList,
ILyricsDownloader lyricsDownloader,
IWarpLogger logger,
IMusicInfoLoader musicInfoLoader)
{ {
_albumDownloaderList = albumDownloaderList;
_lyricsDownloader = lyricsDownloader; _lyricsDownloader = lyricsDownloader;
_logger = logger;
_musicInfoLoader = musicInfoLoader; _musicInfoLoader = musicInfoLoader;
_options = options.Value; _albumDownloader = albumDownloader;
} }
#region > Options < #region > Options <
@ -65,72 +52,10 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
if (DownloadAlbum) if (DownloadAlbum)
{ {
// await DownloadAlbumAsync( await _albumDownloader.DownloadAsync(await _musicInfoLoader.LoadAsync(SongsDirectory, ParallelNumber), ParallelNumber);
// await LoadMusicInfoAsync(
// await ScanMusicFilesAsync()));
} }
return 0; return 0;
} }
private List<string> RemoveExistLyricFiles(List<string> filePaths)
{
if (!_options.Provider.Lyric.Config.IsSkipExistLyricFiles)
{
return filePaths;
}
return filePaths
.Where(path =>
{
if (!File.Exists(Path.ChangeExtension(path, ".lrc")))
{
return true;
}
_logger.WarnAsync($"已经存在歌词文件 {path},跳过。").GetAwaiter().GetResult();
return false;
})
.ToList();
}
#region > Ablum image download logic <
private async ValueTask DownloadAlbumAsync(List<MusicInfo> musicInfos)
{
await _logger.InfoAsync("开始下载专辑图像数据...");
var downloader = _albumDownloaderList.FirstOrDefault(d => d.DownloaderName == InternalAlbumDownloaderNames.NetEase);
var warpTask = new WarpTask(ParallelNumber);
var warpTaskList = musicInfos.Select(info =>
warpTask.RunAsync(() => Task.Run(async () => await DownloadAlbumTaskLogicAsync(downloader, info))));
await Task.WhenAll(warpTaskList);
await _logger.InfoAsync($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
}
private async Task DownloadAlbumTaskLogicAsync(IAlbumDownloader downloader, MusicInfo info)
{
_logger.LogSuccessful(info);
try
{
var album = await downloader.DownloadAsync(info.Name, info.Artist);
var filePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!, $"{Path.GetFileNameWithoutExtension(info.FilePath)}.png");
if (File.Exists(filePath) || album.Length <= 0)
{
return;
}
await new FileStream(filePath, FileMode.Create).WriteBytesToFileAsync(album, 1024);
}
catch (ErrorCodeException ex)
{
_logger.LogWarningInfo(ex);
}
}
#endregion
} }
} }

View File

@ -0,0 +1,68 @@
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
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;
namespace ZonyLrcTools.Common.Album;
public class AlbumDownloader : IAlbumDownloader, ISingletonDependency
{
private readonly IEnumerable<IAlbumProvider> _albumProviders;
public IEnumerable<IAlbumProvider> AvailableProviders => new Lazy<IEnumerable<IAlbumProvider>>(() =>
{
return _albumProviders.Where(d => d.DownloaderName == InternalAlbumProviderNames.NetEase);
}).Value;
private readonly IWarpLogger _logger;
public AlbumDownloader(IEnumerable<IAlbumProvider> albumProviders,
IWarpLogger logger)
{
_albumProviders = albumProviders;
_logger = logger;
}
public async Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
int parallelCount = 2,
CancellationToken cancellationToken = default)
{
await _logger.InfoAsync("开始下载专辑图像数据...");
var provider = AvailableProviders.FirstOrDefault(d => d.DownloaderName == InternalAlbumProviderNames.NetEase);
if (provider == null)
{
return;
}
var warpTask = new WarpTask(parallelCount);
var warpTaskList = needDownloadMusicInfos.Select(info =>
warpTask.RunAsync(() =>
Task.Run(async () =>
{
_logger.LogSuccessful(info);
try
{
var album = await provider.DownloadAsync(info.Name, info.Artist);
var filePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!, $"{Path.GetFileNameWithoutExtension(info.FilePath)}.png");
if (File.Exists(filePath) || album.Length <= 0)
{
return;
}
await new FileStream(filePath, FileMode.Create).WriteBytesToFileAsync(album);
}
catch (ErrorCodeException ex)
{
_logger.LogWarningInfo(ex);
}
}, cancellationToken), cancellationToken));
await Task.WhenAll(warpTaskList);
await _logger.InfoAsync($"专辑数据下载完成,成功: {needDownloadMusicInfos.Count(m => m.IsSuccessful)} 失败{needDownloadMusicInfos.Count(m => m.IsSuccessful == false)}。");
}
}

View File

@ -1,21 +1,10 @@
namespace ZonyLrcTools.Common.Album namespace ZonyLrcTools.Common.Album;
{
/// <summary>
/// 专辑封面下载器,用于匹配并下载歌曲的专辑封面。
/// </summary>
public interface IAlbumDownloader public interface IAlbumDownloader
{ {
/// <summary> Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
/// 下载器的名称。 int parallelCount = 2,
/// </summary> CancellationToken cancellationToken = default);
string DownloaderName { get; }
/// <summary> IEnumerable<IAlbumProvider> AvailableProviders { get; }
/// 下载专辑封面。
/// </summary>
/// <param name="songName">歌曲的名称。</param>
/// <param name="artist">歌曲的作者。</param>
/// <returns>专辑封面的图像数据。</returns>
ValueTask<byte[]> DownloadAsync(string songName, string artist);
}
} }

View File

@ -0,0 +1,21 @@
namespace ZonyLrcTools.Common.Album
{
/// <summary>
/// 专辑封面下载器,用于匹配并下载歌曲的专辑封面。
/// </summary>
public interface IAlbumProvider
{
/// <summary>
/// 下载器的名称。
/// </summary>
string DownloaderName { get; }
/// <summary>
/// 下载专辑封面。
/// </summary>
/// <param name="songName">歌曲的名称。</param>
/// <param name="artist">歌曲的作者。</param>
/// <returns>专辑封面的图像数据。</returns>
ValueTask<byte[]> DownloadAsync(string songName, string artist);
}
}

View File

@ -3,7 +3,7 @@
/// <summary> /// <summary>
/// 定义了程序默认提供的专辑图像下载器。 /// 定义了程序默认提供的专辑图像下载器。
/// </summary> /// </summary>
public static class InternalAlbumDownloaderNames public static class InternalAlbumProviderNames
{ {
/// <summary> /// <summary>
/// 网易云音乐专辑图像下载器。 /// 网易云音乐专辑图像下载器。

View File

@ -7,9 +7,9 @@ using ZonyLrcTools.Common.Lyrics.Providers.NetEase.JsonModel;
namespace ZonyLrcTools.Common.Album.NetEase namespace ZonyLrcTools.Common.Album.NetEase
{ {
public class NetEaseAlbumDownloader : IAlbumDownloader, ITransientDependency public class NetEaseAlbumProvider : IAlbumProvider, ITransientDependency
{ {
public string DownloaderName => InternalAlbumDownloaderNames.NetEase; public string DownloaderName => InternalAlbumProviderNames.NetEase;
private readonly IWarpHttpClient _warpHttpClient; private readonly IWarpHttpClient _warpHttpClient;
private readonly Action<HttpRequestMessage> _defaultOption; private readonly Action<HttpRequestMessage> _defaultOption;
@ -18,7 +18,7 @@ namespace ZonyLrcTools.Common.Album.NetEase
private const string GetMusicInfoApi = @"https://music.163.com/api/song/detail"; private const string GetMusicInfoApi = @"https://music.163.com/api/song/detail";
private const string DefaultReferer = @"https://music.163.com"; private const string DefaultReferer = @"https://music.163.com";
public NetEaseAlbumDownloader(IWarpHttpClient warpHttpClient) public NetEaseAlbumProvider(IWarpHttpClient warpHttpClient)
{ {
_warpHttpClient = warpHttpClient; _warpHttpClient = warpHttpClient;
_defaultOption = message => _defaultOption = message =>

View File

@ -5,9 +5,9 @@ using ZonyLrcTools.Common.Lyrics.Providers.QQMusic.JsonModel;
namespace ZonyLrcTools.Common.Album.QQMusic namespace ZonyLrcTools.Common.Album.QQMusic
{ {
public class QQMusicAlbumDownloader : IAlbumDownloader, ITransientDependency public class QQMusicAlbumProvider : IAlbumProvider, ITransientDependency
{ {
public string DownloaderName => InternalAlbumDownloaderNames.QQ; public string DownloaderName => InternalAlbumProviderNames.QQ;
private readonly IWarpHttpClient _warpHttpClient; private readonly IWarpHttpClient _warpHttpClient;
@ -24,7 +24,7 @@ namespace ZonyLrcTools.Common.Album.QQMusic
private const string SearchApi = "https://c.y.qq.com/soso/fcgi-bin/client_search_cp"; private const string SearchApi = "https://c.y.qq.com/soso/fcgi-bin/client_search_cp";
private const string DefaultReferer = "https://y.qq.com"; private const string DefaultReferer = "https://y.qq.com";
public QQMusicAlbumDownloader(IWarpHttpClient warpHttpClient) public QQMusicAlbumProvider(IWarpHttpClient warpHttpClient)
{ {
_warpHttpClient = warpHttpClient; _warpHttpClient = warpHttpClient;
} }

View File

@ -2,7 +2,7 @@
public interface ILyricsDownloader public interface ILyricsDownloader
{ {
Task<List<MusicInfo>> DownloadAsync(List<MusicInfo> needDownloadMusicInfos, Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
int parallelCount = 2, int parallelCount = 2,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);

View File

@ -9,33 +9,33 @@ using ZonyLrcTools.Common.Infrastructure.Threading;
namespace ZonyLrcTools.Common.Lyrics; namespace ZonyLrcTools.Common.Lyrics;
public class DefaultLyricsDownloader : ILyricsDownloader, ISingletonDependency public class LyricsDownloader : ILyricsDownloader, ISingletonDependency
{ {
private readonly IEnumerable<ILyricsProvider> _lyricDownloaderList; private readonly IEnumerable<ILyricsProvider> _lyricsProviders;
private readonly GlobalOptions _options;
private readonly IWarpLogger _logger; private readonly IWarpLogger _logger;
private readonly GlobalOptions _options;
public IEnumerable<ILyricsProvider> AvailableProviders => new Lazy<IEnumerable<ILyricsProvider>>(() => public IEnumerable<ILyricsProvider> AvailableProviders => new Lazy<IEnumerable<ILyricsProvider>>(() =>
{ {
return _options.Provider.Lyric.Plugin return _options.Provider.Lyric.Plugin
.Where(op => op.Priority != -1) .Where(op => op.Priority != -1)
.OrderBy(op => op.Priority) .OrderBy(op => op.Priority)
.Join(_lyricDownloaderList, .Join(_lyricsProviders,
op => op.Name, op => op.Name,
loader => loader.DownloaderName, loader => loader.DownloaderName,
(op, loader) => loader); (_, loader) => loader);
}).Value; }).Value;
public DefaultLyricsDownloader(IEnumerable<ILyricsProvider> lyricDownloaderList, public LyricsDownloader(IEnumerable<ILyricsProvider> lyricsProviders,
IOptions<GlobalOptions> options, IOptions<GlobalOptions> options,
IWarpLogger logger) IWarpLogger logger)
{ {
_lyricDownloaderList = lyricDownloaderList; _lyricsProviders = lyricsProviders;
_logger = logger; _logger = logger;
_options = options.Value; _options = options.Value;
} }
public async Task<List<MusicInfo>> DownloadAsync(List<MusicInfo> needDownloadMusicInfos, public async Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
int parallelCount = 2, int parallelCount = 2,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
@ -67,7 +67,6 @@ public class DefaultLyricsDownloader : ILyricsDownloader, ISingletonDependency
await Task.WhenAll(downloadTasks); await Task.WhenAll(downloadTasks);
await _logger.InfoAsync($"歌词数据下载完成,成功: {needDownloadMusicInfos.Count(m => m.IsSuccessful)} 失败{needDownloadMusicInfos.Count(m => m.IsSuccessful == false)}。"); await _logger.InfoAsync($"歌词数据下载完成,成功: {needDownloadMusicInfos.Count(m => m.IsSuccessful)} 失败{needDownloadMusicInfos.Count(m => m.IsSuccessful == false)}。");
return needDownloadMusicInfos;
} }
private async Task DownloadAndWriteLyricsAsync(ILyricsProvider provider, MusicInfo info) private async Task DownloadAndWriteLyricsAsync(ILyricsProvider provider, MusicInfo info)

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using ZonyLrcTools.Common.Configuration; using ZonyLrcTools.Common.Configuration;
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
using ZonyLrcTools.Common.Infrastructure.Exceptions; using ZonyLrcTools.Common.Infrastructure.Exceptions;
using ZonyLrcTools.Common.Infrastructure.IO; using ZonyLrcTools.Common.Infrastructure.IO;
using ZonyLrcTools.Common.Infrastructure.Logging; using ZonyLrcTools.Common.Infrastructure.Logging;
@ -8,7 +9,7 @@ using ZonyLrcTools.Common.TagInfo;
namespace ZonyLrcTools.Common; namespace ZonyLrcTools.Common;
public class MusicInfoLoader : IMusicInfoLoader public class MusicInfoLoader : IMusicInfoLoader, ITransientDependency
{ {
private readonly IWarpLogger _logger; private readonly IWarpLogger _logger;
private readonly IFileScanner _fileScanner; private readonly IFileScanner _fileScanner;
@ -30,7 +31,7 @@ public class MusicInfoLoader : IMusicInfoLoader
int parallelCount = 2, int parallelCount = 2,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var files = (await _fileScanner.ScanMusicFilesAsync(dirPath, _options.SupportFileExtensions)).ToList(); var files = RemoveExistLyricFiles(await _fileScanner.ScanMusicFilesAsync(dirPath, _options.SupportFileExtensions));
if (files.Count == 0) if (files.Count == 0)
{ {
@ -66,4 +67,25 @@ public class MusicInfoLoader : IMusicInfoLoader
return result; return result;
} }
private List<string> RemoveExistLyricFiles(IEnumerable<string> filePaths)
{
if (!_options.Provider.Lyric.Config.IsSkipExistLyricFiles)
{
return filePaths.ToList();
}
return filePaths
.Where(path =>
{
if (!File.Exists(Path.ChangeExtension(path, ".lrc")))
{
return true;
}
_logger.WarnAsync($"已经存在歌词文件 {path},跳过。").GetAwaiter().GetResult();
return false;
})
.ToList();
}
} }

View File

@ -14,8 +14,8 @@ namespace ZonyLrcTools.Tests.Infrastructure.Album
[Fact] [Fact]
public async Task DownloadDataAsync_Test() public async Task DownloadDataAsync_Test()
{ {
var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumDownloader>>() var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumProvider>>()
.FirstOrDefault(x => x.DownloaderName == InternalAlbumDownloaderNames.NetEase); .FirstOrDefault(x => x.DownloaderName == InternalAlbumProviderNames.NetEase);
downloader.ShouldNotBeNull(); downloader.ShouldNotBeNull();
var albumBytes = await downloader.DownloadAsync("东方红", null); var albumBytes = await downloader.DownloadAsync("东方红", null);

View File

@ -12,8 +12,8 @@ namespace ZonyLrcTools.Tests.Infrastructure.Album
{ {
public async Task DownloadDataAsync_Test() public async Task DownloadDataAsync_Test()
{ {
var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumDownloader>>() var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumProvider>>()
.FirstOrDefault(x => x.DownloaderName == InternalAlbumDownloaderNames.QQ); .FirstOrDefault(x => x.DownloaderName == InternalAlbumProviderNames.QQ);
downloader.ShouldNotBeNull(); downloader.ShouldNotBeNull();
var albumBytes = await downloader.DownloadAsync("东方红", null); var albumBytes = await downloader.DownloadAsync("东方红", null);