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;
}
public SongInfo WithSongId(string value)
{
songInfo.SongId = value;
return songInfo;
}
public PlayHistoryItem? ToPlayHistoryItem(double actualPlayedMs)
{
if (songInfo == null) return null;

View File

@@ -10,27 +10,27 @@ namespace BetterLyrics.WinUI3.Helper
{
public static partial class MetadataComparer
{
private const double WeightTitle = 0.40;
private const double WeightArtist = 0.40;
private const double WeightTitle = 0.30;
private const double WeightArtist = 0.30;
private const double WeightAlbum = 0.10;
private const double WeightDuration = 0.10;
private const double WeightDuration = 0.30;
// JaroWinkler 适合短字符串匹配
private static readonly JaroWinkler _algo = new();
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)
{
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(
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;
@@ -42,7 +42,7 @@ namespace BetterLyrics.WinUI3.Helper
double titleScore = GetStringSimilarity(songInfo.Title, compareTitle);
double artistScore = GetStringSimilarity(songInfo.Artist, compareArtist);
double albumScore = GetStringSimilarity(songInfo.Album, compareAlbum);
double durationScore = GetDurationSimilarity(songInfo.DurationMs, compareDurationMs);
double durationScore = GetDurationSimilarity(songInfo.Duration, compareDuration);
totalScore = (titleScore * WeightTitle) +
(artistScore * WeightArtist) +
@@ -94,19 +94,18 @@ namespace BetterLyrics.WinUI3.Helper
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; // 远程没有时长数据,不匹配
double localSeconds = localMs / 1000.0;
double diff = Math.Abs(localSeconds - remoteSeconds.Value);
// 差距 <= 3100% 相似
// 差距 >= 200% 相似
// 差距 <= 1 100 % 相似
// 差距 >= 10 0 % 相似
// 中间线性插值
const double PerfectTolerance = 3.0;
const double MaxTolerance = 20.0;
const double PerfectTolerance = 1.0;
const double MaxTolerance = 10.0;
if (diff <= PerfectTolerance) return 1.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 TertiaryText { get; set; } = "";
public new string Text => PrimaryText;
public new int StartIndex = 0;
public LyricsLine()
{
for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++)

View File

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

View File

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

View File

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