mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
fix: fill Duration (via searching NCM) for amll-ttml-db lyrics source; improve matching system
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
// 差距 <= 3秒:100% 相似
|
// 差距 <= 1 秒:100 % 相似
|
||||||
// 差距 >= 20秒:0% 相似
|
// 差距 >= 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;
|
||||||
|
|||||||
@@ -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++)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user