Use YAML as configuration file; fix a download failure issue with QQ Music. (Reason: QQ Music API changed)

This commit is contained in:
real-zony 2022-03-18 19:51:08 +08:00
parent d1c50323b1
commit 2b0b14cd7a
30 changed files with 136 additions and 171 deletions

View File

@ -82,7 +82,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
private async Task<List<string>> ScanMusicFilesAsync()
{
var files = (await _fileScanner.ScanAsync(Directory, _options.SupportFileExtensions.Split(';')))
var files = (await _fileScanner.ScanAsync(Directory, _options.SupportFileExtensions))
.SelectMany(t => t.FilePaths)
.ToList();
@ -112,7 +112,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
private IEnumerable<ILyricDownloader> GetLyricDownloaderList()
{
var downloader = _options.LyricDownloaderOptions
var downloader = _options.Provider.Lyric.Plugin
.Where(op => op.Priority != -1)
.OrderBy(op => op.Priority)
.Join(_lyricDownloaderList,

View File

@ -0,0 +1,17 @@
using ZonyLrcTools.Cli.Infrastructure.Lyric;
using ZonyLrcTools.Cli.Infrastructure.Tag;
namespace ZonyLrcTools.Cli.Config;
public class ProviderOption
{
/// <summary>
/// 标签加载器相关的配置属性。
/// </summary>
public TagOption Tag { get; set; }
/// <summary>
/// 歌词下载相关的配置信息。
/// </summary>
public LyricOption Lyric { get; set; }
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using YamlDotNet.Serialization;
using ZonyLrcTools.Cli.Infrastructure.Lyric;
using ZonyLrcTools.Cli.Infrastructure.Network;
using ZonyLrcTools.Cli.Infrastructure.Tag;
@ -8,19 +9,9 @@ namespace ZonyLrcTools.Cli.Config
public class ToolOptions
{
/// <summary>
/// 支持的音乐文件后缀集合,以 ; 进行分隔
/// 支持的音乐文件后缀集合
/// </summary>
public string SupportFileExtensions { get; set; }
/// <summary>
/// 歌词下载相关的配置信息。
/// </summary>
public LyricItemCollectionOption LyricOption { get; set; }
/// <summary>
/// 标签加载器相关的配置属性。
/// </summary>
public IEnumerable<TagInfoProviderInstance> TagInfoProviderOptions { get; set; }
public List<string> SupportFileExtensions { get; set; }
/// <summary>
/// 网络代理相关的配置信息。
@ -28,13 +19,8 @@ namespace ZonyLrcTools.Cli.Config
public NetworkOptions NetworkOptions { get; set; }
/// <summary>
/// 歌词下载器相关的配置属性
/// 定义下载器的相关配置信息
/// </summary>
public IEnumerable<LyricDownloaderOption> LyricDownloaderOptions { get; set; }
/// <summary>
/// 屏蔽词功能相关配置。
/// </summary>
public BlockWordOption BlockWordOptions { get; set; }
public ProviderOption Provider { get; set; }
}
}

View File

@ -31,8 +31,8 @@ namespace ZonyLrcTools.Cli.Infrastructure.DependencyInject
return new HttpClientHandler
{
UseProxy = option.NetworkOptions.Enable,
Proxy = new WebProxy($"{option.NetworkOptions.ProxyIp}:{option.NetworkOptions.ProxyPort}")
UseProxy = option.NetworkOptions.IsEnable,
Proxy = new WebProxy($"{option.NetworkOptions.Ip}:{option.NetworkOptions.Port}")
};
});
@ -46,10 +46,10 @@ namespace ZonyLrcTools.Cli.Infrastructure.DependencyInject
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddYamlFile("config.yaml")
.Build();
services.Configure<ToolOptions>(configuration.GetSection("ToolOption"));
services.Configure<ToolOptions>(configuration.GetSection("globalOption"));
return services;
}

View File

@ -9,8 +9,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
/// 根据指定的歌曲数据构建新的 <see cref="LyricItemCollection"/> 实例。
/// </summary>
/// <param name="sourceLyric">原始歌词数据。</param>
/// <param name="translateLyric">翻译歌词数据。</param>
/// <returns>构建完成的 <see cref="LyricItemCollection"/> 对象。</returns>
LyricItemCollection Build(string sourceLyric, string translateLyric = null);
LyricItemCollection Build(string sourceLyric);
}
}

View File

@ -16,9 +16,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
/// </summary>
public bool IsPruneMusic => Count == 0;
public LyricItemCollectionOption Option { get; private set; }
public LyricConfigOption Option { get; private set; }
public LyricItemCollection(LyricItemCollectionOption option)
public LyricItemCollection(LyricConfigOption option)
{
Option = option;
}
@ -33,7 +33,6 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
var option = left.Option;
var newCollection = new LyricItemCollection(option);
var indexDiff = left.Count - right.Count;
if (!option.IsOneLine)
{
left.ForEach(item => newCollection.Add(item));

View File

@ -17,9 +17,9 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
_options = options.Value;
}
public LyricItemCollection Build(string sourceLyric, string translateLyric = null)
public LyricItemCollection Build(string sourceLyric)
{
var items = new LyricItemCollection(_options.LyricOption);
var items = new LyricItemCollection(_options.Provider.Lyric.Config);
if (string.IsNullOrEmpty(sourceLyric))
{
return items;

View File

@ -1,17 +0,0 @@
namespace ZonyLrcTools.Cli.Infrastructure.Lyric
{
public class LyricItemCollectionOption
{
/// <summary>
/// 双语歌词是否合并为一行。
/// </summary>
public bool IsOneLine { get; set; } = false;
/// <summary>
/// 换行符格式,取值来自 <see cref="LineBreakType"/> 常量类。
/// </summary>
public string LineBreak { get; set; } = LineBreakType.Windows;
public static readonly LyricItemCollectionOption NullInstance = new();
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace ZonyLrcTools.Cli.Infrastructure.Lyric;
public class LyricOption
{
public IEnumerable<LyricProviderOption> Plugin { get; set; }
public LyricConfigOption Config { get; set; }
}
public class LyricConfigOption
{
/// <summary>
/// 双语歌词是否合并为一行。
/// </summary>
public bool IsOneLine { get; set; } = false;
/// <summary>
/// 换行符格式,取值来自 <see cref="LineBreakType"/> 常量类。
/// </summary>
public string LineBreak { get; set; } = LineBreakType.Windows;
/// <summary>
/// 是否启用歌词翻译功能。
/// </summary>
public bool IsEnableTranslation { get; set; } = false;
}

View File

@ -1,6 +1,6 @@
namespace ZonyLrcTools.Cli.Infrastructure.Lyric
{
public class LyricDownloaderOption
public class LyricProviderOption
{
/// <summary>
/// 歌词下载器的唯一标识。

View File

@ -61,7 +61,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
var json = JsonConvert.DeserializeObject<GetLyricResponse>(Encoding.UTF8.GetString(data));
if (json?.OriginalLyric == null)
{
return new LyricItemCollection(LyricItemCollectionOption.NullInstance);
return new LyricItemCollection(null);
}
if (json.OriginalLyric.Text.Contains("纯音乐,请欣赏"))
@ -70,8 +70,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
}
return _lyricItemCollectionFactory.Build(
json.OriginalLyric.Text,
json.TranslationLyric?.Text);
json.OriginalLyric.Text);
}
protected virtual void ValidateSongSearchResponse(SongSearchResponse response, LyricDownloaderArgs args)

View File

@ -6,70 +6,32 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
{
public class SongSearchRequest
{
[JsonProperty("ct")] public int UnknownParameter1 { get; set; }
[JsonProperty("qqmusic_ver")] public int ClientVersion { get; set; }
[JsonProperty("new_json")] public int UnknownParameter2 { get; set; }
[JsonProperty("remoteplace")] public string RemotePlace { get; set; }
[JsonProperty("t")] public int UnknownParameter3 { get; set; }
[JsonProperty("aggr")] public int UnknownParameter4 { get; set; }
[JsonProperty("cr")] public int UnknownParameter5 { get; set; }
[JsonProperty("catZhida")] public int UnknownParameter6 { get; set; }
[JsonProperty("lossless")] public int LossLess { get; set; }
[JsonProperty("flag_qc")] public int UnknownParameter7 { get; set; }
[JsonProperty("p")] public int Page { get; set; }
[JsonProperty("n")] public int Limit { get; set; }
[JsonProperty("w")] public string Keyword { get; set; }
[JsonProperty("g_tk")] public int UnknownParameter8 { get; set; }
[JsonProperty("hostUin")] public int UnknownParameter9 { get; set; }
[JsonProperty("format")] public string ResultFormat { get; set; }
[JsonProperty("inCharset")] public string InCharset { get; set; }
[JsonProperty("outCharset")] public string OutCharset { get; set; }
[JsonProperty("notice")] public int UnknownParameter10 { get; set; }
[JsonProperty("platform")] public string Platform { get; set; }
[JsonProperty("needNewCode")] public int UnknownParameter11 { get; set; }
protected SongSearchRequest()
{
UnknownParameter1 = 24;
ClientVersion = 1298;
UnknownParameter2 = 1;
RemotePlace = "txt.yqq.song";
UnknownParameter3 = 0;
UnknownParameter4 = 1;
UnknownParameter5 = 1;
UnknownParameter6 = 1;
LossLess = 0;
UnknownParameter7 = 0;
Page = 1;
Limit = 5;
UnknownParameter8 = 5381;
UnknownParameter9 = 0;
ResultFormat = "json";
InCharset = "utf8";
OutCharset = "utf8";
UnknownParameter10 = 0;
Platform = "yqq";
UnknownParameter11 = 0;
}
public SongSearchRequest(string musicName, string artistName) : this()

View File

@ -22,11 +22,6 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
public class QQMusicInnerSongItem
{
[JsonProperty("mid")] public string SongId { get; set; }
}
public class AlbumInfo
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("songmid")] public string SongId { get; set; }
}
}

View File

@ -65,7 +65,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic
var sourceLyric = HttpUtility.HtmlDecode(HttpUtility.HtmlDecode(lyricJsonObj.SelectToken("$.lyric").Value<string>()));
var translateLyric = HttpUtility.HtmlDecode(HttpUtility.HtmlDecode(lyricJsonObj.SelectToken("$.trans").Value<string>()));
return _lyricItemCollectionFactory.Build(sourceLyric, translateLyric);
return _lyricItemCollectionFactory.Build(sourceLyric);
}
protected virtual void ValidateSongSearchResponse(SongSearchResponse response, LyricDownloaderArgs args)

View File

@ -8,16 +8,16 @@ namespace ZonyLrcTools.Cli.Infrastructure.Network
/// <summary>
/// 是否启用了网络代理功能。
/// </summary>
public bool Enable { get; set; }
public bool IsEnable { get; set; }
/// <summary>
/// 代理服务器的 Ip。
/// </summary>
public string ProxyIp { get; set; }
public string Ip { get; set; }
/// <summary>
/// 代理服务器的 端口。
/// </summary>
public int ProxyPort { get; set; }
public int Port { get; set; }
}
}

View File

@ -21,7 +21,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
_wordsDictionary = new Lazy<Dictionary<string, string>>(() =>
{
var jsonData = File.ReadAllText(_options.BlockWordOptions.BlockWordDictionaryFile);
var jsonData = File.ReadAllText(_options.Provider.Tag.BlockWord.FilePath);
return JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonData);
});
}

View File

@ -13,6 +13,6 @@
/// <summary>
/// 屏蔽词字典文件,用于替换歌曲名或者歌手名称。
/// </summary>
public string BlockWordDictionaryFile { get; set; }
public string FilePath { get; set; }
}
}

View File

@ -48,7 +48,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
protected void HandleBlockWord(MusicInfo info)
{
if (Options.BlockWordOptions.IsEnable)
if (Options.Provider.Tag.BlockWord.IsEnable)
{
info.Name = BlockWordDictionary.GetValue(info.Name) ?? info.Name;
info.Artist = BlockWordDictionary.GetValue(info.Name) ?? info.Artist;

View File

@ -15,7 +15,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
{
public string Name => ConstantName;
public const string ConstantName = "FileName";
public const string RegularExpressionsOption = "RegularExpressions";
public const string RegularExpressionsOption = "regularExpressions";
private readonly ToolOptions _options;
@ -28,7 +28,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
{
await ValueTask.CompletedTask;
var regex = _options.TagInfoProviderOptions
var regex = _options.Provider.Tag.Plugin
.First(t => t.Name == ConstantName)
.Extensions[RegularExpressionsOption];

View File

@ -1,8 +1,9 @@
using System.Collections.Generic;
using YamlDotNet.Serialization;
namespace ZonyLrcTools.Cli.Infrastructure.Tag
{
public class TagInfoProviderInstance
public class TagInfoProviderOption
{
public string Name { get; set; }
@ -10,4 +11,14 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
public Dictionary<string, string> Extensions { get; set; }
}
public class TagOption
{
public IEnumerable<TagInfoProviderOption> Plugin { get; set; }
/// <summary>
/// 屏蔽词功能相关配置。
/// </summary>
public BlockWordOption BlockWord { get; set; }
}
}

View File

@ -75,7 +75,7 @@ namespace ZonyLrcTools.Cli
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
.AddYamlFile("config.yaml");
})
.ConfigureServices((_, services) =>
{

View File

@ -10,6 +10,7 @@
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine" Version="4.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Refit" Version="6.3.2" />
<PackageReference Include="Refit.HttpClientFactory" Version="6.3.2" />
@ -23,9 +24,6 @@
<ItemGroup>
<None Remove="appsettings.json" />
<Content Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Remove="Resources\error_msg.json" />
<Content Include="Resources\error_msg.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
@ -34,6 +32,10 @@
<Content Include="BlockWords.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Remove="config.yaml" />
<Content Include="config.yaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -1,52 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"ToolOption": {
"SupportFileExtensions": "*.mp3;*.flac;*.wav",
"NetworkOptions": {
"Enable": false,
"ProxyIp": "127.0.0.1",
"ProxyPort": 4780
},
"TagInfoProviderOptions": [
{
"Name": "Taglib",
"Priority": 1
},
{
"Name": "FileName",
"Priority": 2,
"Extensions": {
"RegularExpressions": "(?'artist'.+)\\s-\\s(?'name'.+)"
}
}
],
"LyricDownloaderOptions": [
{
"Name": "NetEase",
"Priority": 1
},
{
"Name": "QQ",
"Priority": 2
},
{
"Name": "KuGou",
"Priority": 3
}
],
"LyricOption": {
"IsOneLine": true,
"LineBreak": "\n"
},
"BlockWordOptions": {
"IsEnable": false,
"BlockWordDictionaryFile": "BlockWords.json"
}
}
}

View File

@ -0,0 +1,34 @@
globalOption:
supportFileExtensions:
- '*.mp3'
- '*.flac'
- '*.wav'
networkOptions:
isEnable: false
ip: 127.0.0.1
port: 4780
provider:
tag:
plugin:
- name: Taglib
priority: 1
- name: FileName
priority: 2
extensions:
regularExpressions: "(?'artist'.+)\\s-\\s(?'name'.+)"
blockWord:
isEnable: false
filePath: 'BlockWords.json'
lyric:
plugin:
- name: NetEase
priority: 1
- name: QQ
priority: 2
- name: KuGou
priority: 3
config:
isOneLine: true
lineBreak: '\n'
isEnableTranslation: false

View File

@ -15,7 +15,8 @@ namespace ZonyLrcTools.Tests
public async Task ScanAsync_Test()
{
var tempMusicFilePath = Path.Combine(Directory.GetCurrentDirectory(), "Temp.mp3");
File.Create(tempMusicFilePath);
var fs = File.Create(tempMusicFilePath);
fs.Close();
var fileScanner = ServiceProvider.GetRequiredService<IFileScanner>();
var result = await fileScanner.ScanAsync(

View File

@ -12,7 +12,7 @@ namespace ZonyLrcTools.Tests.Infrastructure.Exceptions
ErrorCodeHelper.LoadErrorMessage();
ErrorCodeHelper.ErrorMessages.ShouldNotBeNull();
ErrorCodeHelper.ErrorMessages.Count.ShouldBe(11);
ErrorCodeHelper.ErrorMessages.Count.ShouldBe(15);
}
[Fact]

View File

@ -9,7 +9,7 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
[Fact]
public void LyricCollectionLineBreak_Test()
{
var lyricObject = new LyricItemCollection(new LyricItemCollectionOption
var lyricObject = new LyricItemCollection(new LyricConfigOption
{
IsOneLine = false,
LineBreak = LineBreakType.MacOs
@ -18,7 +18,7 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
new(0, 20, "你好世界!"),
new(0, 22, "Hello World!")
};
lyricObject.ToString().ShouldContain(LineBreakType.MacOs);
}
}

View File

@ -21,7 +21,7 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
[Fact]
public async Task DownloadAsync_Test()
{
var lyric = await _lyricDownloader.DownloadAsync("Hollow", "Janet Leon");
var lyric = await _lyricDownloader.DownloadAsync("东风破", "胡歌");
lyric.ShouldNotBeNull();
lyric.IsPruneMusic.ShouldBe(false);
}

View File

@ -34,8 +34,8 @@ namespace ZonyLrcTools.Tests.Infrastructure.Network
public async Task GetAsyncWithProxy_Test()
{
var option = ServiceProvider.GetRequiredService<IOptions<ToolOptions>>();
option.Value.NetworkOptions.ProxyIp = "127.0.0.1";
option.Value.NetworkOptions.ProxyPort = 4780;
option.Value.NetworkOptions.Ip = "127.0.0.1";
option.Value.NetworkOptions.Port = 4780;
var client = ServiceProvider.GetRequiredService<IWarpHttpClient>();

View File

@ -1,3 +1,4 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
@ -14,7 +15,7 @@ namespace ZonyLrcTools.Tests.Infrastructure.Tag
var tagLoader = ServiceProvider.GetRequiredService<ITagLoader>();
tagLoader.ShouldNotBeNull();
var info = await tagLoader.LoadTagAsync(@"D:\はるまきごはん 煮ル果実 くらげP 蜂屋ななし じん かいりきベア - ダンスロボットダンス (アレンジメドレー (キメラver) はるまきごはん×煮ル果実×和田たけあき×栗山夕璃(蜂屋.flac");
var info = await tagLoader.LoadTagAsync(Path.Combine(Directory.GetCurrentDirectory(), "MusicFiles", "曾经艺也 - 荀彧(纯音乐版).mp3"));
info.ShouldNotBeNull();
info.Name.ShouldBe("荀彧(纯音乐版)");
info.Artist.ShouldBe("曾经艺也");