perf(device-sync): parallelise getAlbum calls when syncing an artist source (#246)

fetchTracksForSource() iterated over an artist's albums with sequential
`await getAlbum(...)` inside a for-loop. A 50-album artist sync stalled
for ~7 seconds of round-trips before any device write started.

Switch to Promise.all(albums.map(...)). Same pattern ArtistDetail.tsx
already uses for "Play All" without issues — Navidrome handles parallel
getAlbum requests fine.

Per-album errors fall back to an empty song list rather than aborting
the whole sync.

Co-authored-by: Psychotoxical <dev@psysonic.app>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Frank Stellmacher
2026-04-21 21:37:52 +02:00
committed by GitHub
parent 9da1d643f7
commit 1ec27f9aff
+7 -3
View File
@@ -63,9 +63,13 @@ async function fetchTracksForSource(source: DeviceSyncSource): Promise<SubsonicS
if (source.type === 'playlist') { const { songs } = await getPlaylist(source.id); return songs; }
if (source.type === 'album') { const { songs } = await getAlbum(source.id); return songs; }
const { albums } = await getArtist(source.id);
const all: SubsonicSong[] = [];
for (const album of albums) { const { songs } = await getAlbum(album.id); all.push(...songs); }
return all;
// Parallel album fetches — Navidrome handles getAlbum requests in flight
// without serialising. Sequential awaits here multiplied a 50-album artist
// sync into 50 round-trips (~7 s blocking) before any device write started.
const results = await Promise.all(
albums.map(a => getAlbum(a.id).then(r => r.songs).catch(() => [] as SubsonicSong[])),
);
return results.flat();
}
/** Tracks that came from `calculate_sync_payload` may carry embedded playlist