From 0034b28f1a14e8f74e3b527836f593554f974f62 Mon Sep 17 00:00:00 2001 From: Tor Wingalen Date: Thu, 14 May 2026 19:07:53 +0200 Subject: [PATCH] Improve large playlist stability and Spotify item endpoint --- src/backend/src/shared/spotify-api.service.ts | 2 +- .../src/track/track-search.processor.ts | 27 +++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/backend/src/shared/spotify-api.service.ts b/src/backend/src/shared/spotify-api.service.ts index 0d21b3b..c28e731 100644 --- a/src/backend/src/shared/spotify-api.service.ts +++ b/src/backend/src/shared/spotify-api.service.ts @@ -168,7 +168,7 @@ export class SpotifyApiService { ); const response = await fetch( - `https://api.spotify.com/v1/playlists/${playlistId}/tracks?offset=${offset}&limit=100&fields=items(track(id,name,artists,preview_url,album(images))),next`, + `https://api.spotify.com/v1/playlists/${playlistId}/items?offset=${offset}&limit=100&fields=items(track(id,name,artists,preview_url,album(images))),next`, { headers: { Authorization: `Bearer ${accessToken}`, diff --git a/src/backend/src/track/track-search.processor.ts b/src/backend/src/track/track-search.processor.ts index 0336193..53ebd07 100644 --- a/src/backend/src/track/track-search.processor.ts +++ b/src/backend/src/track/track-search.processor.ts @@ -4,8 +4,18 @@ import { Job } from 'bullmq'; import { TrackService } from './track.service'; import { TrackEntity } from './track.entity'; +function getSearchDelayMs(): number { + const parsed = Number(process.env.YT_SEARCH_DELAY_MS || 5000); + + if (!Number.isFinite(parsed) || parsed < 0) { + return 5000; + } + + return Math.max(1000, Math.min(30000, Math.floor(parsed))); +} + @Processor('track-search-processor', { - concurrency: 2, + concurrency: 1, }) export class TrackSearchProcessor extends WorkerHost { private readonly logger = new Logger(TrackSearchProcessor.name); @@ -14,13 +24,14 @@ export class TrackSearchProcessor extends WorkerHost { super(); } - async process(job: Job): Promise { - if (!job.data?.id) { - this.logger.warn(`Skipping malformed search job ${job.id}`); - return; - } + async process(job: Job<{ id: number }, void, string>): Promise { + const delayMs = getSearchDelayMs(); - this.logger.debug(`Processing search job ${job.id} for track ${job.data.id}`); - await this.trackService.findOnYoutube(job.data); + this.logger.debug( + `Processing search job ${job.id} for track ${job.data.id}; pacing ${delayMs}ms`, + ); + + await new Promise((res) => setTimeout(res, delayMs)); + await this.trackService.findOnYoutube({ id: job.data.id } as TrackEntity); } }