fix: fill Duration (via searching NCM) for amll-ttml-db lyrics source; improve matching system

This commit is contained in:
Zhe Fang
2026-01-09 13:50:46 -05:00
parent 77a9bb0a1b
commit 2738d45b69
6 changed files with 45 additions and 22 deletions

View File

@@ -36,6 +36,12 @@ namespace BetterLyrics.WinUI3.Extensions
return songInfo; return songInfo;
} }
public SongInfo WithSongId(string value)
{
songInfo.SongId = value;
return songInfo;
}
public PlayHistoryItem? ToPlayHistoryItem(double actualPlayedMs) public PlayHistoryItem? ToPlayHistoryItem(double actualPlayedMs)
{ {
if (songInfo == null) return null; if (songInfo == null) return null;

View File

@@ -10,27 +10,27 @@ namespace BetterLyrics.WinUI3.Helper
{ {
public static partial class MetadataComparer public static partial class MetadataComparer
{ {
private const double WeightTitle = 0.40; private const double WeightTitle = 0.30;
private const double WeightArtist = 0.40; private const double WeightArtist = 0.30;
private const double WeightAlbum = 0.10; private const double WeightAlbum = 0.10;
private const double WeightDuration = 0.10; private const double WeightDuration = 0.30;
// JaroWinkler 适合短字符串匹配 // JaroWinkler 适合短字符串匹配
private static readonly JaroWinkler _algo = new(); private static readonly JaroWinkler _algo = new();
public static int CalculateScore(SongInfo songInfo, LyricsCacheItem remote) public static int CalculateScore(SongInfo songInfo, LyricsCacheItem remote)
{ {
return CalculateScore(songInfo, remote.Title, remote.Artist, remote.Album, remote.Duration * 1000); return CalculateScore(songInfo, remote.Title, remote.Artist, remote.Album, remote.Duration);
} }
public static int CalculateScore(SongInfo songInfo, FilesIndexItem local) public static int CalculateScore(SongInfo songInfo, FilesIndexItem local)
{ {
return CalculateScore(songInfo, local.Title, local.Artist, local.Album, local.Duration * 1000, local.FileName); return CalculateScore(songInfo, local.Title, local.Artist, local.Album, local.Duration, local.FileName);
} }
public static int CalculateScore( public static int CalculateScore(
SongInfo songInfo, SongInfo songInfo,
string? compareTitle, string? compareArtist, string? compareAlbum, double? compareDurationMs, string? compareFileName = null) string? compareTitle, string? compareArtist, string? compareAlbum, double? compareDuration, string? compareFileName = null)
{ {
double totalScore = 0; double totalScore = 0;
@@ -42,7 +42,7 @@ namespace BetterLyrics.WinUI3.Helper
double titleScore = GetStringSimilarity(songInfo.Title, compareTitle); double titleScore = GetStringSimilarity(songInfo.Title, compareTitle);
double artistScore = GetStringSimilarity(songInfo.Artist, compareArtist); double artistScore = GetStringSimilarity(songInfo.Artist, compareArtist);
double albumScore = GetStringSimilarity(songInfo.Album, compareAlbum); double albumScore = GetStringSimilarity(songInfo.Album, compareAlbum);
double durationScore = GetDurationSimilarity(songInfo.DurationMs, compareDurationMs); double durationScore = GetDurationSimilarity(songInfo.Duration, compareDuration);
totalScore = (titleScore * WeightTitle) + totalScore = (titleScore * WeightTitle) +
(artistScore * WeightArtist) + (artistScore * WeightArtist) +
@@ -94,19 +94,18 @@ namespace BetterLyrics.WinUI3.Helper
return _algo.Similarity(s1, s2); return _algo.Similarity(s1, s2);
} }
private static double GetDurationSimilarity(double localMs, double? remoteSeconds) private static double GetDurationSimilarity(double localSeconds, double? remoteSeconds)
{ {
if (remoteSeconds == null || remoteSeconds == 0) return 0.0; // 远程没有时长数据,不匹配 if (remoteSeconds == null || remoteSeconds == 0) return 0.0; // 远程没有时长数据,不匹配
double localSeconds = localMs / 1000.0;
double diff = Math.Abs(localSeconds - remoteSeconds.Value); double diff = Math.Abs(localSeconds - remoteSeconds.Value);
// 差距 <= 3100% 相似 // 差距 <= 1 100 % 相似
// 差距 >= 200% 相似 // 差距 >= 10 0 % 相似
// 中间线性插值 // 中间线性插值
const double PerfectTolerance = 3.0; const double PerfectTolerance = 1.0;
const double MaxTolerance = 20.0; const double MaxTolerance = 10.0;
if (diff <= PerfectTolerance) return 1.0; if (diff <= PerfectTolerance) return 1.0;
if (diff >= MaxTolerance) return 0.0; if (diff >= MaxTolerance) return 0.0;

View File

@@ -19,6 +19,9 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
public string SecondaryText { get; set; } = ""; public string SecondaryText { get; set; } = "";
public string TertiaryText { get; set; } = ""; public string TertiaryText { get; set; } = "";
public new string Text => PrimaryText;
public new int StartIndex = 0;
public LyricsLine() public LyricsLine()
{ {
for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++) for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++)

View File

@@ -206,7 +206,6 @@ namespace BetterLyrics.WinUI3.Services.GSMTCService
private void InitMediaManager() private void InitMediaManager()
{ {
_mediaManager.Start(); _mediaManager.Start();
_mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSession(x.Value.Id)); _mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSession(x.Value.Id));
_mediaManager.OnAnySessionOpened += MediaManager_OnAnySessionOpened; _mediaManager.OnAnySessionOpened += MediaManager_OnAnySessionOpened;

View File

@@ -55,6 +55,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsCacheService
existingItem.Title = result.Title; existingItem.Title = result.Title;
existingItem.Artist = result.Artist; existingItem.Artist = result.Artist;
existingItem.Album = result.Album; existingItem.Album = result.Album;
existingItem.Duration = result.Duration;
existingItem.TransliterationProvider = result.TransliterationProvider; existingItem.TransliterationProvider = result.TransliterationProvider;
existingItem.TranslationProvider = result.TranslationProvider; existingItem.TranslationProvider = result.TranslationProvider;

View File

@@ -1,5 +1,6 @@
// 2025/6/23 by Zhe Fang // 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Constants;
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions; using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
@@ -29,7 +30,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{ {
private readonly HttpClient _amllTtmlDbHttpClient; private readonly HttpClient _amllTtmlDbHttpClient;
private readonly HttpClient _lrcLibHttpClient; private readonly HttpClient _lrcLibHttpClient;
private readonly AppleMusic _appleMusic; private readonly Providers.AppleMusic _appleMusic;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly IFileSystemService _fileSystemService; private readonly IFileSystemService _fileSystemService;
@@ -54,10 +55,10 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
_lrcLibHttpClient = new(); _lrcLibHttpClient = new();
_lrcLibHttpClient.DefaultRequestHeaders.Add( _lrcLibHttpClient.DefaultRequestHeaders.Add(
"User-Agent", "User-Agent",
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.BetterLyricsGitHub})" $"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Link.BetterLyricsGitHub})"
); );
_amllTtmlDbHttpClient = new(); _amllTtmlDbHttpClient = new();
_appleMusic = new AppleMusic(); _appleMusic = new Providers.AppleMusic();
} }
private static bool IsAmllTtmlDbIndexInvalid() private static bool IsAmllTtmlDbIndexInvalid()
@@ -402,6 +403,8 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
} }
string? rawLyricFile = null; string? rawLyricFile = null;
string? bestNcmMusicId = null;
await foreach (var line in File.ReadLinesAsync(PathHelper.AmllTtmlDbIndexPath)) await foreach (var line in File.ReadLinesAsync(PathHelper.AmllTtmlDbIndexPath))
{ {
if (string.IsNullOrWhiteSpace(line)) if (string.IsNullOrWhiteSpace(line))
@@ -416,6 +419,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
string? title = null; string? title = null;
string? artist = null; string? artist = null;
string? album = null; string? album = null;
string? ncmMusicId = null;
foreach (var meta in metadataArr.EnumerateArray()) foreach (var meta in metadataArr.EnumerateArray())
{ {
@@ -429,6 +433,8 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
artist = string.Join(" / ", valueArr.EnumerateArray()); artist = string.Join(" / ", valueArr.EnumerateArray());
if (key == "album" && valueArr.GetArrayLength() > 0) if (key == "album" && valueArr.GetArrayLength() > 0)
album = valueArr[0].GetString(); album = valueArr[0].GetString();
if (key == "ncmMusicId" && valueArr.GetArrayLength() > 0)
ncmMusicId = valueArr[0].GetString();
} }
int score = MetadataComparer.CalculateScore(songInfo, new LyricsCacheItem int score = MetadataComparer.CalculateScore(songInfo, new LyricsCacheItem
@@ -436,12 +442,12 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
Title = title, Title = title,
Artist = artist, Artist = artist,
Album = album, Album = album,
Duration = 0,
}); });
if (score > lyricsSearchResult.MatchPercentage) if (score > lyricsSearchResult.MatchPercentage)
{ {
if (root.TryGetProperty("rawLyricFile", out var rawLyricFileProp)) if (root.TryGetProperty("rawLyricFile", out var rawLyricFileProp))
{ {
bestNcmMusicId = ncmMusicId;
rawLyricFile = rawLyricFileProp.GetString(); rawLyricFile = rawLyricFileProp.GetString();
lyricsSearchResult.Title = title; lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist; lyricsSearchResult.Artist = artist;
@@ -458,19 +464,28 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult; return lyricsSearchResult;
} }
// 下载歌词内容 var url = $"{_settingsService.AppSettings.GeneralSettings.AmllTtmlDbBaseUrl}/{AmllTTmlDB.QueryPrefix}/{rawLyricFile}";
var url = $"{_settingsService.AppSettings.GeneralSettings.AmllTtmlDbBaseUrl}/{Constants.AmllTTmlDB.QueryPrefix}/{rawLyricFile}";
lyricsSearchResult.Reference = url; lyricsSearchResult.Reference = url;
try try
{ {
// 下载写入歌词
using var response = await _amllTtmlDbHttpClient.GetAsync(url); using var response = await _amllTtmlDbHttpClient.GetAsync(url);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
{ {
return lyricsSearchResult; return lyricsSearchResult;
} }
string lyrics = await response.Content.ReadAsStringAsync(); string lyrics = await response.Content.ReadAsStringAsync();
lyricsSearchResult.Raw = lyrics; lyricsSearchResult.Raw = lyrics;
// 反查时长
if (bestNcmMusicId != null && lyricsSearchResult.Duration == null)
{
var tmp = await SearchQQNeteaseKugouAsync(
((SongInfo)songInfo.Clone()).WithSongId($"{ExtendedGenreFiled.NetEaseCloudMusicTrackID}{bestNcmMusicId}"),
Searchers.Netease);
lyricsSearchResult.Duration = tmp.Duration;
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
}
} }
catch catch
{ {
@@ -633,7 +648,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
} }
lyricsSearchResult.Title = result?.Title; lyricsSearchResult.Title = result?.Title;
lyricsSearchResult.Artist = string.Join(" / ", result?.Artists ?? []); lyricsSearchResult.Artist = result?.Artist;
lyricsSearchResult.Album = result?.Album; lyricsSearchResult.Album = result?.Album;
lyricsSearchResult.Duration = result?.DurationMs / 1000; lyricsSearchResult.Duration = result?.DurationMs / 1000;