diff --git a/Netease-sync/server.js b/Netease-sync/server.js index f51ccb8..d6f5c17 100644 --- a/Netease-sync/server.js +++ b/Netease-sync/server.js @@ -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]; - - for (const song of songs) { - if (song.title && song.path) { - return song.id; + const rawName = (song?.name || '').trim(); + const rawArtist = (song?.artist || '').trim(); + const rawAlbum = (song?.album || '').trim(); + const idToken = song?.id ? `netease_${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,12 +640,8 @@ 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);