feat(同步功能): 改进网易云音乐歌曲在Navidrome中的搜索逻辑

重构searchSongInNavidrome函数,支持多种查询组合和重试机制
- 新增对歌曲名、艺术家、专辑的多维度查询
- 添加基于ID令牌的精确匹配
- 实现最多2次的重试机制
- 优化匹配逻辑,提高搜索成功率
This commit is contained in:
史悦
2026-01-13 09:09:58 +08:00
parent a2a366d34a
commit c6fb745b85

View File

@@ -443,20 +443,69 @@ function processSong(song) {
});
}
async function searchSongInNavidrome(filename) {
async function searchSongInNavidrome(song) {
try {
const result = await callSubsonicAPI('search3', { query: filename });
if (result.searchResult3?.song) {
const songs = Array.isArray(result.searchResult3.song)
? result.searchResult3.song
: [result.searchResult3.song];
const rawName = (song?.name || '').trim();
const rawArtist = (song?.artist || '').trim();
const rawAlbum = (song?.album || '').trim();
const idToken = song?.id ? `netease_${song.id}` : '';
for (const song of songs) {
if (song.title && song.path) {
return song.id;
const normalize = (str) => (str || '').toString().trim().toLowerCase();
const safeName = sanitizeFilename(rawName);
const safeArtist = sanitizeFilename(rawArtist);
const queries = [];
if (rawName && rawArtist) queries.push(`${rawName} ${rawArtist}`);
if (rawName) queries.push(rawName);
if (rawArtist && rawAlbum) queries.push(`${rawArtist} ${rawAlbum}`);
if (safeName) {
const fallbackName = safeArtist ? `${safeArtist} - ${safeName}` : safeName;
queries.push(idToken ? `${fallbackName} [${idToken}]` : fallbackName);
}
const seen = new Set();
const uniqueQueries = queries.filter(q => {
const key = normalize(q);
if (!key || seen.has(key)) return false;
seen.add(key);
return true;
});
const MAX_ATTEMPTS = 2;
for (const query of uniqueQueries) {
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
const result = await callSubsonicAPI('search3', { query });
if (result.searchResult3?.song) {
const songs = Array.isArray(result.searchResult3.song)
? result.searchResult3.song
: [result.searchResult3.song];
if (idToken) {
const byPath = songs.find(s => typeof s.path === 'string' && s.path.includes(idToken));
if (byPath) return byPath.id;
}
const byTitleArtist = songs.find(s =>
normalize(s.title) === normalize(rawName) &&
normalize(s.artist) === normalize(rawArtist)
);
if (byTitleArtist) return byTitleArtist.id;
if (songs.length === 1) {
const only = songs[0];
if (normalize(only.title) === normalize(rawName)) {
return only.id;
}
}
}
if (attempt < MAX_ATTEMPTS) {
await new Promise(resolve => setTimeout(resolve, 1500));
}
}
}
return null;
} catch (error) {
console.error('Search song error:', error.message);
@@ -591,11 +640,7 @@ async function syncPlaylist(playlist, cachedInfo = null) {
if (playlist.songMapping && playlist.songMapping[neteaseSongId]) {
navidromeSongId = playlist.songMapping[neteaseSongId];
} else {
const safeName = sanitizeFilename(song.name);
const safeArtist = sanitizeFilename(song.artist);
const filename = `${safeArtist} - ${safeName} [netease_${neteaseSongId}]`;
navidromeSongId = await searchSongInNavidrome(filename);
navidromeSongId = await searchSongInNavidrome(song);
if (navidromeSongId) {
newSongIds.push(navidromeSongId);