fix: enforce Spotify rate limit cooldown server- and client-side

Server tracks _spRateLimitUntil and rejects retries instantly without
hitting Spotify, returning the remaining wait time. Retrying before the
window expired was resetting Spotify's timer (19s → 7s → 48s).

Frontend now shows a live countdown with a note not to retry early.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-12 20:22:10 +02:00
1 parent 5265aa1f33
commit d4c41a9f92
2 files changed
+34 -11

No files matched your search

+13 -4
View File
@@ -124,13 +124,21 @@ async function _resolveSpotifyViaSpotdl(url) {
});
}
async function _resolveSpotify(type, id, originalUrl) {
return await _resolveSpotifyViaSpotdl(originalUrl);
}
// Tracks when the Spotify rate limit window expires so retries never reach Spotify.
let _spRateLimitUntil = 0;
app.post('/api/spotify/resolve', async (req, res) => {
const { url } = req.body;
if (!url) return res.status(400).json({ error: 'url required' });
// Block retries until the cooldown the Spotify told us has actually elapsed.
const now = Date.now();
if (_spRateLimitUntil > now) {
const secs = Math.ceil((_spRateLimitUntil - now) / 1000);
console.log(`[spotify/resolve] cooldown active — ${secs}s remaining`);
return res.status(429).json({ error: 'RATE_LIMITED', retryAfter: secs });
}
try {
const { type, id } = parseSpotifyUrl(url);
console.log(`[spotify/resolve] ${type} ${id}`);
@@ -138,7 +146,7 @@ app.post('/api/spotify/resolve', async (req, res) => {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Zeitüberschreitung — Spotify API antwortet nicht (>120s)')), 120000)
);
const data = await Promise.race([_resolveSpotify(type, id, url), timeout]);
const data = await Promise.race([_resolveSpotifyViaSpotdl(url), timeout]);
res.json(data);
} catch(e) {
console.error('[spotify/resolve]', e.message);
@@ -147,6 +155,7 @@ app.post('/api/spotify/resolve', async (req, res) => {
}
if (e.message.startsWith('RATE_LIMITED:')) {
const secs = parseInt(e.message.split(':')[1], 10) || 30;
_spRateLimitUntil = Date.now() + secs * 1000;
return res.status(429).json({ error: 'RATE_LIMITED', retryAfter: secs });
}
res.status(500).json({ error: e.message });