feat: Reinitialize the Repository.

重新初始化仓库。
This commit is contained in:
Zony
2021-05-07 10:26:26 +08:00
parent a754f419b2
commit 8e1e61764c
78 changed files with 3329 additions and 22 deletions

View File

@@ -0,0 +1,196 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ZonyLrcTools.Cli.Config;
using ZonyLrcTools.Cli.Infrastructure;
using ZonyLrcTools.Cli.Infrastructure.Album;
using ZonyLrcTools.Cli.Infrastructure.Exceptions;
using ZonyLrcTools.Cli.Infrastructure.Extensions;
using ZonyLrcTools.Cli.Infrastructure.IO;
using ZonyLrcTools.Cli.Infrastructure.Lyric;
using ZonyLrcTools.Cli.Infrastructure.Tag;
using ZonyLrcTools.Cli.Infrastructure.Threading;
namespace ZonyLrcTools.Cli.Commands
{
[Command("download", Description = "下载歌词文件或专辑图像。")]
public class DownloadCommand : ToolCommandBase
{
private readonly ILogger<DownloadCommand> _logger;
private readonly IFileScanner _fileScanner;
private readonly ITagLoader _tagLoader;
private readonly IEnumerable<ILyricDownloader> _lyricDownloaderList;
private readonly IEnumerable<IAlbumDownloader> _albumDownloaderList;
private readonly ToolOptions _options;
public DownloadCommand(ILogger<DownloadCommand> logger,
IFileScanner fileScanner,
IOptions<ToolOptions> options,
ITagLoader tagLoader,
IEnumerable<ILyricDownloader> lyricDownloaderList,
IEnumerable<IAlbumDownloader> albumDownloaderList)
{
_logger = logger;
_fileScanner = fileScanner;
_tagLoader = tagLoader;
_lyricDownloaderList = lyricDownloaderList;
_albumDownloaderList = albumDownloaderList;
_options = options.Value;
}
#region > Options <
[Option("-d|--dir", CommandOptionType.SingleValue, Description = "指定需要扫描的目录。")]
[DirectoryExists]
public string Directory { get; set; }
[Option("-l|--lyric", CommandOptionType.NoValue, Description = "指定程序需要下载歌词文件。")]
public bool DownloadLyric { get; set; }
[Option("-a|--album", CommandOptionType.NoValue, Description = "指定程序需要下载专辑图像。")]
public bool DownloadAlbum { get; set; }
[Option("-n|--number", CommandOptionType.SingleValue, Description = "指定下载时候的线程数量。(默认值 2)")]
public int ParallelNumber { get; set; } = 2;
#endregion
public override List<string> CreateArgs() => new();
protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
{
var files = await ScanMusicFilesAsync();
var musicInfos = await LoadMusicInfoAsync(files);
if (DownloadLyric)
{
await DownloadLyricFilesAsync(musicInfos);
}
if (DownloadAlbum)
{
await DownloadAlbumAsync(musicInfos);
}
return 0;
}
private async Task<List<string>> ScanMusicFilesAsync()
{
var files = (await _fileScanner.ScanAsync(Directory, _options.SupportFileExtensions.Split(';')))
.SelectMany(t => t.FilePaths)
.ToList();
if (files.Count == 0)
{
_logger.LogError("没有找到任何音乐文件。");
throw new ErrorCodeException(ErrorCodes.NoFilesWereScanned);
}
_logger.LogInformation($"已经扫描到了 {files.Count} 个音乐文件。");
return files;
}
private async Task<ImmutableList<MusicInfo>> LoadMusicInfoAsync(IReadOnlyCollection<string> files)
{
_logger.LogInformation("开始加载音乐文件的标签信息...");
var warpTask = new WarpTask(ParallelNumber);
var warpTaskList = files.Select(file => warpTask.RunAsync(() => Task.Run(async () => await _tagLoader.LoadTagAsync(file))));
var result = await Task.WhenAll(warpTaskList);
_logger.LogInformation($"已成功加载 {files.Count} 个音乐文件的标签信息。");
return result.ToImmutableList();
}
#region > <
private async ValueTask DownloadLyricFilesAsync(ImmutableList<MusicInfo> musicInfos)
{
_logger.LogInformation("开始下载歌词文件数据...");
var downloader = _lyricDownloaderList.FirstOrDefault(d => d.DownloaderName == InternalLyricDownloaderNames.NetEase);
var warpTask = new WarpTask(ParallelNumber);
var warpTaskList = musicInfos.Select(info =>
warpTask.RunAsync(() => Task.Run(async () => await DownloadLyricTaskLogicAsync(downloader, info))));
await Task.WhenAll(warpTaskList);
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count} 失败{0}。");
}
private async Task DownloadLyricTaskLogicAsync(ILyricDownloader downloader, MusicInfo info)
{
try
{
var lyric = await downloader.DownloadAsync(info.Name, info.Artist);
var filePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!, $"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");
if (File.Exists(filePath))
{
return;
}
if (lyric.IsPruneMusic)
{
return;
}
await using var stream = new FileStream(filePath, FileMode.Create);
await using var sw = new StreamWriter(stream);
await sw.WriteAsync(lyric.ToString());
await sw.FlushAsync();
}
catch (ErrorCodeException ex)
{
_logger.LogWarningInfo(ex);
}
}
#endregion
#region > <
private async ValueTask DownloadAlbumAsync(ImmutableList<MusicInfo> musicInfos)
{
_logger.LogInformation("开始下载专辑图像数据...");
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);
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count} 失败{0}。");
}
private async Task DownloadAlbumTaskLogicAsync(IAlbumDownloader downloader, MusicInfo 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,47 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ZonyLrcTools.Cli.Config;
using ZonyLrcTools.Cli.Infrastructure.IO;
namespace ZonyLrcTools.Cli.Commands
{
[Command("scan", Description = "扫描指定目录下符合条件的音乐文件数量。")]
public class ScanCommand : ToolCommandBase
{
private readonly IFileScanner _fileScanner;
private readonly ToolOptions _options;
private readonly ILogger<ScanCommand> _logger;
public ScanCommand(IFileScanner fileScanner,
IOptions<ToolOptions> options,
ILogger<ScanCommand> logger)
{
_fileScanner = fileScanner;
_logger = logger;
_options = options.Value;
}
[Option("-d|--dir", CommandOptionType.SingleValue, Description = "指定需要扫描的目录。")]
[DirectoryExists]
public string DirectoryPath { get; set; }
protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
{
var result = await _fileScanner.ScanAsync(
DirectoryPath,
_options.SupportFileExtensions.Split(';'));
_logger.LogInformation($"目录扫描完成,共扫描到 {result.Sum(f => f.FilePaths.Count)} 个音乐文件。");
return 0;
}
public override List<string> CreateArgs()
{
return new();
}
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using ZonyLrcTools.Cli.Infrastructure.DependencyInject;
using ZonyLrcTools.Cli.Infrastructure.Exceptions;
using ZonyLrcTools.Cli.Infrastructure.Extensions;
namespace ZonyLrcTools.Cli.Commands
{
[Command("lyric-tool")]
[Subcommand(typeof(ScanCommand), typeof(DownloadCommand))]
public class ToolCommand : ToolCommandBase
{
public override List<string> CreateArgs()
{
return new();
}
public static async Task<int> Main(string[] args)
{
ConfigureLogger();
ConfigureErrorMessage();
try
{
return await BuildHostedService(args);
}
catch (Exception ex)
{
return HandleException(ex);
}
finally
{
Log.CloseAndFlush();
}
}
#region > <
private static void ConfigureErrorMessage() => ErrorCodeHelper.LoadErrorMessage();
private static void ConfigureLogger()
{
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("System.Net.Http.HttpClient", LogEventLevel.Error)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.Console())
.WriteTo.Logger(lc =>
{
lc.Filter.ByIncludingOnly(lc => lc.Level == LogEventLevel.Warning)
.WriteTo.Async(c => c.File("Logs/warnings.txt"));
})
.WriteTo.Logger(lc =>
{
lc.Filter.ByIncludingOnly(lc => lc.Level == LogEventLevel.Error)
.WriteTo.Async(c => c.File("Logs/errors.txt"));
})
.CreateLogger();
}
private static Task<int> BuildHostedService(string[] args)
{
return new HostBuilder()
.ConfigureLogging(builder => builder.AddSerilog())
.ConfigureHostConfiguration(builder =>
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
})
.ConfigureServices((_, services) =>
{
services.AddSingleton(PhysicalConsole.Singleton);
services.BeginAutoDependencyInject<ToolCommand>();
services.ConfigureConfiguration();
services.ConfigureToolService();
})
.RunCommandLineApplicationAsync<ToolCommand>(args);
}
private static int HandleException(Exception ex)
{
switch (ex)
{
case ErrorCodeException exception:
Log.Logger.Error($"出现了未处理的异常,错误代码: {exception.ErrorCode},错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n调用栈:\n{exception.StackTrace}");
Environment.Exit(exception.ErrorCode);
return exception.ErrorCode;
case Exception unknownException:
Log.Logger.Error($"出现了未处理的异常: {unknownException.Message}\n调用栈:\n{unknownException.StackTrace}");
Environment.Exit(-1);
return 1;
default:
return 1;
}
}
#endregion
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
namespace ZonyLrcTools.Cli.Commands
{
[HelpOption("--help|-h", Description = "欢迎使用 ZonyLrcToolsX Command Line Interface。")]
public abstract class ToolCommandBase
{
public abstract List<string> CreateArgs();
protected virtual Task<int> OnExecuteAsync(CommandLineApplication app)
{
return Task.FromResult(0);
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
namespace ZonyLrcTools.Cli.Commands
{
/// <summary>
/// 工具类相关命令。
/// </summary>
[Command("util", Description = "提供常用的工具类功能。")]
public class UtilityCommand : ToolCommandBase
{
public override List<string> CreateArgs() => new();
protected override Task<int> OnExecuteAsync(CommandLineApplication app)
{
return base.OnExecuteAsync(app);
}
}
}