musicbrainz-api 0.10.3 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,7 +25,7 @@ We are looking into making this package usable in the browser as well.
25
25
 
26
26
  ## Before using this library
27
27
 
28
- MusicBrainz asks that you [identifying your application](https://wiki.musicbrainz.org/Development/XML_Web_Service/Version_2#User%20Data) by filling in the ['User-Agent' Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent).
28
+ MusicBrainz asks that you to [identify your application](https://wiki.musicbrainz.org/Development/XML_Web_Service/Version_2#User%20Data) by filling in the ['User-Agent' Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent).
29
29
  By passing `appName`, `appVersion`, `appMail` musicbrainz-api takes care of that.
30
30
 
31
31
  ## Submitting metadata
@@ -115,6 +115,20 @@ Lookup an `artist` and include their `releases`, `release-groups` and `aliases`
115
115
  const artist = await mbApi.lookupArtist('ab2528d9-719f-4261-8098-21849222a0f2');
116
116
  ```
117
117
 
118
+ ### Lookup collection
119
+
120
+ Lookup an instrument
121
+
122
+ ```js
123
+ const collection = await mbApi.lookupCollection('de4fdfc4-53aa-458a-b463-8761cc7f5af8');
124
+ ```
125
+
126
+ Lookup an event
127
+
128
+ ```js
129
+ const event = await mbApi.lookupEvent('6d32c658-151e-45ec-88c4-fb8787524d61');
130
+ ```
131
+
118
132
  ### Lookup instrument
119
133
 
120
134
  Lookup an instrument
@@ -137,6 +151,10 @@ const label = await mbApi.lookupLabel('25dda9f9-f069-4898-82f0-59330a106c7f');
137
151
  const place = await mbApi.lookupPlace('e6cfb74d-d69b-44c3-b890-1b3f509816e4');
138
152
  ```
139
153
 
154
+ ```js
155
+ const place = await mbApi.lookupSeries('1ae6c9bc-2931-4d75-bee4-3dc53dfd246a');
156
+ ```
157
+
140
158
  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:
141
159
  ```js
142
160
  const artist = await mbApi.lookupArtist('ab2528d9-719f-4261-8098-21849222a0f2', ['releases', 'recordings', 'url-rels']);
@@ -475,6 +493,27 @@ assert.isTrue(succeed, 'Login successful');
475
493
  await mbApi.addSpotifyIdToRecording(recording, '2AMysGXOe0zzZJMtH3Nizb');
476
494
  ```
477
495
 
496
+ ## Cover Art Archive API
497
+
498
+ Implementation of the [Cover Art Archive API](https://musicbrainz.org/doc/Cover_Art_Archive/API).
499
+
500
+ ```js
501
+ import {CoverArtArchiveApi} from 'musicbrainz-api';
502
+
503
+ coverArtArchiveApiClient.getReleaseCovers(releaseMbid).then(releaseCoverInfo => {
504
+ console.log('Release cover info', releaseCoverInfo);
505
+ });
506
+
507
+ coverArtArchiveApiClient.getReleaseCovers(releaseMbid, 'front').then(releaseCoverInfo => {
508
+ console.log('Get best front cover', releaseCoverInfo);
509
+ });
510
+
511
+ coverArtArchiveApiClient.getReleaseCovers(releaseMbid, 'back').then(releaseCoverInfo => {
512
+ console.log('Get best back cover', releaseCoverInfo);
513
+ });
514
+
515
+ ```
516
+
478
517
  ## Compatibility
479
518
 
480
519
  The JavaScript in runtime is compliant with [ECMAScript 2017 (ES8)](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017).
@@ -0,0 +1,31 @@
1
+ export type CovertType = 'Front' | 'Back' | 'Booklet' | 'Medium' | 'Obi' | 'Spine' | 'Track' | 'Tray' | 'Sticker' | 'Poster' | 'Liner' | 'Watermark' | 'Raw/Unedited' | 'Matrix/Runout' | 'Top' | 'Bottom' | 'Other';
2
+ export interface IImage {
3
+ types: CovertType[];
4
+ front: boolean;
5
+ back: boolean;
6
+ edit: number;
7
+ image: string;
8
+ comment: string;
9
+ approved: boolean;
10
+ id: string;
11
+ thumbnails: {
12
+ large: string;
13
+ small: string;
14
+ '250': string;
15
+ '500'?: string;
16
+ '1200'?: string;
17
+ };
18
+ }
19
+ export interface ICoverInfo {
20
+ images: IImage[];
21
+ release: string;
22
+ }
23
+ export declare class CoverArtArchiveApi {
24
+ private host;
25
+ private getJson;
26
+ /**
27
+ *
28
+ * @param releaseId MusicBrainz Release MBID
29
+ */
30
+ getReleaseCovers(releaseId: string, coverType?: 'front' | 'back'): Promise<ICoverInfo>;
31
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CoverArtArchiveApi = void 0;
4
+ /* eslint-disable-next-line */
5
+ const got_1 = require("got");
6
+ class CoverArtArchiveApi {
7
+ constructor() {
8
+ this.host = 'coverartarchive.org';
9
+ }
10
+ async getJson(path) {
11
+ const response = await got_1.default.get('https://' + this.host + path, {
12
+ headers: {
13
+ Accept: `application/json`
14
+ },
15
+ responseType: 'json'
16
+ });
17
+ return response.body;
18
+ }
19
+ /**
20
+ *
21
+ * @param releaseId MusicBrainz Release MBID
22
+ */
23
+ async getReleaseCovers(releaseId, coverType) {
24
+ const path = ['release', releaseId];
25
+ if (coverType) {
26
+ path.push(coverType);
27
+ }
28
+ const info = await this.getJson('/' + path.join('/'));
29
+ // Hack to correct http addresses into https
30
+ if (info.release && info.release.startsWith('http:')) {
31
+ info.release = 'https' + info.release.substring(4);
32
+ }
33
+ return info;
34
+ }
35
+ }
36
+ exports.CoverArtArchiveApi = CoverArtArchiveApi;
37
+ //# sourceMappingURL=coverartarchive-api.js.map
@@ -12,10 +12,10 @@ export declare class DigestAuth {
12
12
  * If the algorithm directive's value is "MD5-sess", then HA1 is
13
13
  * HA1=MD5(MD5(username:realm:password):nonce:cnonce)
14
14
  */
15
- static ha1Compute(algorithm: any, user: any, realm: any, pass: any, nonce: any, cnonce: any): string;
15
+ static ha1Compute(algorithm: string, user: string, realm: string, pass: string, nonce: string, cnonce: string): string;
16
16
  hasAuth: boolean;
17
17
  sentAuth: boolean;
18
- bearerToken: string;
18
+ bearerToken: string | null;
19
19
  constructor(credentials: ICredentials);
20
20
  digest(method: string, path: string, authHeader: string): string;
21
21
  }
@@ -64,16 +64,16 @@ class DigestAuth {
64
64
  opaque: challenge.opaque
65
65
  };
66
66
  const parts = [];
67
- for (const k in authValues) {
68
- if (authValues[k]) {
69
- if (k === 'qop' || k === 'nc' || k === 'algorithm') {
70
- parts.push(k + '=' + authValues[k]);
67
+ Object.entries(authValues).forEach(([key, value]) => {
68
+ if (value) {
69
+ if (key === 'qop' || key === 'nc' || key === 'algorithm') {
70
+ parts.push(key + '=' + value);
71
71
  }
72
72
  else {
73
- parts.push(k + '="' + authValues[k] + '"');
73
+ parts.push(key + '="' + value + '"');
74
74
  }
75
75
  }
76
- }
76
+ });
77
77
  authHeader = 'Digest ' + parts.join(', ');
78
78
  this.sentAuth = true;
79
79
  return authHeader;
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './coverartarchive-api';
2
+ export * from './musicbrainz-api';
package/lib/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./coverartarchive-api"), exports);
18
+ __exportStar(require("./musicbrainz-api"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -42,13 +42,13 @@ export type ReleaseGroupIncludes = MiscIncludes | SubQueryIncludes | RelationsIn
42
42
  export type SeriesIncludes = MiscIncludes | RelationsIncludes;
43
43
  export type WorkIncludes = MiscIncludes | RelationsIncludes;
44
44
  export type UrlIncludes = RelationsIncludes;
45
- export interface IFormData {
45
+ export type IFormData = {
46
46
  [key: string]: string | number;
47
- }
47
+ };
48
48
  export interface IMusicBrainzConfig {
49
- botAccount?: {
50
- username: string;
51
- password: string;
49
+ botAccount: {
50
+ username?: string;
51
+ password?: string;
52
52
  };
53
53
  baseUrl?: string;
54
54
  appName?: string;
@@ -62,11 +62,12 @@ export interface IMusicBrainzConfig {
62
62
  */
63
63
  appContactInfo?: string;
64
64
  }
65
+ export interface ICsrfSession {
66
+ sessionKey: string;
67
+ token: string;
68
+ }
65
69
  export interface ISessionInformation {
66
- csrf: {
67
- sessionKey: string;
68
- token: string;
69
- };
70
+ csrf: ICsrfSession;
70
71
  loggedIn?: boolean;
71
72
  }
72
73
  export declare class MusicBrainzApi {
@@ -74,11 +75,8 @@ export declare class MusicBrainzApi {
74
75
  readonly config: IMusicBrainzConfig;
75
76
  private rateLimiter;
76
77
  private options;
77
- private session;
78
- static fetchCsrf(html: string): {
79
- sessionKey: string;
80
- token: string;
81
- };
78
+ private session?;
79
+ static fetchCsrf(html: string): ICsrfSession;
82
80
  private static fetchValue;
83
81
  private getCookies;
84
82
  constructor(_config?: IMusicBrainzConfig);
@@ -147,6 +145,11 @@ export declare class MusicBrainzApi {
147
145
  * @param inc Include: artist-credits, isrcs
148
146
  */
149
147
  lookupRecording(recordingId: string, inc?: RecordingIncludes[]): Promise<mb.IRecording>;
148
+ /**
149
+ * Lookup series
150
+ * @param seriesId Series MBID
151
+ */
152
+ lookupSeries(seriesId: string): Promise<mb.ISeries>;
150
153
  /**
151
154
  * Lookup work
152
155
  * @param workId Work MBID
@@ -30,11 +30,11 @@ Object.defineProperty(exports, "XmlRecording", { enumerable: true, get: function
30
30
  const digest_auth_1 = require("./digest-auth");
31
31
  const rate_limiter_1 = require("./rate-limiter");
32
32
  const mb = require("./musicbrainz.types");
33
+ /* eslint-disable-next-line */
33
34
  const got_1 = require("got");
34
- const tough = require("tough-cookie");
35
+ const tough_cookie_1 = require("tough-cookie");
35
36
  __exportStar(require("./musicbrainz.types"), exports);
36
37
  const util_1 = require("util");
37
- const retries = 3;
38
38
  const debug = Debug('musicbrainz-api');
39
39
  class MusicBrainzApi {
40
40
  static escapeText(text) {
@@ -86,10 +86,11 @@ class MusicBrainzApi {
86
86
  }
87
87
  constructor(_config) {
88
88
  this.config = {
89
- baseUrl: 'https://musicbrainz.org'
89
+ baseUrl: 'https://musicbrainz.org',
90
+ botAccount: {}
90
91
  };
91
92
  Object.assign(this.config, _config);
92
- const cookieJar = new tough.CookieJar();
93
+ const cookieJar = new tough_cookie_1.CookieJar();
93
94
  this.getCookies = (0, util_1.promisify)(cookieJar.getCookies.bind(cookieJar));
94
95
  this.options = {
95
96
  prefixUrl: this.config.baseUrl,
@@ -97,37 +98,17 @@ class MusicBrainzApi {
97
98
  headers: {
98
99
  'User-Agent': `${this.config.appName}/${this.config.appVersion} ( ${this.config.appContactInfo} )`
99
100
  },
100
- cookieJar
101
+ cookieJar: cookieJar
101
102
  };
102
- this.rateLimiter = new rate_limiter_1.RateLimiter(60, 50);
103
+ this.rateLimiter = new rate_limiter_1.RateLimiter(15, 18);
103
104
  }
104
105
  async restGet(relUrl, query = {}, attempt = 1) {
105
106
  query.fmt = 'json';
106
- let response;
107
107
  await this.rateLimiter.limit();
108
- do {
109
- response = await got_1.default.get('ws/2' + relUrl, Object.assign({ searchParams: query, responseType: 'json' }, this.options));
110
- if (response.statusCode !== 503)
111
- break;
112
- debug('Rate limiter kicked in, slowing down...');
113
- await rate_limiter_1.RateLimiter.sleep(500);
114
- } while (true);
115
- switch (response.statusCode) {
116
- case http_status_codes_1.StatusCodes.OK:
117
- return response.body;
118
- case http_status_codes_1.StatusCodes.BAD_REQUEST:
119
- case http_status_codes_1.StatusCodes.NOT_FOUND:
120
- throw new Error(`Got response status ${response.statusCode}: ${(0, http_status_codes_1.getReasonPhrase)(response.status)}`);
121
- case http_status_codes_1.StatusCodes.SERVICE_UNAVAILABLE: // 503
122
- default:
123
- const msg = `Got response status ${response.statusCode} on attempt #${attempt} (${(0, http_status_codes_1.getReasonPhrase)(response.status)})`;
124
- debug(msg);
125
- if (attempt < retries) {
126
- return this.restGet(relUrl, query, attempt + 1);
127
- }
128
- else
129
- throw new Error(msg);
130
- }
108
+ const response = await got_1.default.get('ws/2' + relUrl, Object.assign(Object.assign({}, this.options), { searchParams: query, responseType: 'json', retry: {
109
+ limit: 10
110
+ } }));
111
+ return response.body;
131
112
  }
132
113
  // -----------------------------------------------------------------------------------------------------------------
133
114
  // Lookup functions
@@ -214,6 +195,13 @@ class MusicBrainzApi {
214
195
  lookupRecording(recordingId, inc = []) {
215
196
  return this.lookupEntity('recording', recordingId, inc);
216
197
  }
198
+ /**
199
+ * Lookup series
200
+ * @param seriesId Series MBID
201
+ */
202
+ lookupSeries(seriesId) {
203
+ return this.lookupEntity('series', seriesId);
204
+ }
217
205
  /**
218
206
  * Lookup work
219
207
  * @param workId Work MBID
@@ -353,15 +341,15 @@ class MusicBrainzApi {
353
341
  const clientId = `${this.config.appName.replace(/-/g, '.')}-${this.config.appVersion}`;
354
342
  const path = `ws/2/${entity}/`;
355
343
  // Get digest challenge
356
- let digest = null;
344
+ let digest;
357
345
  let n = 1;
358
346
  const postData = xmlMetadata.toXml();
359
347
  do {
360
348
  await this.rateLimiter.limit();
361
- const response = await got_1.default.post(path, Object.assign({ searchParams: { client: clientId }, headers: {
349
+ const response = await got_1.default.post(path, Object.assign(Object.assign({}, this.options), { searchParams: { client: clientId }, headers: {
362
350
  authorization: digest,
363
351
  'Content-Type': 'application/xml'
364
- }, body: postData, throwHttpErrors: false }, this.options));
352
+ }, body: postData, throwHttpErrors: false }));
365
353
  if (response.statusCode === http_status_codes_1.StatusCodes.UNAUTHORIZED) {
366
354
  // Respond to digest challenge
367
355
  const auth = new digest_auth_1.DigestAuth(this.config.botAccount);
@@ -384,7 +372,7 @@ class MusicBrainzApi {
384
372
  }
385
373
  }
386
374
  }
387
- this.session = await this.getSession(this.config.baseUrl);
375
+ this.session = await this.getSession();
388
376
  const redirectUri = '/success';
389
377
  const formData = {
390
378
  username: this.config.botAccount.username,
@@ -393,9 +381,9 @@ class MusicBrainzApi {
393
381
  csrf_token: this.session.csrf.token,
394
382
  remember_me: 1
395
383
  };
396
- const response = await got_1.default.post('login', Object.assign({ followRedirect: false, searchParams: {
384
+ const response = await got_1.default.post('login', Object.assign(Object.assign({}, this.options), { followRedirect: false, searchParams: {
397
385
  returnto: redirectUri
398
- }, form: formData }, this.options));
386
+ }, form: formData }));
399
387
  const success = response.statusCode === http_status_codes_1.StatusCodes.MOVED_TEMPORARILY && response.headers.location === redirectUri;
400
388
  if (success) {
401
389
  this.session.loggedIn = true;
@@ -407,11 +395,11 @@ class MusicBrainzApi {
407
395
  */
408
396
  async logout() {
409
397
  const redirectUri = '/success';
410
- const response = await got_1.default.get('logout', Object.assign({ followRedirect: false, searchParams: {
398
+ const response = await got_1.default.get('logout', Object.assign(Object.assign({}, this.options), { followRedirect: false, searchParams: {
411
399
  returnto: redirectUri
412
- } }, this.options));
400
+ } }));
413
401
  const success = response.statusCode === http_status_codes_1.StatusCodes.MOVED_TEMPORARILY && response.headers.location === redirectUri;
414
- if (success) {
402
+ if (success && this.session) {
415
403
  this.session.loggedIn = true;
416
404
  }
417
405
  return success;
@@ -424,13 +412,13 @@ class MusicBrainzApi {
424
412
  */
425
413
  async editEntity(entity, mbid, formData) {
426
414
  await this.rateLimiter.limit();
427
- this.session = await this.getSession(this.config.baseUrl);
415
+ this.session = await this.getSession();
428
416
  formData.csrf_session_key = this.session.csrf.sessionKey;
429
417
  formData.csrf_token = this.session.csrf.token;
430
418
  formData.username = this.config.botAccount.username;
431
419
  formData.password = this.config.botAccount.password;
432
420
  formData.remember_me = 1;
433
- const response = await got_1.default.post(`${entity}/${mbid}/edit`, Object.assign({ form: formData, followRedirect: false }, this.options));
421
+ const response = await got_1.default.post(`${entity}/${mbid}/edit`, Object.assign(Object.assign({}, this.options), { form: formData, followRedirect: false }));
434
422
  if (response.statusCode === http_status_codes_1.StatusCodes.OK)
435
423
  throw new Error(`Failed to submit form data`);
436
424
  if (response.statusCode === http_status_codes_1.StatusCodes.MOVED_TEMPORARILY)
@@ -444,15 +432,16 @@ class MusicBrainzApi {
444
432
  * @param editNote Edit note
445
433
  */
446
434
  async addUrlToRecording(recording, url2add, editNote = '') {
435
+ var _a;
447
436
  const formData = {};
448
437
  formData['edit-recording.name'] = recording.title; // Required
449
438
  formData['edit-recording.comment'] = recording.disambiguation;
450
439
  formData['edit-recording.make_votable'] = true;
451
440
  formData['edit-recording.url.0.link_type_id'] = url2add.linkTypeId;
452
441
  formData['edit-recording.url.0.text'] = url2add.text;
453
- for (const i in recording.isrcs) {
454
- formData[`edit-recording.isrcs.${i}`] = recording.isrcs[i];
455
- }
442
+ (_a = recording.isrcs) === null || _a === void 0 ? void 0 : _a.forEach((isrcs, i) => {
443
+ formData[`edit-recording.isrcs.${i}`] = isrcs;
444
+ });
456
445
  formData['edit-recording.edit_note'] = editNote;
457
446
  return this.editEntity('recording', recording.id, formData);
458
447
  }
@@ -527,8 +516,8 @@ class MusicBrainzApi {
527
516
  searchUrl(query) {
528
517
  return this.search('url', query);
529
518
  }
530
- async getSession(url) {
531
- const response = await got_1.default.get('login', Object.assign({ followRedirect: false, responseType: 'text' }, this.options));
519
+ async getSession() {
520
+ const response = await got_1.default.get('login', Object.assign(Object.assign({}, this.options), { followRedirect: false, responseType: 'text' }));
532
521
  return {
533
522
  csrf: MusicBrainzApi.fetchCsrf(response.body)
534
523
  };
@@ -33,7 +33,7 @@ export interface IArtist extends IEntity {
33
33
  disambiguation: string;
34
34
  'sort-name': string;
35
35
  'type-id'?: string;
36
- 'gender-id'?: any;
36
+ 'gender-id'?: string;
37
37
  'life-span'?: IPeriod;
38
38
  country?: string;
39
39
  ipis?: any[];
@@ -183,6 +183,7 @@ export interface IAreaList extends ISearchResult {
183
183
  }
184
184
  export interface IReleaseList extends ISearchResult {
185
185
  releases: IReleaseMatch[];
186
+ 'release-count': number;
186
187
  }
187
188
  export interface IReleaseGroupList extends ISearchResult {
188
189
  'release-groups': IReleaseGroupMatch[];
@@ -219,6 +220,12 @@ export interface ILabel extends IEntity {
219
220
  export interface IPlace extends IEntity {
220
221
  name: string;
221
222
  }
223
+ export interface ISeries extends IEntity {
224
+ name: string;
225
+ type: string;
226
+ disambiguation: string;
227
+ 'type-id': string;
228
+ }
222
229
  export interface IUrl extends IEntity {
223
230
  id: string;
224
231
  resource: string;
@@ -1,8 +1,8 @@
1
1
  export declare class RateLimiter {
2
2
  private maxCalls;
3
- static sleep(ms: any): Promise<void>;
3
+ static sleep(ms: number): Promise<void>;
4
4
  queue: number[];
5
5
  private readonly period;
6
- constructor(period: number, maxCalls: number);
6
+ constructor(maxCalls: number, period: number);
7
7
  limit(): Promise<void>;
8
8
  }
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RateLimiter = void 0;
4
- const Debug = require("debug");
5
- const debug = Debug('musicbrainz-api:rate-limiter');
4
+ const debug_1 = require("debug");
5
+ const debug = (0, debug_1.default)('musicbrainz-api:rate-limiter');
6
6
  class RateLimiter {
7
7
  static sleep(ms) {
8
8
  return new Promise(resolve => setTimeout(resolve, ms));
9
9
  }
10
- constructor(period, maxCalls) {
10
+ constructor(maxCalls, period) {
11
11
  this.maxCalls = maxCalls;
12
12
  this.queue = [];
13
+ debug(`Rate limiter initialized with max ${maxCalls} calls in ${period} seconds.`);
13
14
  this.period = 1000 * period;
14
15
  }
15
16
  async limit() {
@@ -18,6 +19,7 @@ class RateLimiter {
18
19
  while (this.queue.length > 0 && this.queue[0] < t0) {
19
20
  this.queue.shift();
20
21
  }
22
+ // debug(`Current rate is ${this.queue.length} per ${this.period / 1000} sec`);
21
23
  if (this.queue.length >= this.maxCalls) {
22
24
  const delay = this.queue[0] + this.period - now;
23
25
  debug(`Client side rate limiter activated: cool down for ${delay / 1000} s...`);
@@ -13,5 +13,5 @@ export declare class XmlIsrcList {
13
13
  id: string;
14
14
  };
15
15
  }[];
16
- };
16
+ } | null;
17
17
  }
@@ -8,7 +8,7 @@ export declare class XmlRecording {
8
8
  attrs: {
9
9
  id: string;
10
10
  };
11
- children: {
11
+ children: ({
12
12
  name: string;
13
13
  attrs: {
14
14
  count: number;
@@ -19,6 +19,6 @@ export declare class XmlRecording {
19
19
  id: string;
20
20
  };
21
21
  }[];
22
- }[];
22
+ } | null)[];
23
23
  };
24
24
  }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "musicbrainz-api",
3
- "version": "0.10.3",
3
+ "version": "0.12.0",
4
4
  "description": "MusicBrainz API client for reading and submitting metadata",
5
- "main": "lib/musicbrainz-api",
6
- "types": "lib/musicbrainz-api",
5
+ "main": "lib/index",
6
+ "types": "lib/index",
7
7
  "author": {
8
8
  "name": "Borewit",
9
9
  "url": "https://github.com/Borewit"
@@ -20,7 +20,13 @@
20
20
  "web",
21
21
  "service",
22
22
  "submit",
23
- "metabrainz"
23
+ "metabrainz",
24
+ "Cover Art Archive",
25
+ "coverartarchive",
26
+ "coverartarchive.org",
27
+ "album art",
28
+ "covers",
29
+ "download covers"
24
30
  ],
25
31
  "license": "MIT",
26
32
  "private": false,
@@ -45,13 +51,14 @@
45
51
  "json-stringify-safe": "^5.0.1",
46
52
  "jsontoxml": "^1.0.1",
47
53
  "source-map-support": "^0.5.16",
48
- "tough-cookie": "^4.0.0",
54
+ "tough-cookie": "^4.1.3",
49
55
  "uuid": "^9.0.0"
50
56
  },
51
57
  "devDependencies": {
52
58
  "@types/chai": "^4.3.0",
59
+ "@types/jsontoxml": "^1.0.5",
53
60
  "@types/mocha": "^9.0.0",
54
- "@types/node": "^18.6.1",
61
+ "@types/node": "^20.8.10",
55
62
  "@typescript-eslint/eslint-plugin": "^5.13.0",
56
63
  "@typescript-eslint/parser": "^5.13.0",
57
64
  "chai": "^4.2.0",
@@ -60,7 +67,7 @@
60
67
  "eslint-config-prettier": "^8.4.0",
61
68
  "eslint-import-resolver-typescript": "^3.3.0",
62
69
  "eslint-plugin-import": "^2.25.4",
63
- "eslint-plugin-jsdoc": "^40.1.0",
70
+ "eslint-plugin-jsdoc": "^46.8.2",
64
71
  "eslint-plugin-node": "^11.1.0",
65
72
  "eslint-plugin-unicorn": "^46.0.0",
66
73
  "mocha": "^9.0.1",
@@ -71,6 +78,10 @@
71
78
  "tslint": "^6.1.1",
72
79
  "typescript": "^5.0.2"
73
80
  },
81
+ "files": [
82
+ "lib/**/*.js",
83
+ "lib/**/*.d.ts"
84
+ ],
74
85
  "scripts": {
75
86
  "clean": "del-cli lib/**/*.js lib/**/*.js.map lib/**/*.d.ts test/**/*.js test/**/*.js.map",
76
87
  "compile-lib": "tsc -p lib",
@@ -1,16 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="CheckStyle-IDEA" serialisationVersion="2">
4
- <checkstyleVersion>10.5.0</checkstyleVersion>
5
- <scanScope>JavaOnly</scanScope>
6
- <copyLibs>true</copyLibs>
7
- <option name="thirdPartyClasspath" />
8
- <option name="activeLocationIds" />
9
- <option name="locations">
10
- <list>
11
- <ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
12
- <ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
13
- </list>
14
- </option>
15
- </component>
16
- </project>
@@ -1,7 +0,0 @@
1
- <component name="InspectionProjectProfileManager">
2
- <profile version="1.0">
3
- <option name="myName" value="Project Default" />
4
- <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
5
- <inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
6
- </profile>
7
- </component>
package/.idea/misc.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectRootManager">
4
- <output url="file://$PROJECT_DIR$/out" />
5
- </component>
6
- </project>
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/.idea/musicbrainz-api.iml" filepath="$PROJECT_DIR$/.idea/musicbrainz-api.iml" />
6
- </modules>
7
- </component>
8
- </project>
package/.idea/vcs.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="" vcs="Git" />
5
- </component>
6
- </project>