mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-07-01 20:30:41 +00:00
feat: Added support for reading song information from CSV files and NetEase Cloud Music playlists.
This commit is contained in:
parent
0e5e48cd00
commit
fb1f743365
@ -1,8 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using McMaster.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ZonyLrcTools.Cli.Infrastructure.MusicScannerOptions;
|
||||
using ZonyLrcTools.Common;
|
||||
using ZonyLrcTools.Common.Album;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
using ZonyLrcTools.Common.MusicScanner;
|
||||
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
@ -15,14 +21,17 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
private readonly ILyricsDownloader _lyricsDownloader;
|
||||
private readonly IAlbumDownloader _albumDownloader;
|
||||
private readonly IMusicInfoLoader _musicInfoLoader;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public DownloadCommand(ILyricsDownloader lyricsDownloader,
|
||||
IMusicInfoLoader musicInfoLoader,
|
||||
IAlbumDownloader albumDownloader)
|
||||
IAlbumDownloader albumDownloader,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
_lyricsDownloader = lyricsDownloader;
|
||||
_musicInfoLoader = musicInfoLoader;
|
||||
_albumDownloader = albumDownloader;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
#region > Options <
|
||||
@ -42,19 +51,82 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
||||
#endregion
|
||||
|
||||
#region > Scanner Options <
|
||||
|
||||
[Option("-sc|--scanner", CommandOptionType.SingleValue, Description = "指定歌词文件扫描器,目前支持本地文件(local),网易云音乐(netease),csv 文件(csv),默认值为 local。")]
|
||||
public string Scanner { get; set; } = "local";
|
||||
|
||||
[Option("-o|--output", Description = "指定歌词文件的输出路径。")]
|
||||
public string OutputDirectory { get; set; } = "DownloadedLrc";
|
||||
|
||||
[Option("-p|--pattern", Description = "指定歌词文件的输出文件名模式。")]
|
||||
public string OutputFileNamePattern { get; set; } = "{Artist} - {Name}.lrc";
|
||||
|
||||
[Option("-f|--file", Description = "指定 CSV 文件的路径。")]
|
||||
public string CsvFilePath { get; set; }
|
||||
|
||||
[Option("-s|--song-list-id", Description = "指定网易云音乐歌单的 ID。")]
|
||||
public string SongListId { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
|
||||
{
|
||||
if (DownloadLyric)
|
||||
{
|
||||
await _lyricsDownloader.DownloadAsync(await _musicInfoLoader.LoadAsync(SongsDirectory, ParallelNumber), ParallelNumber);
|
||||
await _lyricsDownloader.DownloadAsync(await GetMusicInfosAsync(Scanner), ParallelNumber);
|
||||
}
|
||||
|
||||
if (DownloadAlbum)
|
||||
{
|
||||
await _albumDownloader.DownloadAsync(await _musicInfoLoader.LoadAsync(SongsDirectory, ParallelNumber), ParallelNumber);
|
||||
await _albumDownloader.DownloadAsync(await GetMusicInfosAsync(Scanner), ParallelNumber);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the music infos by the scanner.
|
||||
/// </summary>
|
||||
private async Task<List<MusicInfo>> GetMusicInfosAsync(string scanner)
|
||||
{
|
||||
ValidateScannerOptions(scanner);
|
||||
|
||||
return scanner switch
|
||||
{
|
||||
MusicScannerConsts.LocalScanner => await _musicInfoLoader.LoadAsync(SongsDirectory, ParallelNumber),
|
||||
MusicScannerConsts.CsvScanner => await _serviceProvider.GetService<CsvFileMusicScanner>()
|
||||
.GetMusicInfoFromCsvFileAsync(CsvFilePath, OutputDirectory, OutputFileNamePattern),
|
||||
MusicScannerConsts.NeteaseScanner => await _serviceProvider.GetService<NetEaseMusicSongListMusicScanner>()
|
||||
.GetMusicInfoFromNetEaseMusicSongListAsync(SongListId, OutputDirectory, OutputFileNamePattern),
|
||||
_ => await _musicInfoLoader.LoadAsync(SongsDirectory, ParallelNumber)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually validate the options.
|
||||
/// </summary>
|
||||
/// <param name="scanner">Scanner Name.</param>
|
||||
/// <exception cref="ArgumentException">If the options are invalid.</exception>
|
||||
private void ValidateScannerOptions(string scanner)
|
||||
{
|
||||
if (scanner != MusicScannerConsts.LocalScanner && string.IsNullOrEmpty(OutputDirectory))
|
||||
{
|
||||
throw new ArgumentException("当使用非本地文件扫描器时,必须指定歌词文件的输出路径。");
|
||||
}
|
||||
|
||||
if (scanner != MusicScannerConsts.LocalScanner && !Directory.Exists(OutputDirectory))
|
||||
{
|
||||
throw new ArgumentException("指定的歌词文件输出路径不存在。");
|
||||
}
|
||||
|
||||
switch (scanner)
|
||||
{
|
||||
case MusicScannerConsts.CsvScanner when string.IsNullOrWhiteSpace(CsvFilePath):
|
||||
throw new ArgumentException("当使用 CSV 文件扫描器时,必须指定 CSV 文件的路径。");
|
||||
case MusicScannerConsts.NeteaseScanner when string.IsNullOrWhiteSpace(SongListId):
|
||||
throw new ArgumentException("当使用网易云音乐扫描器时,必须指定歌单的 ID。");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
// ReSharper disable IdentifierTypo
|
||||
|
||||
namespace ZonyLrcTools.Cli.Infrastructure.MusicScannerOptions;
|
||||
|
||||
public class MusicScannerConsts
|
||||
{
|
||||
public const string LocalScanner = "local";
|
||||
public const string NeteaseScanner = "netease";
|
||||
public const string CsvScanner = "csv";
|
||||
}
|
@ -8,7 +8,6 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Sinks.SystemConsole.Themes;
|
||||
using ZonyLrcTools.Cli.Commands;
|
||||
using ZonyLrcTools.Cli.Commands.SubCommand;
|
||||
using ZonyLrcTools.Cli.Infrastructure;
|
||||
@ -33,6 +32,7 @@ namespace ZonyLrcTools.Cli
|
||||
|
||||
try
|
||||
{
|
||||
Log.Logger.Information("Starting...");
|
||||
return await BuildHostedService(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -4,19 +4,19 @@ namespace ZonyLrcTools.Common.MusicScanner;
|
||||
|
||||
public class CsvFileMusicScanner : ITransientDependency
|
||||
{
|
||||
public async Task<List<MusicInfo>> GetMusicInfoFromCsvFileAsync(string csvFilePath, ManualDownloadOptions options)
|
||||
public async Task<List<MusicInfo>> GetMusicInfoFromCsvFileAsync(string csvFilePath, string outputDirectory, string pattern)
|
||||
{
|
||||
var csvFileContent = await File.ReadAllTextAsync(csvFilePath);
|
||||
var csvFileLines = csvFileContent.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return csvFileLines.Skip(1).Select(line => GetMusicInfoFromCsvFileLine(line, options)).ToList();
|
||||
return csvFileLines.Skip(1).Select(line => GetMusicInfoFromCsvFileLine(line, outputDirectory, pattern)).ToList();
|
||||
}
|
||||
|
||||
private MusicInfo GetMusicInfoFromCsvFileLine(string csvFileLine, ManualDownloadOptions options)
|
||||
private MusicInfo GetMusicInfoFromCsvFileLine(string csvFileLine, string outputDirectory, string pattern)
|
||||
{
|
||||
var csvFileLineItems = csvFileLine.Split(',');
|
||||
var name = csvFileLineItems[0];
|
||||
var artist = csvFileLineItems[1];
|
||||
var fakeFilePath = Path.Combine(options.OutputDirectory, options.OutputFileNamePattern.Replace("{Name}", name).Replace("{Artist}", artist));
|
||||
var fakeFilePath = Path.Combine(outputDirectory, pattern.Replace("{Name}", name).Replace("{Artist}", artist));
|
||||
var musicInfo = new MusicInfo(fakeFilePath, name, artist);
|
||||
return musicInfo;
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
namespace ZonyLrcTools.Common.MusicScanner;
|
||||
|
||||
public class ManualDownloadOptions
|
||||
{
|
||||
public string OutputFileNamePattern { get; set; } = "{Artist} - {Name}.lrc";
|
||||
public string OutputDirectory { get; set; } = "DownloadedLrc";
|
||||
}
|
@ -17,7 +17,7 @@ public class NetEaseMusicSongListMusicScanner : ITransientDependency
|
||||
_warpHttpClient = warpHttpClient;
|
||||
}
|
||||
|
||||
public async Task<List<MusicInfo>> GetMusicInfoFromNetEaseMusicSongListAsync(string songListId, ManualDownloadOptions options)
|
||||
public async Task<List<MusicInfo>> GetMusicInfoFromNetEaseMusicSongListAsync(string songListId, string outputDirectory, string pattern)
|
||||
{
|
||||
var secretKey = NetEaseMusicEncryptionHelper.CreateSecretKey(16);
|
||||
var encSecKey = NetEaseMusicEncryptionHelper.RsaEncode(secretKey);
|
||||
@ -47,7 +47,7 @@ public class NetEaseMusicSongListMusicScanner : ITransientDependency
|
||||
.Select(song =>
|
||||
{
|
||||
var artistName = song.Artists?.FirstOrDefault()?.Name ?? string.Empty;
|
||||
var fakeFilePath = Path.Combine(options.OutputDirectory, options.OutputFileNamePattern.Replace("{Name}", song.Name).Replace("{Artist}", artistName));
|
||||
var fakeFilePath = Path.Combine(outputDirectory, pattern.Replace("{Name}", song.Name).Replace("{Artist}", artistName));
|
||||
|
||||
var info = new MusicInfo(fakeFilePath, song.Name!, artistName);
|
||||
return info;
|
||||
|
@ -12,7 +12,7 @@ public class CsvFileMusicScannerTests : TestBase
|
||||
public async Task GetMusicInfoFromCsvFileAsync_Test()
|
||||
{
|
||||
var musicScanner = GetService<CsvFileMusicScanner>();
|
||||
var musicInfo = await musicScanner.GetMusicInfoFromCsvFileAsync(Path.Combine("TestData", "test.csv"), new ManualDownloadOptions());
|
||||
var musicInfo = await musicScanner.GetMusicInfoFromCsvFileAsync(Path.Combine("TestData", "test.csv"), "DownloadedLrc", "{Artist} - {Name}.lrc");
|
||||
|
||||
musicInfo.ShouldNotBeNull();
|
||||
musicInfo.Count.ShouldBeGreaterThan(0);
|
||||
|
@ -11,7 +11,7 @@ public class NetEaseMusicSongListMusicScannerTests : TestBase
|
||||
public async Task GetMusicInfoFromNetEaseMusicSongListAsync_Test()
|
||||
{
|
||||
var musicScanner = GetService<NetEaseMusicSongListMusicScanner>();
|
||||
var musicInfo = await musicScanner.GetMusicInfoFromNetEaseMusicSongListAsync("7224428149", new ManualDownloadOptions());
|
||||
var musicInfo = await musicScanner.GetMusicInfoFromNetEaseMusicSongListAsync("7224428149", "DownloadedLrc", "{Artist} - {Name}.lrc");
|
||||
|
||||
musicInfo.ShouldNotBeNull();
|
||||
musicInfo.Count.ShouldBeGreaterThan(10);
|
||||
|
Loading…
x
Reference in New Issue
Block a user