mirror of
https://github.com/real-zony/ZonyLrcToolsX.git
synced 2025-07-01 20:30:41 +00:00
feat: Added KuWo(酷我) Music lyrics source (incomplete).
This commit is contained in:
parent
6b72f919b8
commit
b7b1f36bf5
@ -5,7 +5,6 @@ using ZonyLrcTools.Common.Album;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
namespace ZonyLrcTools.Cli.Commands.SubCommand
|
||||
|
@ -43,6 +43,9 @@ globalOption:
|
||||
- name: KuGou # 基于酷狗音乐的歌词下载器。
|
||||
priority: 3
|
||||
depth: 10
|
||||
- name: KuWo # 基于酷我音乐的歌词下载器。
|
||||
priority: 4
|
||||
depth: 10
|
||||
# 歌词下载的一些共有配置参数。
|
||||
config:
|
||||
isOneLine: true # 双语歌词是否合并为一行展示。
|
||||
|
@ -15,7 +15,7 @@ namespace ZonyLrcTools.Common.Infrastructure.Exceptions
|
||||
/// <param name="errorCode">错误码,参考 <see cref="ErrorCodes"/> 类的定义。</param>
|
||||
/// <param name="message">错误信息。</param>
|
||||
/// <param name="attachObj">附加的对象数据。</param>
|
||||
public ErrorCodeException(int errorCode, string message = null, object attachObj = null) : base(message)
|
||||
public ErrorCodeException(int errorCode, string? message = null, object attachObj = null) : base(message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
AttachObject = attachObj;
|
||||
|
@ -19,5 +19,10 @@ namespace ZonyLrcTools.Common.Lyrics
|
||||
/// 酷狗音乐歌词下载器。
|
||||
/// </summary>
|
||||
public const string KuGou = nameof(KuGou);
|
||||
|
||||
/// <summary>
|
||||
/// 酷我音乐歌词下载器。
|
||||
/// </summary>
|
||||
public const string KuWo = nameof(KuWo);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ZonyLrcTools.Common.Lyrics.Providers.KuWo.JsonModel;
|
||||
|
||||
public class GetLyricsRequest
|
||||
{
|
||||
[JsonProperty("musicId")] public long MusicId { get; }
|
||||
|
||||
public GetLyricsRequest(long musicId)
|
||||
{
|
||||
MusicId = musicId;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ZonyLrcTools.Common.Lyrics.Providers.KuWo.JsonModel;
|
||||
|
||||
public class GetLyricsResponse
|
||||
{
|
||||
[JsonProperty("status")] public int Status { get; set; }
|
||||
|
||||
[JsonProperty("lrclist")] public ICollection<GetLyricsItem> Lyrics { get; set; }
|
||||
|
||||
[JsonProperty("msg")] public string? ErrorMessage { get; set; }
|
||||
|
||||
[JsonProperty("msgs")] public string? ErrorMessage2 { get; set; }
|
||||
}
|
||||
|
||||
public class GetLyricsItem
|
||||
{
|
||||
[JsonProperty("lineLyric")] public string Text { get; set; }
|
||||
|
||||
[JsonProperty("time")] public string Position { get; set; }
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ZonyLrcTools.Common.Lyrics.Providers.KuWo.JsonModel;
|
||||
|
||||
public class SongSearchRequest
|
||||
{
|
||||
[JsonProperty("key")] public string Keyword { get; set; }
|
||||
|
||||
[JsonProperty("pn")] public int PageNumber { get; }
|
||||
|
||||
[JsonProperty("rn")] public int PageSize { get; }
|
||||
|
||||
public SongSearchRequest(string name, string artist, int pageNumber = 1, int pageSize = 20)
|
||||
{
|
||||
Keyword = $"{name} {artist}";
|
||||
PageNumber = pageNumber;
|
||||
PageSize = pageSize;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ZonyLrcTools.Common.Lyrics.Providers.KuWo.JsonModel;
|
||||
|
||||
public class SongSearchResponse
|
||||
{
|
||||
[JsonProperty("code")] public int Code { get; set; }
|
||||
|
||||
[JsonProperty("data")] public SongSearchResponseInnerData InnerData { get; set; }
|
||||
|
||||
[JsonProperty("msg")] public string? ErrorMessage { get; set; }
|
||||
|
||||
public long GetMatchedMusicId(string musicName, string artistName, long? duration)
|
||||
{
|
||||
var prefectMatch = InnerData.SongItems.FirstOrDefault(x => x.Name == musicName && x.Artist == artistName);
|
||||
if (prefectMatch != null)
|
||||
{
|
||||
return prefectMatch.MusicId;
|
||||
}
|
||||
|
||||
if (duration is null or 0)
|
||||
{
|
||||
return InnerData.SongItems.First().MusicId;
|
||||
}
|
||||
|
||||
return InnerData.SongItems.OrderBy(t => Math.Abs(t.Duration - duration.Value)).First().MusicId;
|
||||
}
|
||||
}
|
||||
|
||||
public class SongSearchResponseInnerData
|
||||
{
|
||||
[JsonProperty("total")] public string Total { get; set; }
|
||||
|
||||
[JsonProperty("list")] public ICollection<SongSearchResponseDetail> SongItems { get; set; }
|
||||
}
|
||||
|
||||
public class SongSearchResponseDetail
|
||||
{
|
||||
[JsonProperty("artist")] public string? Artist { get; set; }
|
||||
|
||||
[JsonProperty("name")] public string? Name { get; set; }
|
||||
|
||||
[JsonProperty("rid")] public long MusicId { get; set; }
|
||||
|
||||
[JsonProperty("duration")] public long Duration { get; set; }
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Common.Infrastructure.Exceptions;
|
||||
using ZonyLrcTools.Common.Infrastructure.Network;
|
||||
using ZonyLrcTools.Common.Lyrics.Providers.KuWo.JsonModel;
|
||||
|
||||
namespace ZonyLrcTools.Common.Lyrics.Providers.KuWo;
|
||||
|
||||
public class KuWoLyricsProvider : LyricsProvider
|
||||
{
|
||||
public override string DownloaderName => InternalLyricsProviderNames.KuWo;
|
||||
|
||||
private const string KuWoSearchMusicUrl = @"https://www.kuwo.cn/api/www/search/searchMusicBykeyWord";
|
||||
private const string KuWoSearchLyricsUrl = @"https://m.kuwo.cn/newh5/singles/songinfoandlrc";
|
||||
private const string KuWoDefaultToken = "ABCDE12345";
|
||||
|
||||
private readonly IWarpHttpClient _warpHttpClient;
|
||||
private readonly ILyricsItemCollectionFactory _lyricsItemCollectionFactory;
|
||||
private readonly GlobalOptions _options;
|
||||
|
||||
private static readonly ProductInfoHeaderValue UserAgent = new("Chrome", "81.0.4044.138");
|
||||
|
||||
public KuWoLyricsProvider(IWarpHttpClient warpHttpClient,
|
||||
ILyricsItemCollectionFactory lyricsItemCollectionFactory,
|
||||
IOptions<GlobalOptions> options)
|
||||
{
|
||||
_warpHttpClient = warpHttpClient;
|
||||
_lyricsItemCollectionFactory = lyricsItemCollectionFactory;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
protected override async ValueTask<byte[]> DownloadDataAsync(LyricsProviderArgs args)
|
||||
{
|
||||
var songSearchResponse = await _warpHttpClient.GetAsync<SongSearchResponse>(KuWoSearchMusicUrl,
|
||||
new SongSearchRequest(args.SongName, args.Artist, pageSize: _options.Provider.Lyric.GetLyricProviderOption(DownloaderName).Depth),
|
||||
op =>
|
||||
{
|
||||
op.Headers.UserAgent.Add(UserAgent);
|
||||
op.Headers.Referrer = new Uri("https://kuwo.cn");
|
||||
op.Headers.Add("csrf", KuWoDefaultToken);
|
||||
op.Headers.Add("Cookie", $"kw_token={KuWoDefaultToken}");
|
||||
});
|
||||
|
||||
ValidateSongSearchResponse(songSearchResponse, args);
|
||||
|
||||
var songLyricsResponse = await _warpHttpClient.GetAsync<GetLyricsResponse>(KuWoSearchLyricsUrl,
|
||||
new GetLyricsRequest(songSearchResponse.GetMatchedMusicId(args.SongName, args.Artist, args.Duration)),
|
||||
op =>
|
||||
{
|
||||
op.Headers.UserAgent.Add(UserAgent);
|
||||
op.Headers.Referrer = new Uri("https://m.kuwo.cn/yinyue/");
|
||||
});
|
||||
|
||||
return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(songLyricsResponse.Lyrics));
|
||||
}
|
||||
|
||||
protected override ValueTask<LyricsItemCollection> GenerateLyricAsync(byte[] data, LyricsProviderArgs args)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected void ValidateSongSearchResponse(SongSearchResponse response, LyricsProviderArgs args)
|
||||
{
|
||||
if (response.Code != 200)
|
||||
{
|
||||
throw new ErrorCodeException(ErrorCodes.TheReturnValueIsIllegal, response.ErrorMessage, args);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,13 +5,13 @@ using Shouldly;
|
||||
using Xunit;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyrics
|
||||
{
|
||||
public class KuGouLyricDownloaderTests : TestBase
|
||||
public class KuGouLyricProviderTests : TestBase
|
||||
{
|
||||
private readonly ILyricsProvider _lyricsProvider;
|
||||
|
||||
public KuGouLyricDownloaderTests()
|
||||
public KuGouLyricProviderTests()
|
||||
{
|
||||
_lyricsProvider = GetService<IEnumerable<ILyricsProvider>>()
|
||||
.FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.KuGou);
|
@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyrics;
|
||||
|
||||
public class KuWoLyricsProviderTests : TestBase
|
||||
{
|
||||
private readonly ILyricsProvider _kuwoLyricsProvider;
|
||||
|
||||
public KuWoLyricsProviderTests()
|
||||
{
|
||||
_kuwoLyricsProvider = GetService<IEnumerable<ILyricsProvider>>()
|
||||
.FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.KuWo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DownloadAsync_Test()
|
||||
{
|
||||
var lyric = await _kuwoLyricsProvider.DownloadAsync("告白气球", "周杰伦");
|
||||
lyric.ShouldNotBeNull();
|
||||
lyric.IsPruneMusic.ShouldBeFalse();
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ using Xunit;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyrics
|
||||
{
|
||||
public class LyricCollectionTests : TestBase
|
||||
{
|
@ -8,13 +8,13 @@ using Xunit;
|
||||
using ZonyLrcTools.Common.Configuration;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyrics
|
||||
{
|
||||
public class NetEaseLyricDownloaderTests : TestBase
|
||||
public class NetEaseLyricsProviderTests : TestBase
|
||||
{
|
||||
private readonly ILyricsProvider _lyricsProvider;
|
||||
|
||||
public NetEaseLyricDownloaderTests()
|
||||
public NetEaseLyricsProviderTests()
|
||||
{
|
||||
_lyricsProvider = GetService<IEnumerable<ILyricsProvider>>()
|
||||
.FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.NetEase);
|
@ -5,13 +5,13 @@ using Shouldly;
|
||||
using Xunit;
|
||||
using ZonyLrcTools.Common.Lyrics;
|
||||
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyric
|
||||
namespace ZonyLrcTools.Tests.Infrastructure.Lyrics
|
||||
{
|
||||
public class QQLyricDownloaderTests : TestBase
|
||||
public class QQLyricsProviderTests : TestBase
|
||||
{
|
||||
private readonly ILyricsProvider _lyricsProvider;
|
||||
|
||||
public QQLyricDownloaderTests()
|
||||
public QQLyricsProviderTests()
|
||||
{
|
||||
_lyricsProvider = GetService<IEnumerable<ILyricsProvider>>()
|
||||
.FirstOrDefault(t => t.DownloaderName == InternalLyricsProviderNames.QQ);
|
@ -1,6 +1,7 @@
|
||||
New Features:
|
||||
|
||||
- CLI 提供了一个更新检查功能,在程序启动的时候会检测最新的版本。
|
||||
- 支持从酷我音乐搜索歌词。
|
||||
|
||||
Breaking Changes: None
|
||||
Enhancement:
|
||||
|
Loading…
x
Reference in New Issue
Block a user