fix: 429 retry, safety counter, local file filter in Spotify pagination

This commit is contained in:
Honey committed 2026-05-09 20:58:16 +02:00
1 parent b7f09058a9
commit 7c1c444bf2
2 files changed
+22 -6

No files matched your search

+21 -6
View File
@@ -94,33 +94,47 @@ async function fetchSpotifyToken() {
async function _spApi(path, token) { async function _spApi(path, token) {
const url = path.startsWith('http') ? path : `https://api.spotify.com/v1${path}`; const url = path.startsWith('http') ? path : `https://api.spotify.com/v1${path}`;
for (let attempt = 0; attempt < 4; attempt++) {
const r = await fetch(url, { const r = await fetch(url, {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
signal: AbortSignal.timeout(10000), signal: AbortSignal.timeout(10000),
}); });
if (r.status === 429) {
const wait = (parseInt(r.headers.get('Retry-After') || '5', 10) + 1) * 1000;
await new Promise(res => setTimeout(res, wait));
continue;
}
if (!r.ok) throw new Error(`Spotify API Fehler ${r.status}: ${path}`); if (!r.ok) throw new Error(`Spotify API Fehler ${r.status}: ${path}`);
return r.json(); return r.json();
}
throw new Error('Spotify API rate-limited — bitte kurz warten und erneut versuchen');
} }
async function _spAllPlaylistTracks(id, token) { async function _spAllPlaylistTracks(id, token) {
const tracks = []; const tracks = [];
let skippedLocal = 0;
let path = `/playlists/${id}/tracks?limit=100`; let path = `/playlists/${id}/tracks?limit=100`;
while (path) { let pages = 0;
while (path && pages++ < 100) {
const page = await _spApi(path, token); const page = await _spApi(path, token);
for (const item of page.items || []) { for (const item of page.items || []) {
if (item.track?.name) tracks.push(item.track); const t = item.track;
if (!t || !t.id) continue;
if (t.is_local) { skippedLocal++; continue; }
tracks.push(t);
} }
path = page.next || null; path = page.next || null;
} }
return tracks; return { tracks, skippedLocal };
} }
async function _spAllAlbumTracks(id, token) { async function _spAllAlbumTracks(id, token) {
const tracks = []; const tracks = [];
let path = `/albums/${id}/tracks?limit=50`; let path = `/albums/${id}/tracks?limit=50`;
while (path) { let pages = 0;
while (path && pages++ < 100) {
const page = await _spApi(path, token); const page = await _spApi(path, token);
tracks.push(...(page.items || [])); tracks.push(...(page.items || []).filter(t => t?.id));
path = page.next || null; path = page.next || null;
} }
return tracks; return tracks;
@@ -232,13 +246,14 @@ app.post('/api/spotify/resolve', async (req, res) => {
const pl = await _spApi(`/playlists/${id}?fields=name,description,owner,images`, token); const pl = await _spApi(`/playlists/${id}?fields=name,description,owner,images`, token);
const owner = pl.owner?.display_name || ''; const owner = pl.owner?.display_name || '';
const artUrl = _spArtwork(pl); const artUrl = _spArtwork(pl);
const trackItems = await _spAllPlaylistTracks(id, token); const { tracks: trackItems, skippedLocal } = await _spAllPlaylistTracks(id, token);
return res.json({ return res.json({
type: 'playlist', type: 'playlist',
name: pl.name || '', name: pl.name || '',
artist: owner, artist: owner,
artworkUrl: artUrl, artworkUrl: artUrl,
description: pl.description || '', description: pl.description || '',
skippedLocal,
tracks: trackItems.map(t => _spTrackItem(t, null, null, null)), tracks: trackItems.map(t => _spTrackItem(t, null, null, null)),
}); });
} }
+1
View File
@@ -51,6 +51,7 @@ export async function resolveSpotifyLink() {
} }
const data = await res.json(); const data = await res.json();
window._spotifyResolved = data; window._spotifyResolved = data;
if (data.skippedLocal > 0) toast(`${data.skippedLocal} lokale Datei(en) übersprungen (kein Stream verfügbar)`);
_renderSpotifyPreview(data); _renderSpotifyPreview(data);
} catch(e) { } catch(e) {
inner.innerHTML = `<div style="color:var(--danger);padding:16px;background:var(--surface);border-radius:10px">${esc(e.message)}</div>`; inner.innerHTML = `<div style="color:var(--danger);padding:16px;background:var(--surface);border-radius:10px">${esc(e.message)}</div>`;