Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d1c9029c5 | ||
|
|
fed3df7e36 | ||
|
|
971cadd8fa |
No files matched your search
@@ -83,7 +83,9 @@ function buildTitleKeywords(title) {
|
||||
.filter(w => w.length >= 3 && !VARIANT_WORDS.has(w.toLowerCase()) && !STOPWORDS.has(w.toLowerCase()));
|
||||
const main = words.sort((a, b) => b.length - a.length)[0] || '';
|
||||
const qualifier = lower.includes('mix') ? MIX_QUALIFIERS.find(q => lower.includes(q)) : null;
|
||||
const result = main ? [main] : [];
|
||||
// Make trailing 's' optional so "Whispers?" matches both "Whisper" and "Whispers" in yt-dlp regex
|
||||
const toPattern = w => (w.length >= 4 && w.endsWith('s')) ? w + '?' : w;
|
||||
const result = main ? [toPattern(main)] : [];
|
||||
if (qualifier && main.toLowerCase() !== qualifier) result.push(qualifier);
|
||||
return result;
|
||||
}
|
||||
@@ -272,7 +274,10 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t
|
||||
);
|
||||
});
|
||||
|
||||
const ytmUrl = `https://music.youtube.com/search?q=${encodeURIComponent(query)}`;
|
||||
// ¶ms= is YTM's "Songs only" filter (same as clicking "Titel" tab in the browser).
|
||||
// Without it yt-dlp gets a mixed feed of videos, channels, and playlists as well.
|
||||
const YTM_SONGS_PARAM = 'EgWKAQIIAWoKEAkQBRAKEAMQBA%3D%3D';
|
||||
const ytmUrl = `https://music.youtube.com/search?q=${encodeURIComponent(query)}¶ms=${YTM_SONGS_PARAM}`;
|
||||
// Snapshot files already in dir before we start — so hasAudio() only detects NEW files
|
||||
// from the current download attempt, not pre-existing files from parallel jobs.
|
||||
let snapBefore = new Set(fs.readdirSync(dir));
|
||||
|
||||
@@ -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;
|
||||
@@ -380,13 +382,21 @@ async function _createNdPlaylist(tracks, name) {
|
||||
const firstArtist = t.artistName ? t.artistName.split(/[,/•]/)[0].trim() : '';
|
||||
const artistQuery = firstArtist ? `${firstArtist} ${t.trackName}` : t.trackName;
|
||||
const r = await api('search3', `&query=${encodeURIComponent(artistQuery)}&songCount=20&albumCount=0&artistCount=0`);
|
||||
const songs = r.searchResult3?.song || [];
|
||||
// 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));
|
||||
let songs = r.searchResult3?.song || [];
|
||||
// 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 {
|
||||
const r2 = await api('search3', `&query=${encodeURIComponent(t.trackName)}&songCount=20&albumCount=0&artistCount=0`);
|
||||
songs = r2.searchResult3?.song || [];
|
||||
} catch { /* keep original songs */ }
|
||||
}
|
||||
// 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); }
|
||||
|
||||
Reference in new issue
Block a user