From 4cbfa35655ed7fc58ab7604621bacec3c1e9a0b1 Mon Sep 17 00:00:00 2001 From: Honey Date: Fri, 19 Jun 2026 10:00:32 +0200 Subject: [PATCH] fix(backend): normalize iTunes lookup across all download paths - Track path: always fetch iTunes when durationMs/trackNum missing (not just genre) - Both paths: strip release suffixes from title before iTunes lookup - Log "Album:" instead of "Album aus Spotify:" (also fires for iTunes global search) - Log duration when fetched from iTunes so it's visible in job log Ensures all importers have consistent yt-dlp search filters (duration filter now always runs when iTunes finds the track). Co-Authored-By: Claude Sonnet 4.6 --- backend/novasonic-server.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index ecf25dc..4bddf8e 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -576,15 +576,19 @@ app.post('/api/download', async (req, res) => { if (extra.album) log(jobId, `📋 Album: "${extra.album}" (${extra.year || '?'})`); meta = { ...meta, ...extra, artworkUrl: spArt || extra.artworkUrl || '', genre: meta.genre || extra.genre || undefined }; } else { - log(jobId, `📋 Album aus Spotify: "${meta.album}"`); - // Spotify enrichment has no genre, totalTracks, or disc info — fetch from iTunes - if (!meta.genre || !meta.totalTracks) { - const genreMeta = await lookupItunesTrackMeta(searchArtist, title).catch(() => ({})); + log(jobId, `📋 Album: "${meta.album}"`); + // Always fetch iTunes when any field is missing — durationMs is required for the yt-dlp + // duration filter; without it any unrelated video that matches the title text gets downloaded. + if (!meta.genre || !meta.totalTracks || !meta.durationMs || !meta.trackNum) { + // Strip release-type suffixes before iTunes lookup so "recognize - Single" → "recognize" + const RELEASE_SUFFIX_RE = /\s*[-–]\s*(?:(?:official\s+)?(?:music\s+)?(?:single|ep|lp|album|ost|soundtrack|remaster(?:ed)?|deluxe(?:\s+edition)?|bonus\s+tracks?|b-?sides?|version|edit)\b.*|(?:20|19)\d{2})\s*$/i; + const itunesTitle = title.replace(RELEASE_SUFFIX_RE, '').trim() || title; + const genreMeta = await lookupItunesTrackMeta(searchArtist, itunesTitle).catch(() => ({})); if (!meta.genre && genreMeta.genre) { meta.genre = genreMeta.genre; log(jobId, `📋 Genre: "${genreMeta.genre}"`); } if (!meta.totalTracks && genreMeta.totalTracks) { meta.totalTracks = genreMeta.totalTracks; } if (!meta.discNum && genreMeta.discNum) { meta.discNum = genreMeta.discNum; } if (!meta.discTotal && genreMeta.discTotal) { meta.discTotal = genreMeta.discTotal; } - if (!meta.durationMs && genreMeta.durationMs) { meta.durationMs = genreMeta.durationMs; } + if (!meta.durationMs && genreMeta.durationMs) { meta.durationMs = genreMeta.durationMs; log(jobId, `📋 Duration: ${Math.round(genreMeta.durationMs/1000)}s`); } if (!meta.trackNum && genreMeta.trackNum) { meta.trackNum = genreMeta.trackNum; } } } @@ -651,6 +655,9 @@ app.post('/api/download', async (req, res) => { const batchResults = await Promise.allSettled(batch.map(async track => { const trackTitle = track.trackName || ''; const trackArtist = track.artistName || artist || ''; + // Strip release-type suffixes before iTunes lookup ("Song - Single" → "Song") + const RELEASE_SUFFIX_RE = /\s*[-–]\s*(?:(?:official\s+)?(?:music\s+)?(?:single|ep|lp|album|ost|soundtrack|remaster(?:ed)?|deluxe(?:\s+edition)?|bonus\s+tracks?|b-?sides?|version|edit)\b.*|(?:20|19)\d{2})\s*$/i; + const itunesTitle = trackTitle.replace(RELEASE_SUFFIX_RE, '').trim() || trackTitle; // If the frontend already enriched this track with iTunes data (collectionName + // trackNumber + durationMs all present), skip the re-lookup to avoid a different // iTunes result overriding the already-correct enriched metadata. @@ -660,7 +667,7 @@ app.post('/api/download', async (req, res) => { if (alreadyEnriched) { log(jobId, `📋 Frontend-Metadaten: "${trackArtist} – ${trackTitle}" (${track.collectionName})`); // Spotify enrichment copies album/trackNum/duration but not genre/totalTracks/disc — fetch those - const genreMeta = await lookupItunesTrackMeta(searchArtist, trackTitle).catch(() => ({})); + const genreMeta = await lookupItunesTrackMeta(searchArtist, itunesTitle).catch(() => ({})); itunes = { genre: genreMeta.genre, totalTracks: genreMeta.totalTracks, @@ -669,7 +676,7 @@ app.post('/api/download', async (req, res) => { }; } else { log(jobId, `📋 iTunes: "${trackArtist} – ${trackTitle}"`); - itunes = await lookupItunesTrackMeta(searchArtist, trackTitle); + itunes = await lookupItunesTrackMeta(searchArtist, itunesTitle); } // Prefer iTunes-resolved name (from server lookup or frontend enrichment) for sort tags / folders. // trackArtist stays as rawArtist for main tags and YTM search.