mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-07-01 20:30:41 +00:00
refactor: Refactor the code of the Download command.
This commit is contained in:
parent
64d26cbc4c
commit
6b72f919b8
@ -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
|
||||
}
|
||||
}
|
68
src/ZonyLrcTools.Common/Album/AlbumDownloader.cs
Normal file
68
src/ZonyLrcTools.Common/Album/AlbumDownloader.cs
Normal 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)}。");
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
21
src/ZonyLrcTools.Common/Album/IAlbumProvider.cs
Normal file
21
src/ZonyLrcTools.Common/Album/IAlbumProvider.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// 定义了程序默认提供的专辑图像下载器。
|
||||
/// </summary>
|
||||
public static class InternalAlbumDownloaderNames
|
||||
public static class InternalAlbumProviderNames
|
||||
{
|
||||
/// <summary>
|
||||
/// 网易云音乐专辑图像下载器。
|
@ -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 =>
|
@ -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;
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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)
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user