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 McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Options;
using ZonyLrcTools.Common;
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;
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable MemberCanBePrivate.Global
namespace ZonyLrcTools.Cli.Commands.SubCommand
{
[Command("download", Description = "下载歌词文件或专辑图像。")]
public class DownloadCommand : ToolCommandBase
{
private readonly ILyricsDownloader _lyricsDownloader;
private readonly IAlbumDownloader _albumDownloader;
private readonly IMusicInfoLoader _musicInfoLoader;
private readonly IEnumerable<IAlbumDownloader> _albumDownloaderList;
private readonly IWarpLogger _logger;
private readonly GlobalOptions _options;
public DownloadCommand(IOptions<GlobalOptions> options,
IEnumerable<IAlbumDownloader> albumDownloaderList,
ILyricsDownloader lyricsDownloader,
IWarpLogger logger,
IMusicInfoLoader musicInfoLoader)
public DownloadCommand(ILyricsDownloader lyricsDownloader,
IMusicInfoLoader musicInfoLoader,
IAlbumDownloader albumDownloader)
{
_albumDownloaderList = albumDownloaderList;
_lyricsDownloader = lyricsDownloader;
_logger = logger;
_musicInfoLoader = musicInfoLoader;
_options = options.Value;
_albumDownloader = albumDownloader;
}
#region > Options <
@ -65,72 +52,10 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
if (DownloadAlbum)
{
// await DownloadAlbumAsync(
// await LoadMusicInfoAsync(
// await ScanMusicFilesAsync()));
await _albumDownloader.DownloadAsync(await _musicInfoLoader.LoadAsync(SongsDirectory, ParallelNumber), ParallelNumber);
}
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
{
/// <summary>
/// 专辑封面下载器,用于匹配并下载歌曲的专辑封面。
/// </summary>
public interface IAlbumDownloader
{
/// <summary>
/// 下载器的名称。
/// </summary>
string DownloaderName { get; }
namespace ZonyLrcTools.Common.Album;
/// <summary>
/// 下载专辑封面。
/// </summary>
/// <param name="songName">歌曲的名称。</param>
/// <param name="artist">歌曲的作者。</param>
/// <returns>专辑封面的图像数据。</returns>
ValueTask<byte[]> DownloadAsync(string songName, string artist);
}
public interface IAlbumDownloader
{
Task DownloadAsync(List<MusicInfo> needDownloadMusicInfos,
int parallelCount = 2,
CancellationToken cancellationToken = default);
IEnumerable<IAlbumProvider> AvailableProviders { get; }
}

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>
public static class InternalAlbumDownloaderNames
public static class InternalAlbumProviderNames
{
/// <summary>
/// 网易云音乐专辑图像下载器。

View File

@ -7,9 +7,9 @@ using ZonyLrcTools.Common.Lyrics.Providers.NetEase.JsonModel;
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 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 DefaultReferer = @"https://music.163.com";
public NetEaseAlbumDownloader(IWarpHttpClient warpHttpClient)
public NetEaseAlbumProvider(IWarpHttpClient warpHttpClient)
{
_warpHttpClient = warpHttpClient;
_defaultOption = message =>

View File

@ -5,9 +5,9 @@ using ZonyLrcTools.Common.Lyrics.Providers.QQMusic.JsonModel;
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;
@ -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 DefaultReferer = "https://y.qq.com";
public QQMusicAlbumDownloader(IWarpHttpClient warpHttpClient)
public QQMusicAlbumProvider(IWarpHttpClient warpHttpClient)
{
_warpHttpClient = warpHttpClient;
}

View File

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

View File

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

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.Options;
using ZonyLrcTools.Common.Configuration;
using ZonyLrcTools.Common.Infrastructure.DependencyInject;
using ZonyLrcTools.Common.Infrastructure.Exceptions;
using ZonyLrcTools.Common.Infrastructure.IO;
using ZonyLrcTools.Common.Infrastructure.Logging;
@ -8,7 +9,7 @@ using ZonyLrcTools.Common.TagInfo;
namespace ZonyLrcTools.Common;
public class MusicInfoLoader : IMusicInfoLoader
public class MusicInfoLoader : IMusicInfoLoader, ITransientDependency
{
private readonly IWarpLogger _logger;
private readonly IFileScanner _fileScanner;
@ -30,7 +31,7 @@ public class MusicInfoLoader : IMusicInfoLoader
int parallelCount = 2,
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)
{
@ -66,4 +67,25 @@ public class MusicInfoLoader : IMusicInfoLoader
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]
public async Task DownloadDataAsync_Test()
{
var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumDownloader>>()
.FirstOrDefault(x => x.DownloaderName == InternalAlbumDownloaderNames.NetEase);
var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumProvider>>()
.FirstOrDefault(x => x.DownloaderName == InternalAlbumProviderNames.NetEase);
downloader.ShouldNotBeNull();
var albumBytes = await downloader.DownloadAsync("东方红", null);

View File

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