musicbrainz-api 0.21.0 → 0.23.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/LICENSE.txt +9 -0
- package/README.md +151 -31
- package/package.json +6 -5
- package/lib/coverartarchive-api.d.ts +0 -48
- package/lib/coverartarchive-api.js +0 -60
- package/lib/digest-auth.d.ts +0 -21
- package/lib/digest-auth.js +0 -77
- package/lib/entry-default.d.ts +0 -2
- package/lib/entry-default.js +0 -3
- package/lib/entry-node.d.ts +0 -2
- package/lib/entry-node.js +0 -3
- package/lib/http-client-node.d.ts +0 -11
- package/lib/http-client-node.js +0 -19
- package/lib/http-client.d.ts +0 -27
- package/lib/http-client.js +0 -50
- package/lib/musicbrainz-api-node.d.ts +0 -16
- package/lib/musicbrainz-api-node.js +0 -71
- package/lib/musicbrainz-api.d.ts +0 -180
- package/lib/musicbrainz-api.js +0 -207
- package/lib/musicbrainz.types.d.ts +0 -606
- package/lib/musicbrainz.types.js +0 -14
- package/lib/xml/xml-isrc-list.d.ts +0 -17
- package/lib/xml/xml-isrc-list.js +0 -19
- package/lib/xml/xml-isrc.d.ts +0 -10
- package/lib/xml/xml-isrc.js +0 -14
- package/lib/xml/xml-metadata.d.ts +0 -6
- package/lib/xml/xml-metadata.js +0 -26
- package/lib/xml/xml-recording.d.ts +0 -24
- package/lib/xml/xml-recording.js +0 -17
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2025 Borewit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -116,18 +116,34 @@ MusicBrainz API documentation: [XML Web Service/Version 2 Lookups](https://wiki.
|
|
|
116
116
|
### Lookup Function
|
|
117
117
|
|
|
118
118
|
```js
|
|
119
|
-
const artist = await mbApi.lookup('artist', 'ab2528d9-719f-4261-8098-21849222a0f2');
|
|
119
|
+
const artist = await mbApi.lookup('artist', 'ab2528d9-719f-4261-8098-21849222a0f2', ['recordings']);
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
Arguments:
|
|
123
|
-
- entity: `'area'` | `'artist'` | `'collection'` | `'instrument'` | `'label'` | `'place'` | `'release'` | `'release-group'` | `'recording'` | `'series'` | `'work'` | `'url'` | `'event'`
|
|
124
|
-
- MBID [(MusicBrainz identifier)](https://wiki.musicbrainz.org/MusicBrainz_Identifier)
|
|
125
|
-
-
|
|
123
|
+
- entity (`string`): `'area'` | `'artist'` | `'collection'` | `'instrument'` | `'label'` | `'place'` | `'release'` | `'release-group'` | `'recording'` | `'series'` | `'work'` | `'url'` | `'event'`
|
|
124
|
+
- MBID (`string`): [(MusicBrainz identifier)](https://wiki.musicbrainz.org/MusicBrainz_Identifier)
|
|
125
|
+
- include arguments (`string[]`), see [Include arguments](#include-arguments)
|
|
126
126
|
|
|
127
|
+
#### Lookup URLs
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
There is special method to lookup URL entity / entities by one, or an array of URLs
|
|
130
|
+
([MusicBrainz documentation](https://musicbrainz.org/doc/MusicBrainz_API#url_(by_text))):
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
const urls = await mbApi.lookupUrl(['https://open.spotify.com/track/2AMysGXOe0zzZJMtH3Nizb', 'https://open.spotify.com/track/78Teboqh9lPIxWlIW5RMQL']);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
or
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const url = await mbApi.lookupUrl('https://open.spotify.com/track/2AMysGXOe0zzZJMtH3Nizb']);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Arguments:
|
|
143
|
+
- url (`string` | `string[]`): URL or array of URLs
|
|
144
|
+
- include arguments (`string[]`), see [Include arguments](#include-arguments)
|
|
145
|
+
|
|
146
|
+
Note that the return type is different, depending on if a single URL or an array of URLs is provided.
|
|
131
147
|
|
|
132
148
|
### Browse artist
|
|
133
149
|
|
|
@@ -355,6 +371,82 @@ Search a combination of a release-group and an artist.
|
|
|
355
371
|
const result = await mbApi.search('release-group', {artist: 'Racine carrée', releasegroup: 'Stromae'});
|
|
356
372
|
```
|
|
357
373
|
|
|
374
|
+
## Include arguments
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
### Subqueries
|
|
378
|
+
|
|
379
|
+
_Include Arguments_ which allow you to request more information to be included about the entity.
|
|
380
|
+
|
|
381
|
+
| entity | supported _include arguments_ |
|
|
382
|
+
|------------------|----------------------------------------------------------------------------|
|
|
383
|
+
| `area` | |
|
|
384
|
+
| `artist` | `recordings`, `releases`, `release-groups`, `works` |
|
|
385
|
+
| `collection` | `user-collections` (includes private collections, requires authentication) |
|
|
386
|
+
| `event` | |
|
|
387
|
+
| `genre` | |
|
|
388
|
+
| `instrument` | |
|
|
389
|
+
| `label` | `releases` |
|
|
390
|
+
| `place` | |
|
|
391
|
+
| `recording` | `artists`, `releases`, `release-groups`, `isrcs`, `url-rels` |
|
|
392
|
+
| `release` | `artists`, `collections`, `labels`, `recordings`, `release-groups` |
|
|
393
|
+
| `release-group` | `artists`, `releases` |
|
|
394
|
+
| `series` | |
|
|
395
|
+
| `work` | |
|
|
396
|
+
| `url` | |
|
|
397
|
+
|
|
398
|
+
### Arguments which affect subqueries
|
|
399
|
+
|
|
400
|
+
Some additional _include parameters_ are supported to specify how much of the data about the linked entities should be included:
|
|
401
|
+
|
|
402
|
+
| _include argument_ | Description |
|
|
403
|
+
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
404
|
+
| `discids` | include discids for all media in the releases |
|
|
405
|
+
| `media` | include media for all releases, this includes the # of tracks on each medium and its format. |
|
|
406
|
+
| `isrcs` | user-collections (includes private collections, requires authentication)include isrcs for all recordings |
|
|
407
|
+
| `artist-credits` | include artists credits for all releases and recordings |
|
|
408
|
+
| `various-artists` | include only those releases where the artist appears on one of the tracks, but not in the artist credit for the release itself (this is only valid on entity `"artist"` and _include argument_ `"releases request"`). |
|
|
409
|
+
|
|
410
|
+
### Miscellaneous arguments
|
|
411
|
+
|
|
412
|
+
| _include argument_ | Description |
|
|
413
|
+
|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
|
414
|
+
| `aliases` | include artist, label, area or work aliases; treat these as a set, as they are not deliberately ordered |
|
|
415
|
+
| `annotation` | include annotation |
|
|
416
|
+
| `tags`, `ratings` | include tags and/or ratings for the entity |
|
|
417
|
+
| `user-tags`, `user-ratings` | same as above, but only return the tags and/or ratings submitted by the specified user |
|
|
418
|
+
| `genres`, `user-genres` | include genres (tags in the [genres list](https://musicbrainz.org/genres)): either all or the ones submitted by the user, respectively |
|
|
419
|
+
|
|
420
|
+
### Relationships
|
|
421
|
+
|
|
422
|
+
You can request relationships with the appropriate includes:
|
|
423
|
+
- `area-rels`
|
|
424
|
+
- `artist-rels`
|
|
425
|
+
- `event-rels`
|
|
426
|
+
- `genre-rels`
|
|
427
|
+
- `instrument-rels`
|
|
428
|
+
- `label-rels`
|
|
429
|
+
- `place-rels`
|
|
430
|
+
- `recording-rels`
|
|
431
|
+
- `release-rels`
|
|
432
|
+
- `release-group-rels`
|
|
433
|
+
- `series-rels`
|
|
434
|
+
- `url-rels`
|
|
435
|
+
- `work-rels`
|
|
436
|
+
|
|
437
|
+
These will load relationships between the requested entity and the specific entity type.
|
|
438
|
+
For example, if you request "work-rels" when looking up an artist,
|
|
439
|
+
you'll get all the relationships between this artist and any works,
|
|
440
|
+
and if you request "artist-rels" you'll get the relationships between this artist and any other artists.
|
|
441
|
+
As such, keep in mind requesting "artist-rels" for an artist, "release-rels" for a release, etc. will not load all the relationships for the entity, just the ones to other entities of the same type.
|
|
442
|
+
|
|
443
|
+
In a release request, you might also be interested on relationships for the recordings linked to the release, or the release group linked to the release, or even for the works linked to those recordings that are linked to the release (for example, to find out who played guitar on a specific track, who wrote the lyrics for the song being performed, or whether the release group is part of a series). Similarly, for a recording request, you might want to get the relationships for any linked works.
|
|
444
|
+
There are three additional includes for this:
|
|
445
|
+
|
|
446
|
+
- recording-level-rels
|
|
447
|
+
- release-group-level-rels (for releases only)
|
|
448
|
+
- work-level-rels
|
|
449
|
+
|
|
358
450
|
# Submitting data via XML POST
|
|
359
451
|
|
|
360
452
|
[Submitting data via XML POST](https://wiki.musicbrainz.org/Development/XML_Web_Service/Version_2#Submitting_data) may be done using personal MusicBrainz credentials.
|
|
@@ -425,26 +517,40 @@ await mbApi.addSpotifyIdToRecording(recording, '2AMysGXOe0zzZJMtH3Nizb');
|
|
|
425
517
|
This library also supports the [Cover Art Archive API](https://musicbrainz.org/doc/Cover_Art_Archive/API).
|
|
426
518
|
|
|
427
519
|
### Fetch Release Cover Art
|
|
520
|
+
|
|
521
|
+
#### Fetch available cover art information
|
|
522
|
+
|
|
428
523
|
```js
|
|
429
524
|
import { CoverArtArchiveApi } from 'musicbrainz-api';
|
|
430
525
|
|
|
431
526
|
const coverArtArchiveApiClient = new CoverArtArchiveApi();
|
|
432
527
|
|
|
433
|
-
async function
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
console.log(`Cover
|
|
437
|
-
} catch (error) {
|
|
438
|
-
console.error(`Failed to fetch ${coverType || 'all covers'}:`, error);
|
|
528
|
+
async function fetchCoverArt(releaseMbid, coverType = '') {
|
|
529
|
+
const coverInfo = await coverArtArchiveApiClient.getReleaseCovers(releaseMbid);
|
|
530
|
+
for(const image of coverInfo.images) {
|
|
531
|
+
console.log(`Cover art front=${image.front} back=${image.back} url=${image.image}`);
|
|
439
532
|
}
|
|
440
533
|
}
|
|
441
534
|
|
|
442
|
-
(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
535
|
+
fetchCoverArt('976e0677-a480-4a5e-a177-6a86c1900bbf').catch(error => {
|
|
536
|
+
console.error(`Failed to fetch cover art: ${error.message}`);
|
|
537
|
+
})
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
#### Fetch front or back cover for a release
|
|
541
|
+
```js
|
|
542
|
+
import { CoverArtArchiveApi } from 'musicbrainz-api';
|
|
543
|
+
|
|
544
|
+
const coverArtArchiveApiClient = new CoverArtArchiveApi();
|
|
545
|
+
|
|
546
|
+
async function fetchCoverArt(releaseMbid, coverType = '') {
|
|
547
|
+
const coverInfo = await coverArtArchiveApiClient.getReleaseCover(releaseMbid, 'front');
|
|
548
|
+
console.log(`Cover art url=${coverInfo.url}`);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
fetchCoverArt('976e0677-a480-4a5e-a177-6a86c1900bbf').catch(error => {
|
|
552
|
+
console.error(`Failed to fetch cover art: ${error.message}`);
|
|
553
|
+
})
|
|
448
554
|
```
|
|
449
555
|
|
|
450
556
|
### Release Group Cover Art
|
|
@@ -453,22 +559,32 @@ import { CoverArtArchiveApi } from 'musicbrainz-api';
|
|
|
453
559
|
|
|
454
560
|
const coverArtArchiveApiClient = new CoverArtArchiveApi();
|
|
455
561
|
|
|
456
|
-
async function
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
console.log(`Cover
|
|
460
|
-
} catch (error) {
|
|
461
|
-
console.error(`Failed to fetch ${coverType || 'all covers'}:`, error);
|
|
562
|
+
async function fetchCoverArt(releaseMbid, coverType = '') {
|
|
563
|
+
const coverInfo = await coverArtArchiveApiClient.getReleaseGroupCovers(releaseMbid);
|
|
564
|
+
for(const image of coverInfo.images) {
|
|
565
|
+
console.log(`Cover art front=${image.front} back=${image.back} url=${image.image}`);
|
|
462
566
|
}
|
|
463
567
|
}
|
|
464
568
|
|
|
465
|
-
(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
await getCoverArt(releaseGroupMbid, 'back'); // Get best back cover
|
|
470
|
-
})();
|
|
569
|
+
fetchCoverArt('976e0677-a480-4a5e-a177-6a86c1900bbf').catch(error => {
|
|
570
|
+
console.error(`Failed to fetch cover art: ${error.message}`);
|
|
571
|
+
})
|
|
572
|
+
```
|
|
471
573
|
|
|
574
|
+
#### Fetch front or back cover for a release-group
|
|
575
|
+
```js
|
|
576
|
+
import { CoverArtArchiveApi } from 'musicbrainz-api';
|
|
577
|
+
|
|
578
|
+
const coverArtArchiveApiClient = new CoverArtArchiveApi();
|
|
579
|
+
|
|
580
|
+
async function fetchCoverArt(releaseMbid, coverType = '') {
|
|
581
|
+
const coverInfo = await coverArtArchiveApiClient.getReleaseGroupCover(releaseMbid, 'front');
|
|
582
|
+
console.log(`Cover art url=${coverInfo.url}`);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
fetchCoverArt('976e0677-a480-4a5e-a177-6a86c1900bbf').catch(error => {
|
|
586
|
+
console.error(`Failed to fetch cover art: ${error.message}`);
|
|
587
|
+
})
|
|
472
588
|
```
|
|
473
589
|
|
|
474
590
|
## CommonJS backward compatibility
|
|
@@ -505,3 +621,7 @@ async function run() {
|
|
|
505
621
|
|
|
506
622
|
run();
|
|
507
623
|
```
|
|
624
|
+
|
|
625
|
+
## Licence
|
|
626
|
+
|
|
627
|
+
This project is licensed under the [MIT License](LICENSE.txt). Feel free to use, modify, and distribute as needed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musicbrainz-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "MusicBrainz API client for reading and submitting metadata",
|
|
5
5
|
"exports": {
|
|
6
6
|
"node": {
|
|
@@ -90,9 +90,10 @@
|
|
|
90
90
|
"compile-lib": "tsc -p lib",
|
|
91
91
|
"compile-test": "tsc -p test",
|
|
92
92
|
"compile": "yarn run compile-lib && yarn run compile-test",
|
|
93
|
-
"lint
|
|
94
|
-
"lint
|
|
95
|
-
"lint": "
|
|
93
|
+
"lint:md": "remark -u preset-lint-recommended .",
|
|
94
|
+
"lint:ts": "biome check",
|
|
95
|
+
"lint:fix": "biome check --fix",
|
|
96
|
+
"lint": "yarn run lint:md && yarn run lint:ts",
|
|
96
97
|
"test": "mocha",
|
|
97
98
|
"build": "yarn run clean && yarn run compile",
|
|
98
99
|
"start": "yarn run compile && yarn run lint && yarn run cover-test",
|
|
@@ -114,5 +115,5 @@
|
|
|
114
115
|
],
|
|
115
116
|
"report-dir": "coverage"
|
|
116
117
|
},
|
|
117
|
-
"packageManager": "yarn@4.
|
|
118
|
+
"packageManager": "yarn@4.9.1"
|
|
118
119
|
}
|
|
@@ -1,48 +0,0 @@
|
|
|
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 httpClient;
|
|
25
|
-
private getJson;
|
|
26
|
-
/**
|
|
27
|
-
* Fetch release
|
|
28
|
-
* @releaseId Release MBID
|
|
29
|
-
* @param releaseId MusicBrainz Release MBID
|
|
30
|
-
* @param coverType Cover type
|
|
31
|
-
*/
|
|
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;
|
|
48
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/* eslint-disable-next-line */
|
|
2
|
-
import { HttpClient } from "./http-client.js";
|
|
3
|
-
export class CoverArtArchiveApi {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.httpClient = new HttpClient({ baseUrl: 'https://coverartarchive.org', userAgent: 'Node.js musicbrains-api', timeout: 20000 });
|
|
6
|
-
}
|
|
7
|
-
async getJson(path) {
|
|
8
|
-
const response = await this.httpClient.get(path, {
|
|
9
|
-
headers: {
|
|
10
|
-
Accept: "application/json"
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
const contentType = response.headers.get("Content-Type");
|
|
14
|
-
if (response.status === 404 && contentType?.toLowerCase() !== "application/json") {
|
|
15
|
-
return {
|
|
16
|
-
"error": "Not Found",
|
|
17
|
-
"help": "For usage, please see: https://musicbrainz.org/development/mmd"
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
return response.json();
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Fetch release
|
|
24
|
-
* @releaseId Release MBID
|
|
25
|
-
* @param releaseId MusicBrainz Release MBID
|
|
26
|
-
* @param coverType Cover type
|
|
27
|
-
*/
|
|
28
|
-
getReleaseCovers(releaseId, coverType) {
|
|
29
|
-
return this.getCovers(releaseId, 'release', coverType);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Fetch release-group
|
|
33
|
-
* @releaseGroupId Release-group MBID
|
|
34
|
-
* @param releaseGroupId MusicBrainz Release Group MBID
|
|
35
|
-
* @param coverType Cover type
|
|
36
|
-
*/
|
|
37
|
-
getReleaseGroupCovers(releaseGroupId, coverType) {
|
|
38
|
-
return this.getCovers(releaseGroupId, 'release-group', coverType);
|
|
39
|
-
}
|
|
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
|
-
async getCovers(releaseId, releaseType = 'release', coverType) {
|
|
48
|
-
const path = [releaseType, releaseId];
|
|
49
|
-
if (coverType) {
|
|
50
|
-
path.push(coverType);
|
|
51
|
-
}
|
|
52
|
-
const info = await this.getJson(`/${path.join('/')}`);
|
|
53
|
-
// Hack to correct http addresses into https
|
|
54
|
-
if (info.release?.startsWith('http:')) {
|
|
55
|
-
info.release = `https${info.release.substring(4)}`;
|
|
56
|
-
}
|
|
57
|
-
return info;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=coverartarchive-api.js.map
|
package/lib/digest-auth.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export interface ICredentials {
|
|
2
|
-
username: string;
|
|
3
|
-
password: string;
|
|
4
|
-
}
|
|
5
|
-
export declare class DigestAuth {
|
|
6
|
-
private credentials;
|
|
7
|
-
/**
|
|
8
|
-
* RFC 2617: handle both MD5 and MD5-sess algorithms.
|
|
9
|
-
*
|
|
10
|
-
* If the algorithm directive's value is "MD5" or unspecified, then HA1 is
|
|
11
|
-
* HA1=MD5(username:realm:password)
|
|
12
|
-
* If the algorithm directive's value is "MD5-sess", then HA1 is
|
|
13
|
-
* HA1=MD5(MD5(username:realm:password):nonce:cnonce)
|
|
14
|
-
*/
|
|
15
|
-
static ha1Compute(algorithm: string, user: string, realm: string, pass: string, nonce: string, cnonce: string): string;
|
|
16
|
-
hasAuth: boolean;
|
|
17
|
-
sentAuth: boolean;
|
|
18
|
-
bearerToken: string | null;
|
|
19
|
-
constructor(credentials: ICredentials);
|
|
20
|
-
digest(method: string, path: string, authHeader: string): string;
|
|
21
|
-
}
|
package/lib/digest-auth.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
-
import sparkMd5 from 'spark-md5';
|
|
3
|
-
const md5 = sparkMd5.hash;
|
|
4
|
-
export class DigestAuth {
|
|
5
|
-
/**
|
|
6
|
-
* RFC 2617: handle both MD5 and MD5-sess algorithms.
|
|
7
|
-
*
|
|
8
|
-
* If the algorithm directive's value is "MD5" or unspecified, then HA1 is
|
|
9
|
-
* HA1=MD5(username:realm:password)
|
|
10
|
-
* If the algorithm directive's value is "MD5-sess", then HA1 is
|
|
11
|
-
* HA1=MD5(MD5(username:realm:password):nonce:cnonce)
|
|
12
|
-
*/
|
|
13
|
-
static ha1Compute(algorithm, user, realm, pass, nonce, cnonce) {
|
|
14
|
-
const ha1 = md5(`${user}:${realm}:${pass}`); // lgtm [js/insufficient-password-hash]
|
|
15
|
-
return algorithm && algorithm.toLowerCase() === 'md5-sess' ? md5(`${ha1}:${nonce}:${cnonce}`) : ha1;
|
|
16
|
-
}
|
|
17
|
-
constructor(credentials) {
|
|
18
|
-
this.credentials = credentials;
|
|
19
|
-
this.hasAuth = false;
|
|
20
|
-
this.sentAuth = false;
|
|
21
|
-
this.bearerToken = null;
|
|
22
|
-
}
|
|
23
|
-
digest(method, path, authHeader) {
|
|
24
|
-
// TODO: More complete implementation of RFC 2617.
|
|
25
|
-
// - support qop="auth-int" only
|
|
26
|
-
// - handle Authentication-Info (not necessarily?)
|
|
27
|
-
// - check challenge.stale (not necessarily?)
|
|
28
|
-
// - increase nc (not necessarily?)
|
|
29
|
-
// For reference:
|
|
30
|
-
// http://tools.ietf.org/html/rfc2617#section-3
|
|
31
|
-
// https://github.com/bagder/curl/blob/master/lib/http_digest.c
|
|
32
|
-
const challenge = {};
|
|
33
|
-
const re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi;
|
|
34
|
-
while (true) {
|
|
35
|
-
const match = re.exec(authHeader);
|
|
36
|
-
if (!match) {
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
challenge[match[1]] = match[2] || match[3];
|
|
40
|
-
}
|
|
41
|
-
const qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth';
|
|
42
|
-
const nc = qop && '00000001';
|
|
43
|
-
const cnonce = qop && uuidv4().replace(/-/g, '');
|
|
44
|
-
const ha1 = DigestAuth.ha1Compute(challenge.algorithm, this.credentials.username, challenge.realm, this.credentials.password, challenge.nonce, cnonce);
|
|
45
|
-
const ha2 = md5(`${method}:${path}`); // lgtm [js/insufficient-password-hash]
|
|
46
|
-
const digestResponse = qop
|
|
47
|
-
? md5(`${ha1}:${challenge.nonce}:${nc}:${cnonce}:${qop}:${ha2}`) // lgtm [js/insufficient-password-hash]
|
|
48
|
-
: md5(`${ha1}:${challenge.nonce}:${ha2}`); // lgtm [js/insufficient-password-hash]
|
|
49
|
-
const authValues = {
|
|
50
|
-
username: this.credentials.username,
|
|
51
|
-
realm: challenge.realm,
|
|
52
|
-
nonce: challenge.nonce,
|
|
53
|
-
uri: path,
|
|
54
|
-
qop,
|
|
55
|
-
response: digestResponse,
|
|
56
|
-
nc,
|
|
57
|
-
cnonce,
|
|
58
|
-
algorithm: challenge.algorithm,
|
|
59
|
-
opaque: challenge.opaque
|
|
60
|
-
};
|
|
61
|
-
const parts = [];
|
|
62
|
-
Object.entries(authValues).forEach(([key, value]) => {
|
|
63
|
-
if (value) {
|
|
64
|
-
if (key === 'qop' || key === 'nc' || key === 'algorithm') {
|
|
65
|
-
parts.push(`${key}=${value}`);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
parts.push(`${key}="${value}"`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
const digest = `Digest ${parts.join(', ')}`;
|
|
73
|
-
this.sentAuth = true;
|
|
74
|
-
return digest;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
//# sourceMappingURL=digest-auth.js.map
|
package/lib/entry-default.d.ts
DELETED
package/lib/entry-default.js
DELETED
package/lib/entry-node.d.ts
DELETED
package/lib/entry-node.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type Cookie } from "tough-cookie";
|
|
2
|
-
import { HttpClient, type IHttpClientOptions } from "./http-client.js";
|
|
3
|
-
export type HttpFormData = {
|
|
4
|
-
[key: string]: string;
|
|
5
|
-
};
|
|
6
|
-
export declare class HttpClientNode extends HttpClient {
|
|
7
|
-
private cookieJar;
|
|
8
|
-
constructor(options: IHttpClientOptions);
|
|
9
|
-
protected registerCookies(response: Response): Promise<Cookie | undefined>;
|
|
10
|
-
getCookies(): Promise<string | null>;
|
|
11
|
-
}
|
package/lib/http-client-node.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { CookieJar } from "tough-cookie";
|
|
2
|
-
import { HttpClient } from "./http-client.js";
|
|
3
|
-
export class HttpClientNode extends HttpClient {
|
|
4
|
-
constructor(options) {
|
|
5
|
-
super(options);
|
|
6
|
-
this.cookieJar = new CookieJar();
|
|
7
|
-
}
|
|
8
|
-
registerCookies(response) {
|
|
9
|
-
const cookie = response.headers.get('set-cookie');
|
|
10
|
-
if (cookie) {
|
|
11
|
-
return this.cookieJar.setCookie(cookie, response.url);
|
|
12
|
-
}
|
|
13
|
-
return Promise.resolve(undefined);
|
|
14
|
-
}
|
|
15
|
-
getCookies() {
|
|
16
|
-
return this.cookieJar.getCookieString(this.options.baseUrl); // Get cookies for the request
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=http-client-node.js.map
|
package/lib/http-client.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { Cookie } from "tough-cookie";
|
|
2
|
-
export type HttpFormData = {
|
|
3
|
-
[key: string]: string;
|
|
4
|
-
};
|
|
5
|
-
export interface IHttpClientOptions {
|
|
6
|
-
baseUrl: string;
|
|
7
|
-
timeout: number;
|
|
8
|
-
userAgent: string;
|
|
9
|
-
}
|
|
10
|
-
export interface IFetchOptions {
|
|
11
|
-
query?: HttpFormData;
|
|
12
|
-
retryLimit?: number;
|
|
13
|
-
body?: string;
|
|
14
|
-
headers?: HeadersInit;
|
|
15
|
-
followRedirects?: boolean;
|
|
16
|
-
}
|
|
17
|
-
export declare class HttpClient {
|
|
18
|
-
protected options: IHttpClientOptions;
|
|
19
|
-
constructor(options: IHttpClientOptions);
|
|
20
|
-
get(path: string, options?: IFetchOptions): Promise<Response>;
|
|
21
|
-
post(path: string, options?: IFetchOptions): Promise<Response>;
|
|
22
|
-
postForm(path: string, formData: HttpFormData, options?: IFetchOptions): Promise<Response>;
|
|
23
|
-
postJson(path: string, json: Object, options?: IFetchOptions): Promise<Response>;
|
|
24
|
-
private _fetch;
|
|
25
|
-
protected registerCookies(response: Response): Promise<Cookie | undefined>;
|
|
26
|
-
getCookies(): Promise<string | null>;
|
|
27
|
-
}
|
package/lib/http-client.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export class HttpClient {
|
|
2
|
-
constructor(options) {
|
|
3
|
-
this.options = options;
|
|
4
|
-
}
|
|
5
|
-
get(path, options) {
|
|
6
|
-
return this._fetch('get', path, options);
|
|
7
|
-
}
|
|
8
|
-
post(path, options) {
|
|
9
|
-
return this._fetch('post', path, options);
|
|
10
|
-
}
|
|
11
|
-
postForm(path, formData, options) {
|
|
12
|
-
const encodedFormData = new URLSearchParams(formData).toString();
|
|
13
|
-
return this._fetch('post', path, { ...options, body: encodedFormData, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
|
|
14
|
-
}
|
|
15
|
-
// biome-ignore lint/complexity/noBannedTypes:
|
|
16
|
-
postJson(path, json, options) {
|
|
17
|
-
const encodedJson = JSON.stringify(json);
|
|
18
|
-
return this._fetch('post', path, { ...options, body: encodedJson, headers: { 'Content-Type': 'application/json.' } });
|
|
19
|
-
}
|
|
20
|
-
async _fetch(method, path, options) {
|
|
21
|
-
if (!options)
|
|
22
|
-
options = {};
|
|
23
|
-
let url = `${this.options.baseUrl}/${path}`;
|
|
24
|
-
if (options.query) {
|
|
25
|
-
url += `?${new URLSearchParams(options.query)}`;
|
|
26
|
-
}
|
|
27
|
-
const cookies = await this.getCookies();
|
|
28
|
-
const headers = new Headers(options.headers);
|
|
29
|
-
headers.set('User-Agent', this.options.userAgent);
|
|
30
|
-
if (cookies !== null) {
|
|
31
|
-
headers.set('Cookie', cookies);
|
|
32
|
-
}
|
|
33
|
-
const response = await fetch(url, {
|
|
34
|
-
method,
|
|
35
|
-
...options,
|
|
36
|
-
headers,
|
|
37
|
-
body: options.body,
|
|
38
|
-
redirect: options.followRedirects === false ? 'manual' : 'follow'
|
|
39
|
-
});
|
|
40
|
-
await this.registerCookies(response);
|
|
41
|
-
return response;
|
|
42
|
-
}
|
|
43
|
-
registerCookies(response) {
|
|
44
|
-
return Promise.resolve(undefined);
|
|
45
|
-
}
|
|
46
|
-
async getCookies() {
|
|
47
|
-
return Promise.resolve(null);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=http-client.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export { XmlMetadata } from './xml/xml-metadata.js';
|
|
2
|
-
export { XmlIsrc } from './xml/xml-isrc.js';
|
|
3
|
-
export { XmlIsrcList } from './xml/xml-isrc-list.js';
|
|
4
|
-
export { XmlRecording } from './xml/xml-recording.js';
|
|
5
|
-
import { HttpClientNode } from "./http-client-node.js";
|
|
6
|
-
import { MusicBrainzApi as MusicBrainzApiDefault } from "./musicbrainz-api.js";
|
|
7
|
-
export * from './musicbrainz.types.js';
|
|
8
|
-
export * from './http-client.js';
|
|
9
|
-
export declare class MusicBrainzApi extends MusicBrainzApiDefault {
|
|
10
|
-
protected initHttpClient(): HttpClientNode;
|
|
11
|
-
login(): Promise<boolean>;
|
|
12
|
-
/**
|
|
13
|
-
* Logout
|
|
14
|
-
*/
|
|
15
|
-
logout(): Promise<boolean>;
|
|
16
|
-
}
|