musicbrainz-api
Advanced tools
Comparing version 0.11.0 to 0.12.0
@@ -149,2 +149,7 @@ export { XmlMetadata } from './xml/xml-metadata'; | ||
/** | ||
* Lookup series | ||
* @param seriesId Series MBID | ||
*/ | ||
lookupSeries(seriesId: string): Promise<mb.ISeries>; | ||
/** | ||
* Lookup work | ||
@@ -151,0 +156,0 @@ * @param workId Work MBID |
@@ -38,3 +38,2 @@ "use strict"; | ||
const util_1 = require("util"); | ||
const retries = 3; | ||
const debug = Debug('musicbrainz-api'); | ||
@@ -105,31 +104,11 @@ class MusicBrainzApi { | ||
}; | ||
this.rateLimiter = new rate_limiter_1.RateLimiter(60, 50); | ||
this.rateLimiter = new rate_limiter_1.RateLimiter(15, 18); | ||
} | ||
async restGet(relUrl, query = {}, attempt = 1) { | ||
query.fmt = 'json'; | ||
let response; | ||
await this.rateLimiter.limit(); | ||
do { | ||
response = await got_1.default.get('ws/2' + relUrl, Object.assign({ searchParams: query, responseType: 'json' }, this.options)); | ||
if (response.statusCode !== 503) | ||
break; | ||
debug('Rate limiter kicked in, slowing down...'); | ||
await rate_limiter_1.RateLimiter.sleep(500); | ||
} while (true); | ||
switch (response.statusCode) { | ||
case http_status_codes_1.StatusCodes.OK: | ||
return response.body; | ||
case http_status_codes_1.StatusCodes.BAD_REQUEST: | ||
case http_status_codes_1.StatusCodes.NOT_FOUND: | ||
throw new Error(`Got response status ${response.statusCode}: ${(0, http_status_codes_1.getReasonPhrase)(response.status)}`); | ||
case http_status_codes_1.StatusCodes.SERVICE_UNAVAILABLE: // 503 | ||
default: | ||
const msg = `Got response status ${response.statusCode} on attempt #${attempt} (${(0, http_status_codes_1.getReasonPhrase)(response.status)})`; | ||
debug(msg); | ||
if (attempt < retries) { | ||
return this.restGet(relUrl, query, attempt + 1); | ||
} | ||
else | ||
throw new Error(msg); | ||
} | ||
const response = await got_1.default.get('ws/2' + relUrl, Object.assign(Object.assign({}, this.options), { searchParams: query, responseType: 'json', retry: { | ||
limit: 10 | ||
} })); | ||
return response.body; | ||
} | ||
@@ -222,2 +201,9 @@ // ----------------------------------------------------------------------------------------------------------------- | ||
/** | ||
* Lookup series | ||
* @param seriesId Series MBID | ||
*/ | ||
lookupSeries(seriesId) { | ||
return this.lookupEntity('series', seriesId); | ||
} | ||
/** | ||
* Lookup work | ||
@@ -366,6 +352,6 @@ * @param workId Work MBID | ||
await this.rateLimiter.limit(); | ||
const response = await got_1.default.post(path, Object.assign({ searchParams: { client: clientId }, headers: { | ||
const response = await got_1.default.post(path, Object.assign(Object.assign({}, this.options), { searchParams: { client: clientId }, headers: { | ||
authorization: digest, | ||
'Content-Type': 'application/xml' | ||
}, body: postData, throwHttpErrors: false }, this.options)); | ||
}, body: postData, throwHttpErrors: false })); | ||
if (response.statusCode === http_status_codes_1.StatusCodes.UNAUTHORIZED) { | ||
@@ -402,5 +388,5 @@ // Respond to digest challenge | ||
}; | ||
const response = await got_1.default.post('login', Object.assign({ followRedirect: false, searchParams: { | ||
const response = await got_1.default.post('login', Object.assign(Object.assign({}, this.options), { followRedirect: false, searchParams: { | ||
returnto: redirectUri | ||
}, form: formData }, this.options)); | ||
}, form: formData })); | ||
const success = response.statusCode === http_status_codes_1.StatusCodes.MOVED_TEMPORARILY && response.headers.location === redirectUri; | ||
@@ -417,5 +403,5 @@ if (success) { | ||
const redirectUri = '/success'; | ||
const response = await got_1.default.get('logout', Object.assign({ followRedirect: false, searchParams: { | ||
const response = await got_1.default.get('logout', Object.assign(Object.assign({}, this.options), { followRedirect: false, searchParams: { | ||
returnto: redirectUri | ||
} }, this.options)); | ||
} })); | ||
const success = response.statusCode === http_status_codes_1.StatusCodes.MOVED_TEMPORARILY && response.headers.location === redirectUri; | ||
@@ -441,3 +427,3 @@ if (success && this.session) { | ||
formData.remember_me = 1; | ||
const response = await got_1.default.post(`${entity}/${mbid}/edit`, Object.assign({ form: formData, followRedirect: false }, this.options)); | ||
const response = await got_1.default.post(`${entity}/${mbid}/edit`, Object.assign(Object.assign({}, this.options), { form: formData, followRedirect: false })); | ||
if (response.statusCode === http_status_codes_1.StatusCodes.OK) | ||
@@ -541,3 +527,3 @@ throw new Error(`Failed to submit form data`); | ||
async getSession() { | ||
const response = await got_1.default.get('login', Object.assign({ followRedirect: false, responseType: 'text' }, this.options)); | ||
const response = await got_1.default.get('login', Object.assign(Object.assign({}, this.options), { followRedirect: false, responseType: 'text' })); | ||
return { | ||
@@ -544,0 +530,0 @@ csrf: MusicBrainzApi.fetchCsrf(response.body) |
@@ -223,2 +223,8 @@ import DateTimeFormat = Intl.DateTimeFormat; | ||
} | ||
export interface ISeries extends IEntity { | ||
name: string; | ||
type: string; | ||
disambiguation: string; | ||
'type-id': string; | ||
} | ||
export interface IUrl extends IEntity { | ||
@@ -225,0 +231,0 @@ id: string; |
@@ -6,4 +6,4 @@ export declare class RateLimiter { | ||
private readonly period; | ||
constructor(period: number, maxCalls: number); | ||
constructor(maxCalls: number, period: number); | ||
limit(): Promise<void>; | ||
} |
@@ -10,5 +10,6 @@ "use strict"; | ||
} | ||
constructor(period, maxCalls) { | ||
constructor(maxCalls, period) { | ||
this.maxCalls = maxCalls; | ||
this.queue = []; | ||
debug(`Rate limiter initialized with max ${maxCalls} calls in ${period} seconds.`); | ||
this.period = 1000 * period; | ||
@@ -22,2 +23,3 @@ } | ||
} | ||
// debug(`Current rate is ${this.queue.length} per ${this.period / 1000} sec`); | ||
if (this.queue.length >= this.maxCalls) { | ||
@@ -24,0 +26,0 @@ const delay = this.queue[0] + this.period - now; |
{ | ||
"name": "musicbrainz-api", | ||
"version": "0.11.0", | ||
"version": "0.12.0", | ||
"description": "MusicBrainz API client for reading and submitting metadata", | ||
"main": "lib/musicbrainz-api", | ||
"types": "lib/musicbrainz-api", | ||
"main": "lib/index", | ||
"types": "lib/index", | ||
"author": { | ||
@@ -23,3 +23,9 @@ "name": "Borewit", | ||
"submit", | ||
"metabrainz" | ||
"metabrainz", | ||
"Cover Art Archive", | ||
"coverartarchive", | ||
"coverartarchive.org", | ||
"album art", | ||
"covers", | ||
"download covers" | ||
], | ||
@@ -26,0 +32,0 @@ "license": "MIT", |
@@ -118,2 +118,16 @@ [![Node.js CI](https://github.com/Borewit/musicbrainz-api/actions/workflows/nodejs-ci.yml/badge.svg)](https://github.com/Borewit/musicbrainz-api/actions/workflows/nodejs-ci.yml) | ||
### Lookup collection | ||
Lookup an instrument | ||
```js | ||
const collection = await mbApi.lookupCollection('de4fdfc4-53aa-458a-b463-8761cc7f5af8'); | ||
``` | ||
Lookup an event | ||
```js | ||
const event = await mbApi.lookupEvent('6d32c658-151e-45ec-88c4-fb8787524d61'); | ||
``` | ||
### Lookup instrument | ||
@@ -141,2 +155,6 @@ | ||
```js | ||
const place = await mbApi.lookupSeries('1ae6c9bc-2931-4d75-bee4-3dc53dfd246a'); | ||
``` | ||
The second argument can be used to pass [subqueries](https://wiki.musicbrainz.org/Development/XML_Web_Service/Version_2#Subqueries), which will return more (nested) information: | ||
@@ -480,2 +498,23 @@ ```js | ||
## Cover Art Archive API | ||
Implementation of the [Cover Art Archive API](https://musicbrainz.org/doc/Cover_Art_Archive/API). | ||
```js | ||
import {CoverArtArchiveApi} from 'musicbrainz-api'; | ||
coverArtArchiveApiClient.getReleaseCovers(releaseMbid).then(releaseCoverInfo => { | ||
console.log('Release cover info', releaseCoverInfo); | ||
}); | ||
coverArtArchiveApiClient.getReleaseCovers(releaseMbid, 'front').then(releaseCoverInfo => { | ||
console.log('Get best front cover', releaseCoverInfo); | ||
}); | ||
coverArtArchiveApiClient.getReleaseCovers(releaseMbid, 'back').then(releaseCoverInfo => { | ||
console.log('Get best back cover', releaseCoverInfo); | ||
}); | ||
``` | ||
## Compatibility | ||
@@ -482,0 +521,0 @@ |
81637
22
1815
521