diff --git a/src/backend/common/infrastructure/Atomic.ts b/src/backend/common/infrastructure/Atomic.ts index d8ae64a7..a0f1dee9 100644 --- a/src/backend/common/infrastructure/Atomic.ts +++ b/src/backend/common/infrastructure/Atomic.ts @@ -291,7 +291,6 @@ export interface CacheConfigOptions { export interface MusicbrainzApiConfigData { url?: string - rateLimit?: [number, number] contact: string, apiKey?: string requestTimeout?: number diff --git a/src/backend/common/transforms/MusicbrainzTransformer.ts b/src/backend/common/transforms/MusicbrainzTransformer.ts index a288054f..e9749cb2 100644 --- a/src/backend/common/transforms/MusicbrainzTransformer.ts +++ b/src/backend/common/transforms/MusicbrainzTransformer.ts @@ -356,8 +356,8 @@ export default class MusicbrainzTransformer extends AtomicPartsTransformer = { + ...mbConfig, + hostname: u.url.hostname, + minRequestIntervalDuration: 1000, + lastRequest: dayjs().subtract(1000 + 1000, 'ms') + } if(mb === undefined) { const api = new MusicBrainzApi({ appName: 'multi-scrobbler', appVersion: version, appContactInfo: mbConfig.contact, baseUrl: u.url.toString(), - rateLimit: mbConfig.rateLimit ?? [1,1], preRequest: options.logUrl === true || isDebugMode() ? (method, url, headers) => { const cacheKey = this.asyncStore.getStore() ?? nanoid(); this.cache.set(`${cacheKey}-url`, `${method} - ${url}`); @@ -84,11 +91,17 @@ export class MusicbrainzApiClient extends AbstractApiClient { requestTimeout: mbConfig.requestTimeout ?? 6000, retryLimit: 2 }); - mbApis[u.url.hostname] = {api, ...mbConfig, hostname: u.url.hostname}; + mbApis[u.url.hostname] = { + ...mbApiConfig, + api, + }; mbMap.set(u.url.hostname, api); mb = api; } else if(mbApis[u.url.hostname] === undefined) { - mbApis[u.url.hostname] = {api: mb, ...mbConfig, hostname: u.url.hostname}; + mbApis[u.url.hostname] = { + ...mbApiConfig, + api: mb, + }; } } @@ -127,6 +140,17 @@ export class MusicbrainzApiClient extends AbstractApiClient { const triedHosts: string[] = []; while(!triedHosts.includes(apiConfig.hostname)) { + // keep track of last request init at and wait until at least 1 second since that + // to help prevent rate limiting + let waitTime = 0; + const sinceLast = dayjs().diff(apiConfig.lastRequest, 'ms'); + waitTime = Math.max(0, apiConfig.minRequestIntervalDuration - sinceLast); + apiConfig.lastRequest = dayjs().add(waitTime, 'ms'); + //this.logger.trace(`Waiting ${waitTime}ms to call ${apiConfig.hostname} request at ${apiConfig.lastRequest.toISOString()}`) + if(waitTime > 0) { + await sleep(waitTime); + } + try { const res = await this.callApiEndpoint(apiConfig.api, func, options); if(cacheKey !== undefined) {