diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs index b2dd7ef..57154c0 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs @@ -48,7 +48,7 @@ public void UpdateMetadata(WorkingBeatmap beatmap) var metadata = beatmap.Metadata; this.MetaData.Artist = metadata.GetArtist(); this.MetaData.ArtistRomainsed = metadata.Artist; - this.MetaData.Title = metadata.GetTitle(); + this.MetaData.Title = metadata.GetTitle().Title; this.MetaData.TitleRomainsed = metadata.Title; this.MetaData.Mapper = metadata.Author.Username; this.MetaData.DiffName = beatmap.BeatmapInfo.DifficultyName; diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Misc/BeatmapMetataExtension.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Misc/BeatmapMetadataExtension.cs similarity index 80% rename from osu.Game.Rulesets.IGPlayer/Feature/Player/Misc/BeatmapMetataExtension.cs rename to osu.Game.Rulesets.IGPlayer/Feature/Player/Misc/BeatmapMetadataExtension.cs index 284521b..7cb009a 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Misc/BeatmapMetataExtension.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Misc/BeatmapMetadataExtension.cs @@ -1,35 +1,35 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; -using osu.Game.Beatmaps; - -namespace osu.Game.Rulesets.IGPlayer.Feature.Player.Misc -{ - public static class BeatmapMetataExtension - { - public static RomanisableString GetTitleRomanisable(this BeatmapMetadata metadata) - { - return new RomanisableString(metadata.TitleUnicode, metadata.Title); - } - - public static RomanisableString GetArtistRomanisable(this BeatmapMetadata metadata) - { - return new RomanisableString(metadata.ArtistUnicode, metadata.Artist); - } - - public static string GetTitle(this BeatmapMetadata metadata) - { - return string.IsNullOrEmpty(metadata.TitleUnicode) - ? metadata.Title - : metadata.TitleUnicode; - } - - public static string GetArtist(this BeatmapMetadata metadata) - { - return string.IsNullOrEmpty(metadata.ArtistUnicode) - ? metadata.Artist - : metadata.ArtistUnicode; - } - } -} +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.IGPlayer.Feature.Player.Misc +{ + public static class BeatmapMetadataExtension + { + public static RomanisableString GetTitleRomanisable(this BeatmapMetadata metadata) + { + return new RomanisableString(metadata.TitleUnicode, metadata.Title); + } + + public static RomanisableString GetArtistRomanisable(this BeatmapMetadata metadata) + { + return new RomanisableString(metadata.ArtistUnicode, metadata.Artist); + } + + public static (string Title, bool IsUnicode) GetTitle(this BeatmapMetadata metadata) + { + return string.IsNullOrEmpty(metadata.TitleUnicode) + ? (metadata.Title, false) + : (metadata.TitleUnicode, true); + } + + public static string GetArtist(this BeatmapMetadata metadata) + { + return string.IsNullOrEmpty(metadata.ArtistUnicode) + ? metadata.Artist + : metadata.ArtistUnicode; + } + } +} 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/LevenshteinDistance.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/LevenshteinDistance.cs index 0054286..60d4953 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/LevenshteinDistance.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/LevenshteinDistance.cs @@ -44,5 +44,18 @@ public static int Compute(string s, string t) return d[n, m]; } + + public static float ComputeSimilarPercentage(string s, string t) + { + string source = s.Length > t.Length ? s : t; + string target = s.Length > t.Length ? t : s; + + if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(target)) + return 0; + + int distance = Compute(source, target); + float percentage = 1 - (distance / (float)source.Length); + return Math.Abs(percentage); + } } } 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 1e69111..f417f53 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; @@ -74,7 +75,7 @@ public void Search(SearchOption searchOption) var onFinish = searchOption.OnFinish; var onFail = searchOption.OnFail; - if (!searchOption.NoLocalFile && searchOption.NoRetry) + if (!searchOption.NoLocalFile) { var localLyrics = GetLocalLyrics(beatmap); @@ -104,16 +105,15 @@ public void Search(SearchOption searchOption) currentLyricRequest?.Dispose(); //处理要搜索的歌名: "标题 艺术家" - string title = beatmap.Metadata.GetTitle(); - string artist = searchOption.NoArtist ? string.Empty : $" {beatmap.Metadata.GetArtist()}"; + string title = searchOption.SearchMode == SearchMode.RomanisedTitle ? beatmap.Metadata.Title : beatmap.Metadata.TitleUnicode; + string artist = searchOption.SearchMode == SearchMode.NoArtist ? string.Empty : beatmap.Metadata.GetArtist(); string target = encoder.Encode($"{title} {artist}"); var req = new APISearchRequest(target); req.Finished += () => { - var meta = RequestFinishMeta.From(req.ResponseObject, beatmap, onFinish, onFail, searchOption.TitleSimiliarThreshold); - meta.NoRetry = searchOption.NoRetry; + var meta = RequestFinishMeta.From(req.ResponseObject, beatmap, onFinish, onFail, searchOption.SearchMode, searchOption.TitleSimilarThreshold); onSongSearchRequestFinish(meta, req); }; @@ -183,7 +183,7 @@ public void SearchByNeteaseID(long id, WorkingBeatmap beatmap, Action private void onSongSearchRequestFinish(RequestFinishMeta meta, APISearchRequest? searchRequest) { - if (!meta.Success) + var sourceBeatmap = meta.SourceBeatmap; + var songs = meta.SearchResponseRoot.Result?.Songs ?? []; + + songs.ForEach(s => s.CalculateSimilarPercentage(sourceBeatmap)); + + var titleMatches = songs.Where(p => p.TitleSimilarPercentage >= meta.TitleSimilarThreshold) + .OrderByDescending(p => p.TitleSimilarPercentage); + var artistMatches = titleMatches.OrderByDescending(s => s.ArtistSimilarPercentage); + var match = artistMatches.FirstOrDefault(); + string title = meta.SearchMode == SearchMode.RomanisedTitle ? sourceBeatmap.Metadata.Title : sourceBeatmap.Metadata.TitleUnicode; + + if (match != null) { - //如果没成功,尝试使用标题重搜 - if (meta.SourceBeatmap != null && !meta.NoRetry) + if (match.ArtistSimilarPercentage >= (meta.SearchMode == SearchMode.NoArtist ? 0 : meta.TitleSimilarThreshold)) { - var searchMeta = SearchOption.FromRequestFinishMeta(meta); - searchMeta.NoArtist = true; - searchMeta.NoRetry = true; - searchMeta.NoLocalFile = true; + Logging.Log($"Beatmap: '{title}' <-> '{match.Name}' -> {match.TitleSimilarPercentage} >= {meta.TitleSimilarThreshold}"); + } + } - if (searchRequest != null && searchRequest == currentSearchRequest) - setState(SearchState.FuzzySearching); + if (match == null) + { + var searchMeta = SearchOption.FromRequestFinishMeta(meta); - //Logging.Log("精准搜索失败, 将尝试只搜索标题..."); - Search(searchMeta); - } - else + switch (meta.SearchMode) { - if (searchRequest != null && searchRequest == currentSearchRequest) - setState(SearchState.Fail); + case SearchMode.Normal: + Logging.Log("尝试使用罗马音标题搜索"); + searchMeta.SearchMode = SearchMode.RomanisedTitle; + searchMeta.NoLocalFile = true; - meta.OnFail?.Invoke("未搜索到对应歌曲!"); - } + if (searchRequest != null && searchRequest == currentSearchRequest) + setState(SearchState.FuzzySearching); - return; - } + Search(searchMeta); + break; - float similiarPrecentage = meta.GetSimiliarPrecentage(); + case SearchMode.RomanisedTitle: + searchMeta.SearchMode = SearchMode.NoArtist; + searchMeta.NoLocalFile = true; - Logging.Log($"Beatmap: '{meta.SourceBeatmap?.Metadata.GetTitle() ?? "???"}' <-> '{meta.GetNeteaseTitle()}' -> {similiarPrecentage} <-> {meta.TitleSimiliarThreshold}"); + if (searchRequest != null && searchRequest == currentSearchRequest) + setState(SearchState.FuzzySearching); - if (similiarPrecentage >= meta.TitleSimiliarThreshold) - { - //标题匹配,发送歌词查询请求 - var req = new APILyricRequest(meta.SongID); - req.Finished += () => - { - if (currentLyricRequest == req) - setState(SearchState.Success); + Search(searchMeta); + break; - meta.OnFinish?.Invoke(req.ResponseObject); - }; - req.Failed += e => - { - if (currentLyricRequest == req) + case SearchMode.NoArtist: + meta.OnFail?.Invoke("标题匹配失败, 将不会继续搜索歌词..."); setState(SearchState.Fail); + break; + } - Logging.LogError(e, "获取歌词失败"); - }; - req.PerformAsync(cancellationTokenSource.Token).ConfigureAwait(false); - - currentLyricRequest = req; + return; } - else + + var req = new APILyricRequest(match.ID); + req.Finished += () => + { + if (currentLyricRequest == req) + setState(SearchState.Success); + + meta.OnFinish?.Invoke(req.ResponseObject); + }; + req.Failed += e => { - //Logging.Log("标题匹配失败, 将不会继续搜索歌词..."); - this.setState(SearchState.Fail); + if (currentLyricRequest == req) + setState(SearchState.Fail); - Logging.Log($"对 {meta.SourceBeatmap?.Metadata.GetTitle() ?? "未知谱面"} 的标题匹配失败:"); - Logging.Log($"Beatmap: '{meta.SourceBeatmap?.Metadata.GetTitle() ?? "???"}' <-> '{meta.GetNeteaseTitle()}' -> {similiarPrecentage} < {meta.TitleSimiliarThreshold}"); + Logging.LogError(e, "获取歌词失败"); + }; + req.PerformAsync(cancellationTokenSource.Token).ConfigureAwait(false); - meta.OnFail?.Invoke("标题匹配失败, 将不会继续搜索歌词..."); - } + currentLyricRequest = req; } #endregion 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 3d928ff..c82a30d 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,12 +13,12 @@ public struct RequestFinishMeta /// /// 与此请求对应的 /// - public WorkingBeatmap? SourceBeatmap; + public WorkingBeatmap SourceBeatmap; /// /// 标题匹配阈值,值越高要求越严格 /// - public float TitleSimiliarThreshold; + public float TitleSimilarThreshold; /// /// 请求是否成功? @@ -28,59 +26,14 @@ public struct RequestFinishMeta public bool Success; /// - /// 是否要重新搜索? - /// - public bool NoRetry; - - /// - /// 歌曲ID,未搜到歌曲时返回-1 - /// - public long SongID => (SearchResponseRoot.Result?.Songs?.First().ID ?? -1); - - /// - /// 获取网易云歌曲标题和搜索标题的相似度 + /// 请求是否成功? /// - /// 相似度百分比 - public float GetSimiliarPrecentage() - { - 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 precentage = 1 - (distance / (float)source.Length); - - return Math.Abs(precentage); - } - - public string GetNeteaseTitle() - { - return SearchResponseRoot?.Result?.Songs?.First().Name ?? string.Empty; - } + public SearchMode SearchMode; /// - /// 返回一个失败的 + /// 是否要重新搜索? /// - /// 和此Meta对应的> - /// 通过参数构建的> - public static RequestFinishMeta Fail(WorkingBeatmap? beatmap = null) - { - return new RequestFinishMeta - { - Success = false, - - OnFinish = null, - OnFail = null, - SearchResponseRoot = new APISearchResponseRoot(), - - SourceBeatmap = beatmap, - NoRetry = true - }; - } + public bool NoRetry; /// /// 通过给定的参数构建> @@ -89,10 +42,12 @@ public static RequestFinishMeta Fail(WorkingBeatmap? beatmap = null) /// 和此Meta对应的> /// 完成时要进行的动作 /// 失败时要进行的动作 - /// + /// + /// /// 通过参数构建的> - public static RequestFinishMeta From(APISearchResponseRoot responseRoot, WorkingBeatmap? sourceBeatmap, + public static RequestFinishMeta From(APISearchResponseRoot responseRoot, WorkingBeatmap sourceBeatmap, Action? onFinish, Action? onFail, + SearchMode searchMode, float titleSimiliarThreshold) { return new RequestFinishMeta @@ -100,9 +55,9 @@ public static RequestFinishMeta From(APISearchResponseRoot responseRoot, Working OnFinish = onFinish, OnFail = onFail, SearchResponseRoot = responseRoot, - Success = (responseRoot.Result?.Songs?.First().ID ?? -1) > 0, SourceBeatmap = sourceBeatmap, - TitleSimiliarThreshold = titleSimiliarThreshold + SearchMode = searchMode, + TitleSimilarThreshold = titleSimiliarThreshold }; } } diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/SearchOption.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/SearchOption.cs index 5739755..874992f 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/SearchOption.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Helper/SearchOption.cs @@ -1,5 +1,6 @@ using System; 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 @@ -17,12 +18,7 @@ public struct SearchOption /// /// 是否要不带艺术家搜索? /// - public bool NoArtist; - - /// - /// 失败后是否禁止重试? - /// - public bool NoRetry; + public SearchMode SearchMode; /// /// 在发出请求前是否要尝试从本地缓存寻找和谱面ID对应的歌词文件? @@ -32,7 +28,7 @@ public struct SearchOption /// /// 标题匹配阈值,值越高要求越严格 /// - public float TitleSimiliarThreshold; + public float TitleSimilarThreshold; /// /// 通过给定的参数构建. @@ -41,22 +37,22 @@ public struct SearchOption /// /// 完成时要进行的动作 /// 失败时要进行的动作 - /// + /// /// 通过参数构建的> public static SearchOption From(WorkingBeatmap sourceBeatmap, bool noLocalFile, Action? onFinish, Action onFail, - float titleSimiliarThreshold) + float titleSimilarThreshold) { + var title = sourceBeatmap.Metadata.GetTitle(); + return new SearchOption { + SearchMode = title.IsUnicode ? SearchMode.Normal : SearchMode.RomanisedTitle, Beatmap = sourceBeatmap, - OnFinish = onFinish, OnFail = onFail, - NoLocalFile = noLocalFile, - - TitleSimiliarThreshold = titleSimiliarThreshold + TitleSimilarThreshold = titleSimilarThreshold }; } @@ -72,12 +68,17 @@ public static SearchOption FromRequestFinishMeta(RequestFinishMeta requestFinish return new SearchOption { Beatmap = requestFinishMeta.SourceBeatmap, - OnFinish = requestFinishMeta.OnFinish, OnFail = requestFinishMeta.OnFail, - - TitleSimiliarThreshold = requestFinishMeta.TitleSimiliarThreshold + TitleSimilarThreshold = requestFinishMeta.TitleSimilarThreshold }; } } + + public enum SearchMode + { + Normal, + RomanisedTitle, + NoArtist, + } } diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISearchResultInfo.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISearchResultInfo.cs index ba36877..c012b30 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISearchResultInfo.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Misc/APISearchResultInfo.cs @@ -6,7 +6,7 @@ namespace osu.Game.Rulesets.IGPlayer.Feature.Player.Plugins.Bundle.CloudMusic.Mi public class APISearchResultInfo { [JsonProperty("songs")] - public IList? Songs { get; set; } + public List? Songs { get; set; } [JsonProperty("songCount")] public int SongCount { get; set; } 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..be8d820 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,47 @@ +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 long Duration { get; set; } + public string? Name { get; set; } + + public List Artists { get; set; } = []; + + public float TitleSimilarPercentage { get; set; } + + public float ArtistSimilarPercentage { get; set; } + + /// + /// 获取网易云歌曲标题和搜索标题的相似度 + /// + /// 相似度百分比 + public void CalculateSimilarPercentage(WorkingBeatmap beatmap) + { + string neteaseTitle = Name?.ToLowerInvariant() ?? string.Empty; + string ourTitle = beatmap.Metadata.Title.ToLowerInvariant() ?? string.Empty; + + float titleSimilarPercentage = LevenshteinDistance.ComputeSimilarPercentage(neteaseTitle, ourTitle); + ourTitle = beatmap.Metadata.TitleUnicode.ToLowerInvariant() ?? string.Empty; + float titleSimilarPercentageUnicode = LevenshteinDistance.ComputeSimilarPercentage(neteaseTitle, ourTitle); + TitleSimilarPercentage = Math.Max(titleSimilarPercentageUnicode, titleSimilarPercentage); + + ourTitle = beatmap.Metadata.GetArtist(); + ArtistSimilarPercentage = Artists.Count(a => ourTitle.Contains(a.Name)) / (float)Artists.Count; + } + + 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; } } } diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Sidebar/Graphic/Toolbox.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Sidebar/Graphic/Toolbox.cs index d2880ec..64194e0 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Sidebar/Graphic/Toolbox.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/CloudMusic/Sidebar/Graphic/Toolbox.cs @@ -239,7 +239,7 @@ private void initToolbox() textBox.OnCommit += (sender, isNewText) => { - if (int.TryParse(sender.Text, out int id)) + if (long.TryParse(sender.Text, out long id)) plugin.GetLyricFor(id); else { diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/CollectionHelper.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/CollectionHelper.cs index 049d45f..8ba3298 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/CollectionHelper.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/CollectionHelper.cs @@ -222,10 +222,7 @@ public bool IsCurrent { var track = b.Value.Track; - track.Completed += () => - { - if (IsCurrent) Schedule(() => NextTrack()); - }; + track.Completed += onTrackCompleted; trackChangedAfterDisable = false; } @@ -238,15 +235,20 @@ private void changeBeatmap(WorkingBeatmap working) if (Disabled.Value) return; var track = b.Value.Track; + track.Completed -= onTrackCompleted; b.Disabled = false; b.Value = working; b.Disabled = IsCurrent; - track.Completed += () => - { - if (IsCurrent) Schedule(() => NextTrack()); - }; - controller.Play(); + + // 总是重新开始播放,因为两个谱面可能使用同一个track + controller.Play(true); + controller.CurrentTrack.Completed += onTrackCompleted; + } + + private void onTrackCompleted() + { + if (IsCurrent) Schedule(() => NextTrack()); } /// diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/BeatmapPiece.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/BeatmapPiece.cs index cf8b7ac..8295633 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/BeatmapPiece.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/BeatmapPiece.cs @@ -1,5 +1,3 @@ -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -8,7 +6,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -132,13 +129,13 @@ private void load() { new TruncatingSpriteText { - Text = getRomanisableStringFor(Beatmap.Metadata.TitleUnicode, Beatmap.Metadata.Title), + Text = Beatmap.Metadata.GetTitleRomanisable(), Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 20), RelativeSizeAxes = Axes.X }, new TruncatingSpriteText { - Text = getRomanisableStringFor(Beatmap.Metadata.ArtistUnicode, Beatmap.Metadata.Artist), + Text = Beatmap.Metadata.GetArtistRomanisable(), Font = OsuFont.GetFont(weight: FontWeight.Bold), RelativeSizeAxes = Axes.X } @@ -175,14 +172,6 @@ private void load() }, true); } - private RomanisableString getRomanisableStringFor(string original, string romanised) - { - string original1 = string.IsNullOrEmpty(original) ? romanised : original; - string romanised1 = string.IsNullOrEmpty(romanised) ? original : romanised; - - return new RomanisableString(original, romanised1); - } - private void OnActiveChanged(ValueChangedEvent v) { switch (v.NewValue) diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/CollectionPanel.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/CollectionPanel.cs index 30a0052..f2a8db2 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/CollectionPanel.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Collection/Sidebar/CollectionPanel.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Graphics; @@ -326,9 +327,7 @@ private void addBeatmapThumbnails() collections++; var b = beatmaps.GetWorkingBeatmap(c.Beatmaps.First().AsBeatmapInfo()); - string tooltip = $"{b.Metadata.ArtistUnicode}" - + " - " - + $"{b.Metadata.TitleUnicode}"; + LocalisableString tooltip = LocalisableString.Interpolate($"{b.Metadata.GetArtistRomanisable()} - {b.Metadata.GetTitleRomanisable()}"); if (collections <= limit) { diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/ClassicPanel.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/ClassicPanel.cs index 51e82c2..87d644f 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/ClassicPanel.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/ClassicPanel.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -24,7 +23,7 @@ public partial class ClassicPanel : CompositeDrawable, IPanel private void load(YaspPlugin plugin) { var config = (YaspConfigManager)Dependencies.Get().GetConfigManager(plugin); - config.BindWith(YaspSettings.Scale, scaleBindable); + config.BindWith(YaspSettings.Scale, scaleBindable); scaleBindable.BindValueChanged(v => { this.ScaleTo(v.NewValue, 300, Easing.OutQuint); @@ -71,12 +70,12 @@ public void Refresh(WorkingBeatmap beatmap) new OsuSpriteText { Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), - Text = new RomanisableString(beatmap?.Metadata.TitleUnicode, beatmap?.Metadata.Title) + Text = beatmap.Metadata.GetTitleRomanisable() }, new OsuSpriteText { Font = OsuFont.GetFont(size: 25), - Text = new RomanisableString(beatmap?.Metadata.ArtistUnicode, beatmap?.Metadata.Artist) + Text = beatmap.Metadata.GetArtistRomanisable() }, new OsuSpriteText { diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/CoverIIPanel.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/CoverIIPanel.cs index 99df8e7..5e6cf58 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/CoverIIPanel.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/CoverIIPanel.cs @@ -1,6 +1,5 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -19,20 +18,18 @@ namespace osu.Game.Rulesets.IGPlayer.Feature.Player.Plugins.Bundle.Yasp.Panels; public partial class CoverIIPanel : CompositeDrawable, IPanel { - private WorkingBeatmap currentBeatmap; + private WorkingBeatmap? currentBeatmap; - public void Refresh(WorkingBeatmap beatmap) + public void Refresh(WorkingBeatmap? beatmap) { - this.currentBeatmap = beatmap; + currentBeatmap = beatmap; var meta = beatmap?.Metadata ?? new BeatmapMetadata(); - titleText.Text = displayUnicode.Value ? meta.GetTitle() : meta.Title; - artistText.Text = displayUnicode.Value ? meta.GetArtist() : meta.Artist; + titleText.Text = meta.GetTitleRomanisable(); + artistText.Text = meta.GetArtistRomanisable(); sourceText.Text = string.IsNullOrEmpty(meta.Source) - ? displayUnicode.Value - ? meta.Title - : meta.GetTitle() + ? meta.GetTitleRomanisable() : meta.Source; cover?.Refresh(useUserAvatar.Value, beatmap); @@ -41,18 +38,17 @@ public void Refresh(WorkingBeatmap beatmap) public override void Show() { - this.flowContainer?.FadeIn(300, Easing.OutQuint).ScaleTo(1, 1500, Easing.OutBack); + flowContainer?.FadeIn(300, Easing.OutQuint).ScaleTo(1, 1500, Easing.OutBack); base.Show(); } public override void Hide() { - this.flowContainer?.FadeOut(300, Easing.OutQuint).ScaleTo(0.8f, 300, Easing.OutQuint); + flowContainer?.FadeOut(300, Easing.OutQuint).ScaleTo(0.8f, 300, Easing.OutQuint); this.Delay(300).FadeOut(); } private readonly BindableBool useUserAvatar = new BindableBool(); - private readonly BindableBool displayUnicode = new BindableBool(); private readonly TruncatingSpriteText titleText = new TruncatingSpriteText { @@ -81,11 +77,10 @@ public override void Hide() private FillFlowContainer? flowContainer; [BackgroundDependencyLoader] - private void load(YaspPlugin plugin, FrameworkConfigManager frameworkConfig) + private void load(YaspPlugin plugin) { var config = (YaspConfigManager)Dependencies.Get().GetConfigManager(plugin); config.BindWith(YaspSettings.CoverIIUseUserAvatar, useUserAvatar); - frameworkConfig.BindWith(FrameworkSetting.ShowUnicode, displayUnicode); Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -162,16 +157,11 @@ private void load(YaspPlugin plugin, FrameworkConfigManager frameworkConfig) } }; - this.AddInternal(flowContainer); + AddInternal(flowContainer); useUserAvatar.BindValueChanged(v => { - Refresh(this.currentBeatmap); + Refresh(currentBeatmap); }); - - displayUnicode.BindValueChanged(v => - { - Refresh(this.currentBeatmap); - }, true); } private partial class AvatarOrBeatmapCover : CompositeDrawable @@ -185,7 +175,7 @@ private partial class AvatarOrBeatmapCover : CompositeDrawable [BackgroundDependencyLoader] private void load() { - this.Masking = true; + Masking = true; } public void Refresh(bool useUserAvatar, WorkingBeatmap? workingBeatmap) diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/NsiPanel.cs b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/NsiPanel.cs index 25c7ccf..5eaed07 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/NsiPanel.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Player/Plugins/Bundle/Yasp/Panels/NsiPanel.cs @@ -1,6 +1,5 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -25,7 +24,6 @@ public partial class NsiPanel : CompositeDrawable, IPanel private Container metadataContainer; private readonly Bindable beatmap = new Bindable(); - private readonly Bindable displayUnicode = new BindableBool(); private Container coverAnimContainer; private Box bgBox; private OsuSpriteText artistDisplay; @@ -35,14 +33,9 @@ public partial class NsiPanel : CompositeDrawable, IPanel [Resolved] private IImplementLLin player { get; set; } - [Resolved] - private FrameworkConfigManager frameworkConfig { get; set; } - [BackgroundDependencyLoader] private void load() { - frameworkConfig.BindWith(FrameworkSetting.ShowUnicode, displayUnicode); - var shear = new Vector2(0.25f, 0); RelativeSizeAxes = Axes.Both; @@ -122,8 +115,6 @@ private void load() } } }; - - displayUnicode.BindValueChanged(_ => updateMetaText(beatmap.Value?.Metadata)); } public void Refresh(WorkingBeatmap beatmap) @@ -156,14 +147,12 @@ private void updateMetaText(BeatmapMetadata meta) { meta ??= new BeatmapMetadata(); - artistDisplay.Text = displayUnicode.Value ? meta.GetArtist() : meta.Artist; + artistDisplay.Text = meta.GetArtistRomanisable(); sourceDisplay.Text = string.IsNullOrEmpty(meta.Source) - ? displayUnicode.Value - ? meta.Title - : meta.GetTitle() + ? meta.GetTitleRomanisable() : meta.Source; - titleDisplay.Text = displayUnicode.Value ? meta.GetTitle() : meta.Title; + titleDisplay.Text = meta.GetTitleRomanisable(); } public override void Hide()