musicbrainz-api 0.14.0 → 0.16.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 +33 -13
- package/lib/coverartarchive-api.d.ts +18 -1
- package/lib/coverartarchive-api.js +30 -8
- package/lib/digest-auth.js +10 -10
- package/lib/musicbrainz-api.d.ts +4 -6
- package/lib/musicbrainz-api.js +20 -50
- package/lib/musicbrainz.types.d.ts +3 -3
- package/package.json +18 -26
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ const artist = await mbApi.lookup('artist', 'ab2528d9-719f-4261-8098-21849222a0f
|
|
|
98
98
|
### Browse artist
|
|
99
99
|
|
|
100
100
|
```js
|
|
101
|
-
const artists = await browse('artist', query);
|
|
101
|
+
const artists = await mbApi.browse('artist', query);
|
|
102
102
|
````
|
|
103
103
|
|
|
104
104
|
| Query argument | Query value |
|
|
@@ -112,7 +112,7 @@ const artists = await browse('artist', query);
|
|
|
112
112
|
|
|
113
113
|
### Browse collection
|
|
114
114
|
```js
|
|
115
|
-
const collections = await browse('collection', query);
|
|
115
|
+
const collections = await mbApi.browse('collection', query);
|
|
116
116
|
````
|
|
117
117
|
|
|
118
118
|
| Query argument | Query value |
|
|
@@ -130,7 +130,7 @@ const collections = await browse('collection', query);
|
|
|
130
130
|
|
|
131
131
|
### Browse events
|
|
132
132
|
```js
|
|
133
|
-
const events = await browse('event', query);
|
|
133
|
+
const events = await mbApi.browse('event', query);
|
|
134
134
|
````
|
|
135
135
|
|
|
136
136
|
| Query argument | Query value |
|
|
@@ -142,7 +142,7 @@ const events = await browse('event', query);
|
|
|
142
142
|
|
|
143
143
|
### Browse instruments
|
|
144
144
|
```js
|
|
145
|
-
const instruments = await browse('event', query);
|
|
145
|
+
const instruments = await mbApi.browse('event', query);
|
|
146
146
|
````
|
|
147
147
|
|
|
148
148
|
| Query argument | Query value |
|
|
@@ -151,7 +151,7 @@ const instruments = await browse('event', query);
|
|
|
151
151
|
|
|
152
152
|
### Browse labels
|
|
153
153
|
```js
|
|
154
|
-
const labels = await browse('label', query);
|
|
154
|
+
const labels = await mbApi.browse('label', query);
|
|
155
155
|
````
|
|
156
156
|
|
|
157
157
|
| Query argument | Query value |
|
|
@@ -162,7 +162,7 @@ const labels = await browse('label', query);
|
|
|
162
162
|
|
|
163
163
|
### Browse places
|
|
164
164
|
```js
|
|
165
|
-
const places = await browse('place', query);
|
|
165
|
+
const places = await mbApi.browse('place', query);
|
|
166
166
|
````
|
|
167
167
|
|
|
168
168
|
| Query argument | Query value |
|
|
@@ -172,7 +172,7 @@ const places = await browse('place', query);
|
|
|
172
172
|
|
|
173
173
|
### Browse recordings
|
|
174
174
|
```js
|
|
175
|
-
const recordings = await browse('recording', query);
|
|
175
|
+
const recordings = await mbApi.browse('recording', query);
|
|
176
176
|
````
|
|
177
177
|
|
|
178
178
|
| Query argument | Query value |
|
|
@@ -184,7 +184,7 @@ const recordings = await browse('recording', query);
|
|
|
184
184
|
|
|
185
185
|
### Browse releases
|
|
186
186
|
```js
|
|
187
|
-
const releases = await browse('release', query);
|
|
187
|
+
const releases = await mbApi.browse('release', query);
|
|
188
188
|
````
|
|
189
189
|
|
|
190
190
|
| Query argument | Query value |
|
|
@@ -202,7 +202,7 @@ const releases = await browse('release', query);
|
|
|
202
202
|
|
|
203
203
|
### Browse release-groups
|
|
204
204
|
```js
|
|
205
|
-
const releaseGroups = await browse('release-group',query);
|
|
205
|
+
const releaseGroups = await mbApi.browse('release-group',query);
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
| Query argument | Query value |
|
|
@@ -213,7 +213,7 @@ const releaseGroups = await browse('release-group',query);
|
|
|
213
213
|
|
|
214
214
|
### Browse series
|
|
215
215
|
```js
|
|
216
|
-
const series = await browse('series');
|
|
216
|
+
const series = await mbApi.browse('series');
|
|
217
217
|
````
|
|
218
218
|
|
|
219
219
|
| Query argument | Query value |
|
|
@@ -231,7 +231,7 @@ const series = await browse('series');
|
|
|
231
231
|
|
|
232
232
|
### Browse works
|
|
233
233
|
```js
|
|
234
|
-
const works = await browse('work');
|
|
234
|
+
const works = await mbApi.browse('work');
|
|
235
235
|
````
|
|
236
236
|
|
|
237
237
|
| Query argument | Query value |
|
|
@@ -241,7 +241,7 @@ const works = await browse('work');
|
|
|
241
241
|
|
|
242
242
|
### Browse urls
|
|
243
243
|
```js
|
|
244
|
-
const urls = await browse('url');
|
|
244
|
+
const urls = await mbApi.browse('url');
|
|
245
245
|
````
|
|
246
246
|
|
|
247
247
|
| Query argument | Query value |
|
|
@@ -277,7 +277,7 @@ Arguments:
|
|
|
277
277
|
For example, to find any recordings of _'We Will Rock You'_ by Queen:
|
|
278
278
|
```js
|
|
279
279
|
const query = 'query="We Will Rock You" AND arid:0383dadf-2a4e-4d10-a46a-e9e041da8eb3';
|
|
280
|
-
const result = await mbApi.
|
|
280
|
+
const result = await mbApi.search('release-group', {query});
|
|
281
281
|
```
|
|
282
282
|
|
|
283
283
|
##### Example: search Île-de-France
|
|
@@ -391,6 +391,7 @@ await mbApi.addSpotifyIdToRecording(recording, '2AMysGXOe0zzZJMtH3Nizb');
|
|
|
391
391
|
|
|
392
392
|
Implementation of the [Cover Art Archive API](https://musicbrainz.org/doc/Cover_Art_Archive/API).
|
|
393
393
|
|
|
394
|
+
### Release Cover Art
|
|
394
395
|
```js
|
|
395
396
|
import {CoverArtArchiveApi} from 'musicbrainz-api';
|
|
396
397
|
|
|
@@ -408,6 +409,25 @@ coverArtArchiveApiClient.getReleaseCovers(releaseMbid, 'back').then(releaseCover
|
|
|
408
409
|
|
|
409
410
|
```
|
|
410
411
|
|
|
412
|
+
### Release Group Cover Art
|
|
413
|
+
```js
|
|
414
|
+
import {CoverArtArchiveApi} from 'musicbrainz-api';
|
|
415
|
+
|
|
416
|
+
coverArtArchiveApiClient.getReleaseGroupCovers(releaseGroupMbid).then(releaseGroupCoverInfo => {
|
|
417
|
+
console.log('Release cover info', releaseGroupCoverInfo);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
coverArtArchiveApiClient.getReleaseGroupCovers(releaseGroupMbid, 'front').then(releaseGroupCoverInfo => {
|
|
421
|
+
console.log('Get best front cover', releaseGroupCoverInfo);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
coverArtArchiveApiClient.getReleaseGroupCovers(releaseGroupMbid, 'back').then(releaseGroupCoverInfo => {
|
|
425
|
+
console.log('Get best back cover', releaseGroupCoverInfo);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
|
|
411
431
|
## Compatibility
|
|
412
432
|
|
|
413
433
|
The JavaScript in runtime is compliant with [ECMAScript 2017 (ES8)](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017).
|
|
@@ -24,8 +24,25 @@ export declare class CoverArtArchiveApi {
|
|
|
24
24
|
private host;
|
|
25
25
|
private getJson;
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Fetch release
|
|
28
|
+
* @releaseId Release MBID
|
|
28
29
|
* @param releaseId MusicBrainz Release MBID
|
|
30
|
+
* @param coverType Cover type
|
|
29
31
|
*/
|
|
30
32
|
getReleaseCovers(releaseId: string, coverType?: 'front' | 'back'): Promise<ICoverInfo>;
|
|
33
|
+
/**
|
|
34
|
+
* Fetch release-group
|
|
35
|
+
* @releaseGroupId Release-group MBID
|
|
36
|
+
* @param releaseGroupId MusicBrainz Release Group MBID
|
|
37
|
+
* @param coverType Cover type
|
|
38
|
+
*/
|
|
39
|
+
getReleaseGroupCovers(releaseGroupId: string, coverType?: 'front' | 'back'): Promise<ICoverInfo>;
|
|
40
|
+
/**
|
|
41
|
+
* Fetch covers
|
|
42
|
+
* @releaseId MBID
|
|
43
|
+
* @param releaseId MusicBrainz Release Group MBID
|
|
44
|
+
* @param releaseType Fetch covers for specific release or release-group
|
|
45
|
+
* @param coverType Cover type
|
|
46
|
+
*/
|
|
47
|
+
private getCovers;
|
|
31
48
|
}
|
|
@@ -5,27 +5,49 @@ export class CoverArtArchiveApi {
|
|
|
5
5
|
this.host = 'coverartarchive.org';
|
|
6
6
|
}
|
|
7
7
|
async getJson(path) {
|
|
8
|
-
const response = await got.get(
|
|
8
|
+
const response = await got.get(`https://${this.host}${path}`, {
|
|
9
9
|
headers: {
|
|
10
|
-
Accept:
|
|
10
|
+
Accept: "application/json"
|
|
11
11
|
},
|
|
12
12
|
responseType: 'json'
|
|
13
13
|
});
|
|
14
14
|
return response.body;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Fetch release
|
|
18
|
+
* @releaseId Release MBID
|
|
18
19
|
* @param releaseId MusicBrainz Release MBID
|
|
20
|
+
* @param coverType Cover type
|
|
19
21
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
getReleaseCovers(releaseId, coverType) {
|
|
23
|
+
return this.getCovers(releaseId, 'release', coverType);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fetch release-group
|
|
27
|
+
* @releaseGroupId Release-group MBID
|
|
28
|
+
* @param releaseGroupId MusicBrainz Release Group MBID
|
|
29
|
+
* @param coverType Cover type
|
|
30
|
+
*/
|
|
31
|
+
getReleaseGroupCovers(releaseGroupId, coverType) {
|
|
32
|
+
return this.getCovers(releaseGroupId, 'release-group', coverType);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Fetch covers
|
|
36
|
+
* @releaseId MBID
|
|
37
|
+
* @param releaseId MusicBrainz Release Group MBID
|
|
38
|
+
* @param releaseType Fetch covers for specific release or release-group
|
|
39
|
+
* @param coverType Cover type
|
|
40
|
+
*/
|
|
41
|
+
async getCovers(releaseId, releaseType = 'release', coverType) {
|
|
42
|
+
var _a;
|
|
43
|
+
const path = [releaseType, releaseId];
|
|
22
44
|
if (coverType) {
|
|
23
45
|
path.push(coverType);
|
|
24
46
|
}
|
|
25
|
-
const info = await this.getJson(
|
|
47
|
+
const info = await this.getJson(`/${path.join('/')}`);
|
|
26
48
|
// Hack to correct http addresses into https
|
|
27
|
-
if (info.release
|
|
28
|
-
info.release =
|
|
49
|
+
if ((_a = info.release) === null || _a === void 0 ? void 0 : _a.startsWith('http:')) {
|
|
50
|
+
info.release = `https${info.release.substring(4)}`;
|
|
29
51
|
}
|
|
30
52
|
return info;
|
|
31
53
|
}
|
package/lib/digest-auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
-
import * as crypto from 'crypto';
|
|
2
|
+
import * as crypto from 'node:crypto';
|
|
3
3
|
function md5(str) {
|
|
4
4
|
return crypto.createHash('md5').update(str).digest('hex'); // lgtm [js/insufficient-password-hash]
|
|
5
5
|
}
|
|
@@ -13,8 +13,8 @@ export class DigestAuth {
|
|
|
13
13
|
* HA1=MD5(MD5(username:realm:password):nonce:cnonce)
|
|
14
14
|
*/
|
|
15
15
|
static ha1Compute(algorithm, user, realm, pass, nonce, cnonce) {
|
|
16
|
-
const ha1 = md5(user
|
|
17
|
-
return algorithm && algorithm.toLowerCase() === 'md5-sess' ? md5(ha1
|
|
16
|
+
const ha1 = md5(`${user}:${realm}:${pass}`); // lgtm [js/insufficient-password-hash]
|
|
17
|
+
return algorithm && algorithm.toLowerCase() === 'md5-sess' ? md5(`${ha1}:${nonce}:${cnonce}`) : ha1;
|
|
18
18
|
}
|
|
19
19
|
constructor(credentials) {
|
|
20
20
|
this.credentials = credentials;
|
|
@@ -44,10 +44,10 @@ export class DigestAuth {
|
|
|
44
44
|
const nc = qop && '00000001';
|
|
45
45
|
const cnonce = qop && uuidv4().replace(/-/g, '');
|
|
46
46
|
const ha1 = DigestAuth.ha1Compute(challenge.algorithm, this.credentials.username, challenge.realm, this.credentials.password, challenge.nonce, cnonce);
|
|
47
|
-
const ha2 = md5(method
|
|
47
|
+
const ha2 = md5(`${method}:${path}`); // lgtm [js/insufficient-password-hash]
|
|
48
48
|
const digestResponse = qop
|
|
49
|
-
? md5(ha1
|
|
50
|
-
: md5(ha1
|
|
49
|
+
? md5(`${ha1}:${challenge.nonce}:${nc}:${cnonce}:${qop}:${ha2}`) // lgtm [js/insufficient-password-hash]
|
|
50
|
+
: md5(`${ha1}:${challenge.nonce}:${ha2}`); // lgtm [js/insufficient-password-hash]
|
|
51
51
|
const authValues = {
|
|
52
52
|
username: this.credentials.username,
|
|
53
53
|
realm: challenge.realm,
|
|
@@ -64,16 +64,16 @@ export class DigestAuth {
|
|
|
64
64
|
Object.entries(authValues).forEach(([key, value]) => {
|
|
65
65
|
if (value) {
|
|
66
66
|
if (key === 'qop' || key === 'nc' || key === 'algorithm') {
|
|
67
|
-
parts.push(key
|
|
67
|
+
parts.push(`${key}=${value}`);
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
|
-
parts.push(key
|
|
70
|
+
parts.push(`${key}="${value}"`);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
|
-
|
|
74
|
+
const digest = `Digest ${parts.join(', ')}`;
|
|
75
75
|
this.sentAuth = true;
|
|
76
|
-
return
|
|
76
|
+
return digest;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
//# sourceMappingURL=digest-auth.js.map
|
package/lib/musicbrainz-api.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { XmlMetadata } from './xml/xml-metadata.js';
|
|
|
2
2
|
export { XmlIsrc } from './xml/xml-isrc.js';
|
|
3
3
|
export { XmlIsrcList } from './xml/xml-isrc-list.js';
|
|
4
4
|
export { XmlRecording } from './xml/xml-recording.js';
|
|
5
|
-
import { XmlMetadata } from './xml/xml-metadata.js';
|
|
5
|
+
import type { XmlMetadata } from './xml/xml-metadata.js';
|
|
6
6
|
import * as mb from './musicbrainz.types.js';
|
|
7
7
|
export * from './musicbrainz.types.js';
|
|
8
8
|
export type RelationsIncludes = 'area-rels' | 'artist-rels' | 'event-rels' | 'instrument-rels' | 'label-rels' | 'place-rels' | 'recording-rels' | 'release-rels' | 'release-group-rels' | 'series-rels' | 'url-rels' | 'work-rels';
|
|
@@ -46,7 +46,7 @@ export type IFormData = {
|
|
|
46
46
|
[key: string]: string | number;
|
|
47
47
|
};
|
|
48
48
|
export interface IMusicBrainzConfig {
|
|
49
|
-
botAccount
|
|
49
|
+
botAccount?: {
|
|
50
50
|
username?: string;
|
|
51
51
|
password?: string;
|
|
52
52
|
};
|
|
@@ -71,7 +71,6 @@ export interface ISessionInformation {
|
|
|
71
71
|
loggedIn?: boolean;
|
|
72
72
|
}
|
|
73
73
|
export declare class MusicBrainzApi {
|
|
74
|
-
private static escapeText;
|
|
75
74
|
readonly config: IMusicBrainzConfig;
|
|
76
75
|
private rateLimiter;
|
|
77
76
|
private options;
|
|
@@ -82,7 +81,7 @@ export declare class MusicBrainzApi {
|
|
|
82
81
|
constructor(_config?: IMusicBrainzConfig);
|
|
83
82
|
restGet<T>(relUrl: string, query?: {
|
|
84
83
|
[key: string]: any;
|
|
85
|
-
}
|
|
84
|
+
}): Promise<T>;
|
|
86
85
|
/**
|
|
87
86
|
* Lookup entity
|
|
88
87
|
* @param entity 'area', 'artist', collection', 'instrument', 'label', 'place', 'release', 'release-group', 'recording', 'series', 'work', 'url' or 'event'
|
|
@@ -164,9 +163,8 @@ export declare class MusicBrainzApi {
|
|
|
164
163
|
* Add ISRC to recording
|
|
165
164
|
* @param recording Recording to update
|
|
166
165
|
* @param isrc ISRC code to add
|
|
167
|
-
* @param editNote Edit note
|
|
168
166
|
*/
|
|
169
|
-
addIsrc(recording: mb.IRecording, isrc: string
|
|
167
|
+
addIsrc(recording: mb.IRecording, isrc: string): Promise<void>;
|
|
170
168
|
/**
|
|
171
169
|
* Add Spotify-ID to MusicBrainz recording.
|
|
172
170
|
* This function will automatically lookup the recording title, which is required to submit the recording URL
|
package/lib/musicbrainz-api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as assert from 'assert';
|
|
1
|
+
import * as assert from 'node:assert';
|
|
2
2
|
import { StatusCodes as HttpStatus } from 'http-status-codes';
|
|
3
3
|
import Debug from 'debug';
|
|
4
4
|
export { XmlMetadata } from './xml/xml-metadata.js';
|
|
@@ -11,38 +11,9 @@ import * as mb from './musicbrainz.types.js';
|
|
|
11
11
|
import got from 'got';
|
|
12
12
|
import { CookieJar } from 'tough-cookie';
|
|
13
13
|
export * from './musicbrainz.types.js';
|
|
14
|
-
import { promisify } from 'util';
|
|
14
|
+
import { promisify } from 'node:util';
|
|
15
15
|
const debug = Debug('musicbrainz-api');
|
|
16
16
|
export class MusicBrainzApi {
|
|
17
|
-
static escapeText(text) {
|
|
18
|
-
let str = '';
|
|
19
|
-
for (const chr of text) {
|
|
20
|
-
// Escaping Special Characters: + - && || ! ( ) { } [ ] ^ " ~ * ? : \ /
|
|
21
|
-
// ToDo: && ||
|
|
22
|
-
switch (chr) {
|
|
23
|
-
case '+':
|
|
24
|
-
case '-':
|
|
25
|
-
case '!':
|
|
26
|
-
case '(':
|
|
27
|
-
case ')':
|
|
28
|
-
case '{':
|
|
29
|
-
case '}':
|
|
30
|
-
case '[':
|
|
31
|
-
case ']':
|
|
32
|
-
case '^':
|
|
33
|
-
case '"':
|
|
34
|
-
case '~':
|
|
35
|
-
case '*':
|
|
36
|
-
case '?':
|
|
37
|
-
case ':':
|
|
38
|
-
case '\\':
|
|
39
|
-
case '/':
|
|
40
|
-
str += '\\';
|
|
41
|
-
}
|
|
42
|
-
str += chr;
|
|
43
|
-
}
|
|
44
|
-
return str;
|
|
45
|
-
}
|
|
46
17
|
static fetchCsrf(html) {
|
|
47
18
|
return {
|
|
48
19
|
sessionKey: MusicBrainzApi.fetchValue(html, 'csrf_session_key'),
|
|
@@ -56,15 +27,13 @@ export class MusicBrainzApi {
|
|
|
56
27
|
if (pos >= 0) {
|
|
57
28
|
pos += 7;
|
|
58
29
|
const endValuePos = html.indexOf('"', pos);
|
|
59
|
-
|
|
60
|
-
return value;
|
|
30
|
+
return html.substring(pos, endValuePos);
|
|
61
31
|
}
|
|
62
32
|
}
|
|
63
33
|
}
|
|
64
34
|
constructor(_config) {
|
|
65
35
|
this.config = {
|
|
66
|
-
baseUrl: 'https://musicbrainz.org'
|
|
67
|
-
botAccount: {}
|
|
36
|
+
baseUrl: 'https://musicbrainz.org'
|
|
68
37
|
};
|
|
69
38
|
Object.assign(this.config, _config);
|
|
70
39
|
const cookieJar = new CookieJar();
|
|
@@ -82,11 +51,11 @@ export class MusicBrainzApi {
|
|
|
82
51
|
};
|
|
83
52
|
this.rateLimiter = new RateLimitThreshold(15, 18);
|
|
84
53
|
}
|
|
85
|
-
async restGet(relUrl, query = {}
|
|
54
|
+
async restGet(relUrl, query = {}) {
|
|
86
55
|
query.fmt = 'json';
|
|
87
56
|
const delay = await this.rateLimiter.limit();
|
|
88
57
|
debug(`Client side rate limiter activated: cool down for ${Math.round(delay / 100) / 10} s...`);
|
|
89
|
-
const response = await got.get(
|
|
58
|
+
const response = await got.get(`ws/2${relUrl}`, {
|
|
90
59
|
...this.options,
|
|
91
60
|
searchParams: query,
|
|
92
61
|
responseType: 'json',
|
|
@@ -110,7 +79,7 @@ export class MusicBrainzApi {
|
|
|
110
79
|
if (Array.isArray(query.inc)) {
|
|
111
80
|
urlQuery.inc = urlQuery.inc.join(' ');
|
|
112
81
|
}
|
|
113
|
-
return this.restGet(
|
|
82
|
+
return this.restGet(`/${entity}/`, urlQuery);
|
|
114
83
|
}
|
|
115
84
|
// ---------------------------------------------------------------------------
|
|
116
85
|
async postRecording(xmlMetadata) {
|
|
@@ -118,7 +87,7 @@ export class MusicBrainzApi {
|
|
|
118
87
|
}
|
|
119
88
|
async post(entity, xmlMetadata) {
|
|
120
89
|
if (!this.config.appName || !this.config.appVersion) {
|
|
121
|
-
throw new Error(
|
|
90
|
+
throw new Error("XML-Post requires the appName & appVersion to be defined");
|
|
122
91
|
}
|
|
123
92
|
const clientId = `${this.config.appName.replace(/-/g, '.')}-${this.config.appVersion}`;
|
|
124
93
|
const path = `ws/2/${entity}/`;
|
|
@@ -151,9 +120,10 @@ export class MusicBrainzApi {
|
|
|
151
120
|
} while (n++ < 5);
|
|
152
121
|
}
|
|
153
122
|
async login() {
|
|
154
|
-
|
|
155
|
-
assert.ok(this.config.botAccount.
|
|
156
|
-
|
|
123
|
+
var _a, _b, _c;
|
|
124
|
+
assert.ok((_a = this.config.botAccount) === null || _a === void 0 ? void 0 : _a.username, 'bot username should be set');
|
|
125
|
+
assert.ok((_b = this.config.botAccount) === null || _b === void 0 ? void 0 : _b.password, 'bot password should be set');
|
|
126
|
+
if ((_c = this.session) === null || _c === void 0 ? void 0 : _c.loggedIn) {
|
|
157
127
|
for (const cookie of await this.getCookies(this.options.prefixUrl)) {
|
|
158
128
|
if (cookie.key === 'remember_login') {
|
|
159
129
|
return true;
|
|
@@ -208,12 +178,13 @@ export class MusicBrainzApi {
|
|
|
208
178
|
* @param formData
|
|
209
179
|
*/
|
|
210
180
|
async editEntity(entity, mbid, formData) {
|
|
181
|
+
var _a, _b;
|
|
211
182
|
await this.rateLimiter.limit();
|
|
212
183
|
this.session = await this.getSession();
|
|
213
184
|
formData.csrf_session_key = this.session.csrf.sessionKey;
|
|
214
185
|
formData.csrf_token = this.session.csrf.token;
|
|
215
|
-
formData.username = this.config.botAccount.username;
|
|
216
|
-
formData.password = this.config.botAccount.password;
|
|
186
|
+
formData.username = (_a = this.config.botAccount) === null || _a === void 0 ? void 0 : _a.username;
|
|
187
|
+
formData.password = (_b = this.config.botAccount) === null || _b === void 0 ? void 0 : _b.password;
|
|
217
188
|
formData.remember_me = 1;
|
|
218
189
|
const response = await got.post(`${entity}/${mbid}/edit`, {
|
|
219
190
|
...this.options,
|
|
@@ -221,7 +192,7 @@ export class MusicBrainzApi {
|
|
|
221
192
|
followRedirect: false
|
|
222
193
|
});
|
|
223
194
|
if (response.statusCode === HttpStatus.OK)
|
|
224
|
-
throw new Error(
|
|
195
|
+
throw new Error("Failed to submit form data");
|
|
225
196
|
if (response.statusCode === HttpStatus.MOVED_TEMPORARILY)
|
|
226
197
|
return;
|
|
227
198
|
throw new Error(`Unexpected status code: ${response.statusCode}`);
|
|
@@ -250,11 +221,10 @@ export class MusicBrainzApi {
|
|
|
250
221
|
* Add ISRC to recording
|
|
251
222
|
* @param recording Recording to update
|
|
252
223
|
* @param isrc ISRC code to add
|
|
253
|
-
* @param editNote Edit note
|
|
254
224
|
*/
|
|
255
|
-
async addIsrc(recording, isrc
|
|
225
|
+
async addIsrc(recording, isrc) {
|
|
256
226
|
const formData = {};
|
|
257
|
-
formData[
|
|
227
|
+
formData["edit-recording.name"] = recording.title; // Required
|
|
258
228
|
if (!recording.isrcs) {
|
|
259
229
|
throw new Error('You must retrieve recording with existing ISRC values');
|
|
260
230
|
}
|
|
@@ -280,13 +250,13 @@ export class MusicBrainzApi {
|
|
|
280
250
|
assert.strictEqual(spotifyId.length, 22);
|
|
281
251
|
return this.addUrlToRecording(recording, {
|
|
282
252
|
linkTypeId: mb.LinkType.stream_for_free,
|
|
283
|
-
text:
|
|
253
|
+
text: `https://open.spotify.com/track/${spotifyId}`
|
|
284
254
|
}, editNote);
|
|
285
255
|
}
|
|
286
256
|
async getSession() {
|
|
287
257
|
const response = await got.get('login', {
|
|
288
258
|
...this.options,
|
|
289
|
-
followRedirect: false,
|
|
259
|
+
followRedirect: false, // Disable redirects
|
|
290
260
|
responseType: 'text'
|
|
291
261
|
});
|
|
292
262
|
return {
|
|
@@ -36,7 +36,7 @@ export interface IArtist extends IEntity {
|
|
|
36
36
|
'gender-id'?: string;
|
|
37
37
|
'life-span'?: IPeriod;
|
|
38
38
|
country?: string;
|
|
39
|
-
ipis?:
|
|
39
|
+
ipis?: string[];
|
|
40
40
|
isnis?: string[];
|
|
41
41
|
aliases?: IAlias[];
|
|
42
42
|
gender?: string;
|
|
@@ -200,13 +200,13 @@ export interface IUrlList extends ISearchResult {
|
|
|
200
200
|
}
|
|
201
201
|
export type RelationDirection = 'backward' | 'forward';
|
|
202
202
|
export interface IRelation {
|
|
203
|
-
'attribute-ids':
|
|
203
|
+
'attribute-ids': unknown[];
|
|
204
204
|
direction: RelationDirection;
|
|
205
205
|
'target-credit': string;
|
|
206
206
|
end: null | unknown;
|
|
207
207
|
'source-credit': string;
|
|
208
208
|
ended: boolean;
|
|
209
|
-
'attribute-values': unknown;
|
|
209
|
+
'attribute-values': unknown[];
|
|
210
210
|
attributes?: any[];
|
|
211
211
|
type: string;
|
|
212
212
|
begin?: null | unknown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musicbrainz-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "MusicBrainz API client for reading and submitting metadata",
|
|
5
5
|
"exports": "./lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"download covers"
|
|
35
35
|
],
|
|
36
36
|
"license": "MIT",
|
|
37
|
-
"private": false,
|
|
38
37
|
"engines": {
|
|
39
38
|
"node": "^14.13.1 || >=16.0.0"
|
|
40
39
|
},
|
|
@@ -46,55 +45,47 @@
|
|
|
46
45
|
"url": "https://github.com/Borewit/musicbrainz-api/issues"
|
|
47
46
|
},
|
|
48
47
|
"dependencies": {
|
|
48
|
+
"@biomejs/biome": "1.8.3",
|
|
49
49
|
"@types/caseless": "^0.12.1",
|
|
50
50
|
"@types/request-promise-native": "^1.0.17",
|
|
51
|
-
"@types/uuid": "^
|
|
51
|
+
"@types/uuid": "^10.0.0",
|
|
52
52
|
"caseless": "^0.12.0",
|
|
53
53
|
"debug": "^4.3.4",
|
|
54
|
-
"got": "^
|
|
54
|
+
"got": "^14.2.1",
|
|
55
55
|
"http-status-codes": "^2.1.4",
|
|
56
56
|
"json-stringify-safe": "^5.0.1",
|
|
57
57
|
"jsontoxml": "^1.0.1",
|
|
58
58
|
"rate-limit-threshold": "^0.1.5",
|
|
59
59
|
"source-map-support": "^0.5.16",
|
|
60
60
|
"tough-cookie": "^4.1.3",
|
|
61
|
-
"uuid": "^
|
|
61
|
+
"uuid": "^10.0.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/chai": "^4.3.0",
|
|
65
65
|
"@types/jsontoxml": "^1.0.5",
|
|
66
66
|
"@types/mocha": "^10.0.4",
|
|
67
|
-
"@types/node": "^
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"c8": "^8.0.1",
|
|
71
|
-
"chai": "^4.2.0",
|
|
67
|
+
"@types/node": "^22.1.0",
|
|
68
|
+
"c8": "^10.1.2",
|
|
69
|
+
"chai": "^5.1.0",
|
|
72
70
|
"del-cli": "^5.0.0",
|
|
73
|
-
"eslint": "^8.10.0",
|
|
74
|
-
"eslint-config-prettier": "^9.0.0",
|
|
75
|
-
"eslint-import-resolver-typescript": "^3.3.0",
|
|
76
|
-
"eslint-plugin-import": "^2.25.4",
|
|
77
|
-
"eslint-plugin-jsdoc": "^46.8.2",
|
|
78
|
-
"eslint-plugin-node": "^11.1.0",
|
|
79
|
-
"eslint-plugin-unicorn": "^49.0.0",
|
|
80
71
|
"mocha": "^10.1.0",
|
|
81
72
|
"remark-cli": "^12.0.0",
|
|
82
|
-
"remark-preset-lint-recommended": "^
|
|
73
|
+
"remark-preset-lint-recommended": "^7.0.0",
|
|
83
74
|
"ts-node": "^10.0.0",
|
|
84
75
|
"typescript": "^5.0.2"
|
|
85
76
|
},
|
|
86
77
|
"scripts": {
|
|
87
|
-
"clean": "del-cli lib/**/*.js lib/**/*.js.map lib/**/*.d.ts test/**/*.js test/**/*.js.map",
|
|
78
|
+
"clean": "del-cli 'lib/**/*.js' 'lib/**/*.js.map' 'lib/**/*.d.ts' 'test/**/*.js' 'test/**/*.js.map'",
|
|
88
79
|
"compile-lib": "tsc -p lib",
|
|
89
80
|
"compile-test": "tsc -p test",
|
|
90
|
-
"compile": "
|
|
91
|
-
"eslint": "eslint lib/**/*.ts --ignore-pattern lib/**/*.d.ts test/**/*.ts",
|
|
81
|
+
"compile": "yarn run compile-lib && yarn run compile-test",
|
|
92
82
|
"lint-md": "remark -u preset-lint-recommended .",
|
|
93
|
-
"lint": "
|
|
83
|
+
"lint-ts": "biome check",
|
|
84
|
+
"lint": "yarn run lint-md && yarn run lint-ts",
|
|
94
85
|
"test": "mocha",
|
|
95
|
-
"build": "
|
|
96
|
-
"start": "
|
|
97
|
-
"test-coverage": "c8
|
|
86
|
+
"build": "yarn run clean && yarn run compile",
|
|
87
|
+
"start": "yarn run compile && yarn run lint && yarn run cover-test",
|
|
88
|
+
"test-coverage": "c8 yarn run test",
|
|
98
89
|
"send-codacy": "nyc report --reporter=text-lcov | codacy-coverage"
|
|
99
90
|
},
|
|
100
91
|
"nyc": {
|
|
@@ -111,5 +102,6 @@
|
|
|
111
102
|
"text"
|
|
112
103
|
],
|
|
113
104
|
"report-dir": "coverage"
|
|
114
|
-
}
|
|
105
|
+
},
|
|
106
|
+
"packageManager": "yarn@4.1.1"
|
|
115
107
|
}
|