diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/APISearchRequest.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/APISearchRequest.cs index d4ce23e..d3dd298 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/APISearchRequest.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/APISearchRequest.cs @@ -11,7 +11,7 @@ public class APISearchRequest : OsuJsonWebRequest { public APISearchRequest(string target) { - Url = $"https://music.163.com/api/search/get/web?hlpretag=&hlposttag=&s={target}&type=1&total=true&limit=1"; + Url = $"https://music.163.com/api/search/get/web?hlpretag=&hlposttag=&s={target}&type=1&total=true&limit=10"; } protected override void ProcessResponse() diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/LyricProcessor.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/LyricProcessor.cs index b347577..9fa272a 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/LyricProcessor.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/LyricProcessor.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http; using System.Text.Encodings.Web; using System.Threading; @@ -198,41 +199,27 @@ public void SearchByNeteaseID(long id, WorkingBeatmap beatmap, Action private void onSongSearchRequestFinish(RequestFinishMeta meta, APISearchRequest? searchRequest) { - if (!meta.Success) - { - //如果没成功,尝试使用标题重搜 - if (meta.SourceBeatmap != null && !meta.NoRetry) - { - var searchMeta = SearchOption.FromRequestFinishMeta(meta); - searchMeta.NoArtist = true; - searchMeta.NoRetry = true; - searchMeta.NoLocalFile = true; - - if (searchRequest != null && searchRequest == currentSearchRequest) - setState(SearchState.FuzzySearching); - - //Logging.Log("精准搜索失败, 将尝试只搜索标题..."); - Search(searchMeta); - } - else - { - if (searchRequest != null && searchRequest == currentSearchRequest) - setState(SearchState.Fail); - - meta.OnFail?.Invoke("未搜索到对应歌曲!"); - } + var sourceBeatmap = meta.SourceBeatmap; + var songs = meta.SearchResponseRoot.Result?.Songs; + if (songs == null) + { + setState(SearchState.Fail); + meta.OnFail?.Invoke("搜索请求失败"); return; } - float similarPercentage = meta.GetSimilarPercentage(); - - Logging.Log($"Beatmap: '{meta.SourceBeatmap?.Metadata.GetTitle() ?? "???"}' <-> '{meta.GetNeteaseTitle()}' -> {similarPercentage} <-> {meta.TitleSimilarThreshold}"); + var titleMatches = songs.Select(s => new { Song = s, SimilarPercentage = s.GetSimilarPercentage(sourceBeatmap) }) + .Where(p => p.SimilarPercentage >= meta.TitleSimilarThreshold) + .OrderBy(p => p.SimilarPercentage); + var artistMatches = titleMatches.OrderBy(s => LevenshteinDistance.Compute(s.Song.GetArtist(), sourceBeatmap.Metadata.GetArtist())); + var perfectMatch = artistMatches.FirstOrDefault(); - if (similarPercentage >= meta.TitleSimilarThreshold) + if (perfectMatch != null && perfectMatch.SimilarPercentage > meta.TitleSimilarThreshold) { + Logging.Log($"Beatmap: '{sourceBeatmap.Metadata.GetTitle()}' <-> '{perfectMatch.Song.Name}' -> {perfectMatch.SimilarPercentage} > {meta.TitleSimilarThreshold}"); //标题匹配,发送歌词查询请求 - var req = new APILyricRequest(meta.SongID); + var req = new APILyricRequest(perfectMatch.Song.ID); req.Finished += () => { if (currentLyricRequest == req) @@ -254,10 +241,10 @@ private void onSongSearchRequestFinish(RequestFinishMeta meta, APISearchRequest? else { //Logging.Log("标题匹配失败, 将不会继续搜索歌词..."); - this.setState(SearchState.Fail); + setState(SearchState.Fail); - Logging.Log($"对 {meta.SourceBeatmap?.Metadata.GetTitle() ?? "未知谱面"} 的标题匹配失败:"); - Logging.Log($"Beatmap: '{meta.SourceBeatmap?.Metadata.GetTitle() ?? "???"}' <-> '{meta.GetNeteaseTitle()}' -> {similarPercentage} < {meta.TitleSimilarThreshold}"); + Logging.Log($"对 {sourceBeatmap?.Metadata.GetTitle() ?? "未知谱面"} 的标题匹配失败:"); + //Logging.Log($"Beatmap: '{sourceBeatmap?.Metadata.GetTitle() ?? "???"}' <-> '{meta.GetNeteaseTitle()}' -> {similarPercentage} < {meta.TitleSimilarThreshold}"); meta.OnFail?.Invoke("标题匹配失败, 将不会继续搜索歌词..."); } diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/RequestFinishMeta.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/RequestFinishMeta.cs index 1764a90..cdd8154 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/RequestFinishMeta.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/RequestFinishMeta.cs @@ -1,7 +1,5 @@ using System; -using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.IGPlayer.Feature.Player.Misc; using osu.Game.Rulesets.IGPlayer.Feature.Player.Plugins.Bundle.CloudMusic.Misc; namespace osu.Game.Rulesets.IGPlayer.Feature.Player.Plugins.Bundle.CloudMusic.Helper @@ -15,7 +13,7 @@ public struct RequestFinishMeta /// /// 与此请求对应的 /// - public WorkingBeatmap? SourceBeatmap; + public WorkingBeatmap SourceBeatmap; /// /// 标题匹配阈值,值越高要求越严格 @@ -32,56 +30,6 @@ public struct RequestFinishMeta /// public bool NoRetry; - /// - /// 歌曲ID,未搜到歌曲时返回-1 - /// - public long SongID => (SearchResponseRoot.Result?.Songs?.First().ID ?? -1); - - /// - /// 获取网易云歌曲标题和搜索标题的相似度 - /// - /// 相似度百分比 - public float GetSimilarPercentage() - { - string neteaseTitle = GetNeteaseTitle().ToLowerInvariant(); - string ourTitle = SourceBeatmap?.Metadata.GetTitle().ToLowerInvariant() ?? string.Empty; - - string source = neteaseTitle.Length > ourTitle.Length ? neteaseTitle : ourTitle; - string target = neteaseTitle.Length > ourTitle.Length ? ourTitle : neteaseTitle; - - if (string.IsNullOrEmpty(neteaseTitle) || string.IsNullOrEmpty(ourTitle)) return 0; - - int distance = LevenshteinDistance.Compute(source, target); - float percentage = 1 - (distance / (float)source.Length); - - return Math.Abs(percentage); - } - - public string GetNeteaseTitle() - { - return SearchResponseRoot?.Result?.Songs?.First().Name ?? string.Empty; - } - - /// - /// 返回一个失败的 - /// - /// 和此Meta对应的> - /// 通过参数构建的> - public static RequestFinishMeta Fail(WorkingBeatmap? beatmap = null) - { - return new RequestFinishMeta - { - Success = false, - - OnFinish = null, - OnFail = null, - SearchResponseRoot = new APISearchResponseRoot(), - - SourceBeatmap = beatmap, - NoRetry = true - }; - } - /// /// 通过给定的参数构建> /// @@ -91,7 +39,7 @@ public static RequestFinishMeta Fail(WorkingBeatmap? beatmap = null) /// 失败时要进行的动作 /// /// 通过参数构建的> - public static RequestFinishMeta From(APISearchResponseRoot responseRoot, WorkingBeatmap? sourceBeatmap, + public static RequestFinishMeta From(APISearchResponseRoot responseRoot, WorkingBeatmap sourceBeatmap, Action? onFinish, Action? onFail, float titleSimiliarThreshold) { @@ -100,7 +48,6 @@ public static RequestFinishMeta From(APISearchResponseRoot responseRoot, Working OnFinish = onFinish, OnFail = onFail, SearchResponseRoot = responseRoot, - Success = (responseRoot.Result?.Songs?.First().ID ?? -1) > 0, SourceBeatmap = sourceBeatmap, TitleSimilarThreshold = titleSimiliarThreshold }; diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISongInfo.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISongInfo.cs index ec628da..b3fe2ad 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISongInfo.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISongInfo.cs @@ -1,13 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.IGPlayer.Feature.Player.Misc; +using osu.Game.Rulesets.IGPlayer.Feature.Player.Plugins.Bundle.CloudMusic.Helper; namespace osu.Game.Rulesets.IGPlayer.Feature.Player.Plugins.Bundle.CloudMusic.Misc { + [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] public class APISongInfo { - [JsonProperty("id")] public long ID { get; set; } - [JsonProperty("name")] public string? Name { get; set; } + + public List Artists { get; set; } = []; + + /// + /// 获取网易云歌曲标题和搜索标题的相似度 + /// + /// 相似度百分比 + public float GetSimilarPercentage(WorkingBeatmap? beatmap) + { + string neteaseTitle = Name?.ToLowerInvariant() ?? string.Empty; + string ourTitle = beatmap?.Metadata.GetTitle().ToLowerInvariant() ?? string.Empty; + + string source = neteaseTitle.Length > ourTitle.Length ? neteaseTitle : ourTitle; + string target = neteaseTitle.Length > ourTitle.Length ? ourTitle : neteaseTitle; + + if (string.IsNullOrEmpty(neteaseTitle) || string.IsNullOrEmpty(ourTitle)) return 0; + + int distance = LevenshteinDistance.Compute(source, target); + float percentage = 1 - (distance / (float)source.Length); + + return Math.Abs(percentage); + } + + public string GetArtist() => string.Join(' ', Artists.Select(a => a.Name)); } } diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/ArtistInfo.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/ArtistInfo.cs index 24e68cb..5360952 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/ArtistInfo.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/ArtistInfo.cs @@ -1,20 +1,26 @@ -#nullable disable - using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; namespace osu.Game.Rulesets.IGPlayer.Feature.Player.Plugins.Bundle.CloudMusic.Misc { + [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] public class APIArtistInfo { - public int id { get; set; } - public string name { get; set; } - public string picUrl { get; set; } - public IList alias { get; set; } - public int albunSize { get; set; } - public int picId { get; set; } - public string img1v1Url { get; set; } - public int img1v1 { get; set; } - public string trans { get; set; } - public int albumSize { get; set; } + public int ID { get; set; } + public string Name { get; set; } = string.Empty; + public string PicUrl { get; set; } = string.Empty; + public IList? Alias { get; set; } + public int AlbunSize { get; set; } + public int PicId { get; set; } + + [JsonProperty("img1v1Url")] + public string Img1V1Url { get; set; } = string.Empty; + + [JsonProperty("img1v1")] + public int Img1V1 { get; set; } + + public string? Trans { get; set; } + public int AlbumSize { get; set; } } }