feat: download playlist tracks in parallel batches (MAX_PARALLEL)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-02 18:03:29 +02:00
1 parent c1a740240a
commit 252d2d3071
1 file changed
+23 -17
+23 -17
View File
@@ -831,35 +831,41 @@ app.post('/api/download', async (req, res) => {
if (!plTracks.length) throw new Error('Keine Tracks übergeben');
log(jobId, `🎵 Playlist "${title}" — ${plTracks.length} Tracks`);
let done = 0, skipped = 0;
for (const track of plTracks) {
try {
const trackTitle = track.trackName || '';
const CONC = CONFIG.maxParallel || 2;
for (let i = 0; i < plTracks.length; i += CONC) {
const batch = plTracks.slice(i, i + CONC);
const batchResults = await Promise.allSettled(batch.map(async track => {
const trackTitle = track.trackName || '';
// subtitle gives "Artist1, Artist2" — use first for iTunes search, keep full for tags
const trackArtist = track.artistName || artist || '';
const trackArtist = track.artistName || artist || '';
const searchArtist = trackArtist.split(',')[0].trim();
// Always look up iTunes for correct album, artwork, year (Spotify flat format has none)
log(jobId, `📋 iTunes: "${trackArtist} – ${trackTitle}"`);
const itunes = await lookupItunesTrackMeta(searchArtist, trackTitle);
const result = await downloadTrack({
return downloadTrack({
jobId,
artist: trackArtist,
albumArtist: trackArtist,
title: trackTitle,
album: itunes.album || track.collectionName || '',
year: itunes.year || (track.releaseDate ? new Date(track.releaseDate).getFullYear() : undefined),
trackNum: itunes.trackNum || track.trackNumber || undefined,
album: itunes.album || track.collectionName || '',
year: itunes.year || (track.releaseDate ? new Date(track.releaseDate).getFullYear() : undefined),
trackNum: itunes.trackNum || track.trackNumber || undefined,
totalTracks: itunes.totalTracks || undefined,
genre: itunes.genre || track.primaryGenreName || undefined,
artworkUrl: itunes.artworkUrl || track.artworkUrl100 || '',
genre: itunes.genre || track.primaryGenreName || undefined,
artworkUrl: itunes.artworkUrl || track.artworkUrl100 || '',
explicit: track.trackExplicitness === 'explicit',
durationMs: itunes.durationMs || track.durationMs || undefined,
durationMs: itunes.durationMs || track.durationMs || undefined,
});
if (result?.skipped) skipped++; else done++;
}));
for (let j = 0; j < batchResults.length; j++) {
const r = batchResults[j];
if (r.status === 'rejected') {
log(jobId, `⚠️ Fehler bei "${batch[j].trackName || '?'}": ${r.reason?.message || r.reason}`);
} else if (r.value?.skipped) {
skipped++;
} else {
done++;
}
updateJob(jobId, { progress: Math.round(((done + skipped) / plTracks.length) * 90) });
} catch(e) {
log(jobId, `⚠️ Fehler bei "${track.trackName || '?'}": ${e.message}`);
}
}
const allSkipped = skipped === plTracks.length;