From fed3df7e364a847def3752c99fd98c547360125e Mon Sep 17 00:00:00 2001 From: Honey Date: Sat, 2 May 2026 15:33:01 +0200 Subject: [PATCH] fix: normalize diacritics in playlist artist matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit normD strips accents/special chars before comparing artist first-word so "Kløn" (iTunes) matches "Klon" (embedded tag) → "klon" === "klon" Co-Authored-By: Claude Sonnet 4.6 --- src/ui.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ui.js b/src/ui.js index 4befad5..8a6ea64 100644 --- a/src/ui.js +++ b/src/ui.js @@ -371,7 +371,9 @@ async function _createNdPlaylist(tracks, name) { toast('🎵 Erstelle Navidrome-Playlist…'); const songIds = []; const missing = []; - const norm = s => s?.toLowerCase().trim() || ''; + // Normalize: lowercase, trim, strip diacritics (ø→o, ä→a, etc.) for fuzzy matching + const norm = s => s?.toLowerCase().trim() || ''; + const normD = s => norm(s).normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[^\w\s]/g, ''); for (const t of tracks) { if (!t.trackName) continue; const missingLabel = t.artistName ? `${t.artistName} – ${t.trackName}` : t.trackName; @@ -381,7 +383,7 @@ async function _createNdPlaylist(tracks, name) { const artistQuery = firstArtist ? `${firstArtist} ${t.trackName}` : t.trackName; const r = await api('search3', `&query=${encodeURIComponent(artistQuery)}&songCount=20&albumCount=0&artistCount=0`); let songs = r.searchResult3?.song || []; - // If the artist+title query returned no title match (e.g. special chars in artist like "Kløn"), + // If artist+title query returned no title match (e.g. special chars in artist like "Kløn"), // retry with title-only so the track can still be found if (!songs.some(s => norm(s.title) === norm(t.trackName))) { try { @@ -389,12 +391,12 @@ async function _createNdPlaylist(tracks, name) { songs = r2.searchResult3?.song || []; } catch { /* keep original songs */ } } - // Strip trailing punctuation from first word so "Kløn," matches "Kløn" - const firstWord = s => norm(s).split(/[\s,/•]+/)[0].replace(/\W+$/, ''); - const match = songs.find(s => - norm(s.title) === norm(t.trackName) && - firstWord(s.artist) === firstWord(firstArtist || t.artistName) - ) || songs.find(s => norm(s.title) === norm(t.trackName)); + // firstWordD: first word of artist, diacritics stripped — "Kløn" and "Klon" both → "klon" + const firstWordD = s => normD(s).split(/\s+/)[0]; + const titleMatch = s => norm(s.title) === norm(t.trackName); + const artistMatch = s => firstWordD(s.artist) === firstWordD(firstArtist || t.artistName); + const match = songs.find(s => titleMatch(s) && artistMatch(s)) + || songs.find(s => titleMatch(s)); if (match) songIds.push(match.id); else missing.push(missingLabel); } catch { missing.push(missingLabel); }