gerdur-core 2.1.0 → 2.3.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/CHANGELOG.md +47 -0
- package/README.md +66 -0
- package/dist/api/browse.d.ts +49 -0
- package/dist/api/browse.js +82 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/preview.d.ts +21 -0
- package/dist/api/preview.js +37 -0
- package/dist/lib/get-url.d.ts +28 -9
- package/dist/lib/get-url.js +68 -42
- package/dist/types/browse.d.ts +85 -0
- package/dist/types/browse.js +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/tracks.d.ts +12 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0 - 2026-08-31
|
|
4
|
+
|
|
5
|
+
Phase 2.1 — all the formats, and previews. Additive; the `1 / 3 / 9` path is
|
|
6
|
+
unchanged.
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`getTrackPreview(track)` / `downloadPreview(track)`** — the 30-second preview
|
|
11
|
+
clip. A plain MP3: no licence, no `arl`, no Blowfish. Accepts a gw track (reads
|
|
12
|
+
`MEDIA`, no extra request), a track id, or a number.
|
|
13
|
+
- **`DEEZER_FORMATS`** — every format `get_url` understands, best → worst
|
|
14
|
+
(`FLAC`, `MP3_320`, `MP3_256`, `MP3_128`, `MP3_64`, `AAC_64`, `MP4_RA3/2/1`).
|
|
15
|
+
- **`resolveDownloadUrls(tracks, qualities)`** now accepts format strings in the
|
|
16
|
+
preference list (`['FLAC', 'MP3_320', 'AAC_64']`), not just `1 | 3 | 9`. Each
|
|
17
|
+
`ResolvedUrl` gains `cipher` (`'BF_CBC_STRIPE'` | `'NONE'`).
|
|
18
|
+
- **`toFormat(quality)`** — normalise a number or format string to the `get_url`
|
|
19
|
+
format string. `formatName` now also accepts format strings (identity).
|
|
20
|
+
- `Quality` / `DeezerFormat` exported types.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- `isEncrypted` (from `getTrackDownloadUrl` and `resolveDownloadUrls`) is now
|
|
25
|
+
taken from the media API's `cipher` field instead of guessing from the URL
|
|
26
|
+
path — authoritative, and correct for `cipher: NONE` content.
|
|
27
|
+
- `trackType.FILESIZE_*` fields are typed `string` (several were mistyped as the
|
|
28
|
+
literal `'0'`); added `FILESIZE_MP3_MISC` / `FILESIZE_MHM1_RA*`.
|
|
29
|
+
|
|
30
|
+
## 2.2.0 - 2026-08-31
|
|
31
|
+
|
|
32
|
+
Phase 2.4 — browse & discovery. All additive, all on the public REST API (no
|
|
33
|
+
`arl` needed), all memoised.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **Charts / editorial**: `getGenres`, `getChart(genreId, limit)` (the five
|
|
38
|
+
ranked lists), `getChartTracks`, `getGenreArtists`, `getEditorialList`,
|
|
39
|
+
`getEditorialReleases`, `getEditorialSelection`, `getEditorialCharts`.
|
|
40
|
+
- **Artist discovery**: `getArtistTopTracks`, `getRelatedArtists`,
|
|
41
|
+
`getArtistAlbums`, `getArtistPlaylists`, `getArtistRadioTracks`.
|
|
42
|
+
- **ISRC / UPC resolution**: `getTrackByISRC(isrc)` and `getAlbumByUPC(upc)` —
|
|
43
|
+
raw public-API track/album (with `bpm`, `gain`, `preview`, embedded `tracks`).
|
|
44
|
+
Complements the converter's `isrc2deezer` / `upc2deezer`, which hydrate a gw
|
|
45
|
+
track instead.
|
|
46
|
+
- New exported types: `chartType`, `chartTrack`/`chartAlbum`/`chartArtist`/
|
|
47
|
+
`chartPlaylist`/`chartPodcast`, `genreType`, `editorialType`,
|
|
48
|
+
`artistAlbumResult`, `publicApiList<T>`.
|
|
49
|
+
|
|
3
50
|
## 2.1.0 - 2026-08-31
|
|
4
51
|
|
|
5
52
|
Phase 2.5 — search, properly. All additive.
|
package/README.md
CHANGED
|
@@ -196,6 +196,41 @@ ignore the operators, so pass a plain string there.
|
|
|
196
196
|
"as you type" UIs. `nb` (default 5) caps items per type. Needs an initialised
|
|
197
197
|
session (`initDeezerApi`).
|
|
198
198
|
|
|
199
|
+
### Browse & discovery
|
|
200
|
+
|
|
201
|
+
Public REST endpoints — no `arl` needed, memoised like the rest. All return a
|
|
202
|
+
`{data, total?, next?}` list unless noted.
|
|
203
|
+
|
|
204
|
+
| Method | Returns |
|
|
205
|
+
| :--- | :--- |
|
|
206
|
+
| `getGenres()` | Deezer's genre list (`id` `0` = "All"). |
|
|
207
|
+
| `getChart(genreId = 0, limit = 10)` | `{tracks, albums, artists, playlists, podcasts}` — the ranked lists for a genre. |
|
|
208
|
+
| `getChartTracks(genreId = 0, limit = 100, index = 0)` | just the track chart, each with a `position`. |
|
|
209
|
+
| `getGenreArtists(genreId)` | artists filed under a genre. |
|
|
210
|
+
| `getEditorialList()` | Deezer's editorial sections. |
|
|
211
|
+
| `getEditorialReleases(editorialId = 0, limit = 25, index = 0)` | new releases for a section. |
|
|
212
|
+
| `getEditorialSelection(editorialId = 0)` | albums the editors are pushing. |
|
|
213
|
+
| `getEditorialCharts(editorialId = 0)` | a section's charts (same 5-list shape as `getChart`). |
|
|
214
|
+
| `getArtistTopTracks(artistId, limit = 50)` | an artist's most popular tracks. |
|
|
215
|
+
| `getRelatedArtists(artistId, limit = 20)` | similar / related artists. |
|
|
216
|
+
| `getArtistAlbums(artistId, limit = 50, index = 0)` | the artist's discography. |
|
|
217
|
+
| `getArtistPlaylists(artistId, limit = 25)` | playlists featuring the artist. |
|
|
218
|
+
| `getArtistRadioTracks(artistId)` | a ready-made radio seeded from the artist. |
|
|
219
|
+
| `getTrackByISRC(isrc)` | the public-API track for an ISRC (`bpm`, `gain`, `preview`, …). |
|
|
220
|
+
| `getAlbumByUPC(upc)` | the public-API album (with its `tracks`) for a UPC/EAN barcode. |
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
const {data: genres} = await getGenres();
|
|
224
|
+
const rock = genres.find((g) => g.name === 'Rock');
|
|
225
|
+
const {tracks} = await getChart(rock.id, 20); // this week's rock chart
|
|
226
|
+
const similar = await getRelatedArtists(27); // artists like Daft Punk
|
|
227
|
+
const track = await getTrackByISRC('USUM71311296'); // "Get Lucky"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`getTrackByISRC` / `getAlbumByUPC` return raw public-API objects. To download,
|
|
231
|
+
pass the `id` to `getTrackInfo` / `getAlbumTracks` (or use the converter's
|
|
232
|
+
`isrc2deezer` / `upc2deezer`, which hydrate a gw track for you).
|
|
233
|
+
|
|
199
234
|
### `.getTrackDownloadUrl(track, quality);`
|
|
200
235
|
|
|
201
236
|
| Parameters | Required | Type | Description |
|
|
@@ -203,6 +238,37 @@ session (`initDeezerApi`).
|
|
|
203
238
|
| `track` | Yes | `string` | track object |
|
|
204
239
|
| `quality` | Yes | `1, 3 or 9` | 1 = 128kbps, 3 = 320kbps, 9 = flac |
|
|
205
240
|
|
|
241
|
+
Resolves `{trackUrl, isEncrypted, fileSize}`. `isEncrypted` now comes from the
|
|
242
|
+
media API's `cipher` field (authoritative) rather than a URL guess.
|
|
243
|
+
|
|
244
|
+
### Formats
|
|
245
|
+
|
|
246
|
+
Deezer's `get_url` understands more than `1 / 3 / 9`. `DEEZER_FORMATS` lists them
|
|
247
|
+
best → worst: `FLAC`, `MP3_320`, `MP3_256`, `MP3_128`, `MP3_64`, `AAC_64`,
|
|
248
|
+
`MP4_RA3`, `MP4_RA2`, `MP4_RA1` (the last four are the HE-AAC ladder some
|
|
249
|
+
accounts / regions expose).
|
|
250
|
+
|
|
251
|
+
- **`resolveDownloadUrls(tracks, qualities)`** — `qualities` entries may be the
|
|
252
|
+
`1 | 3 | 9` shorthand **or** any format string, e.g.
|
|
253
|
+
`resolveDownloadUrls(tracks, ['FLAC', 'MP3_320', 'AAC_64'])`. Deezer returns
|
|
254
|
+
the best each track is licensed for. Each result now also carries `format` and
|
|
255
|
+
`cipher` (`'BF_CBC_STRIPE'` or `'NONE'`).
|
|
256
|
+
- **`formatName(quality)`** / **`toFormat(quality)`** — normalise a number or
|
|
257
|
+
format string to the `get_url` format string.
|
|
258
|
+
|
|
259
|
+
### `.getTrackPreview(track)` / `.downloadPreview(track)`
|
|
260
|
+
|
|
261
|
+
The 30-second preview clip — a plain MP3, **no licence, no `arl`, no
|
|
262
|
+
encryption**. `track` may be a gw track object (reads its `MEDIA`, no extra
|
|
263
|
+
request), a track id, or a number.
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
const {url} = await getTrackPreview('3135556'); // {url, duration: 30}
|
|
267
|
+
const clip = await downloadPreview('3135556'); // Buffer (ID3-tagged MP3)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Useful for "audition before download" and for CI that shouldn't pull full tracks.
|
|
271
|
+
|
|
206
272
|
### `.decryptDownload(data, song_id);`
|
|
207
273
|
|
|
208
274
|
| Parameters | Required | Type | Description |
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { albumTypePublicApi, artistAlbumResult, chartType, editorialType, genreType, publicApiList, searchResultArtist, searchResultPlaylist, searchResultTrack, trackTypePublicApi } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* The five ranked lists Deezer publishes for a genre: `tracks`, `albums`,
|
|
4
|
+
* `artists`, `playlists`, `podcasts`. `genreId` `0` (the default) is "all
|
|
5
|
+
* genres"; other ids come from {@link getGenres}.
|
|
6
|
+
*/
|
|
7
|
+
export declare const getChart: (genreId?: number | string, limit?: number) => Promise<chartType>;
|
|
8
|
+
/** Just the track chart for a genre (`0` = all). Handy as a ready-to-play list. */
|
|
9
|
+
export declare const getChartTracks: (genreId?: number | string, limit?: number, index?: number) => Promise<publicApiList<searchResultTrack & {
|
|
10
|
+
position: number;
|
|
11
|
+
}>>;
|
|
12
|
+
/** Deezer's genre list — the `id`s feed {@link getChart}, {@link getGenreArtists}, advanced search. */
|
|
13
|
+
export declare const getGenres: () => Promise<publicApiList<genreType>>;
|
|
14
|
+
/** Artists Deezer files under a genre. */
|
|
15
|
+
export declare const getGenreArtists: (genreId: number | string) => Promise<publicApiList<searchResultArtist>>;
|
|
16
|
+
/** Deezer's editorial sections (the `id`s feed {@link getEditorialReleases} / {@link getEditorialSelection}). */
|
|
17
|
+
export declare const getEditorialList: () => Promise<publicApiList<editorialType>>;
|
|
18
|
+
/** New releases for an editorial section (`0` = the default section). */
|
|
19
|
+
export declare const getEditorialReleases: (editorialId?: number | string, limit?: number, index?: number) => Promise<publicApiList<artistAlbumResult>>;
|
|
20
|
+
/** The albums Deezer's editors are currently pushing for a section. */
|
|
21
|
+
export declare const getEditorialSelection: (editorialId?: number | string) => Promise<publicApiList<artistAlbumResult>>;
|
|
22
|
+
/** The editorial charts for a section — same five-list shape as {@link getChart}. */
|
|
23
|
+
export declare const getEditorialCharts: (editorialId?: number | string) => Promise<chartType>;
|
|
24
|
+
/** An artist's most popular tracks. */
|
|
25
|
+
export declare const getArtistTopTracks: (artistId: number | string, limit?: number) => Promise<publicApiList<searchResultTrack>>;
|
|
26
|
+
/** Artists Deezer considers related / similar. */
|
|
27
|
+
export declare const getRelatedArtists: (artistId: number | string, limit?: number) => Promise<publicApiList<searchResultArtist>>;
|
|
28
|
+
/** An artist's discography (public-API album shape). */
|
|
29
|
+
export declare const getArtistAlbums: (artistId: number | string, limit?: number, index?: number) => Promise<publicApiList<artistAlbumResult>>;
|
|
30
|
+
/** Playlists featuring an artist. */
|
|
31
|
+
export declare const getArtistPlaylists: (artistId: number | string, limit?: number) => Promise<publicApiList<searchResultPlaylist>>;
|
|
32
|
+
/** A ready-made radio (track list) seeded from an artist. */
|
|
33
|
+
export declare const getArtistRadioTracks: (artistId: number | string) => Promise<publicApiList<searchResultTrack>>;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve an ISRC to the Deezer **public-API** track (with `bpm`, `gain`,
|
|
36
|
+
* `isrc`, `preview`, `contributors`). Unlike the converter's `isrc2deezer`, this
|
|
37
|
+
* does not hydrate a gw track — pass `result.id` to `getTrackInfo` for that.
|
|
38
|
+
*
|
|
39
|
+
* @throws when Deezer has no track for the code
|
|
40
|
+
*/
|
|
41
|
+
export declare const getTrackByISRC: (isrc: string) => Promise<trackTypePublicApi>;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a UPC / EAN barcode to the Deezer **public-API** album (with its
|
|
44
|
+
* `tracks`). A 13-digit barcode with a leading `0` is trimmed to 12, matching
|
|
45
|
+
* Deezer's own lookup.
|
|
46
|
+
*
|
|
47
|
+
* @throws when Deezer has no album for the code
|
|
48
|
+
*/
|
|
49
|
+
export declare const getAlbumByUPC: (upc: string) => Promise<albumTypePublicApi>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAlbumByUPC = exports.getTrackByISRC = exports.getArtistRadioTracks = exports.getArtistPlaylists = exports.getArtistAlbums = exports.getRelatedArtists = exports.getArtistTopTracks = exports.getEditorialCharts = exports.getEditorialSelection = exports.getEditorialReleases = exports.getEditorialList = exports.getGenreArtists = exports.getGenres = exports.getChartTracks = exports.getChart = void 0;
|
|
4
|
+
const request_1 = require("./request");
|
|
5
|
+
const withParams = (slug, params) => {
|
|
6
|
+
const search = new URLSearchParams();
|
|
7
|
+
for (const [key, value] of Object.entries(params)) {
|
|
8
|
+
if (value !== undefined && value !== '') {
|
|
9
|
+
search.set(key, String(value));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const qs = search.toString();
|
|
13
|
+
return qs ? `${slug}?${qs}` : slug;
|
|
14
|
+
};
|
|
15
|
+
// ─── Charts ──────────────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* The five ranked lists Deezer publishes for a genre: `tracks`, `albums`,
|
|
18
|
+
* `artists`, `playlists`, `podcasts`. `genreId` `0` (the default) is "all
|
|
19
|
+
* genres"; other ids come from {@link getGenres}.
|
|
20
|
+
*/
|
|
21
|
+
const getChart = (genreId = 0, limit = 10) => (0, request_1.requestPublicApi)(withParams(`/chart/${genreId}`, { limit }));
|
|
22
|
+
exports.getChart = getChart;
|
|
23
|
+
/** Just the track chart for a genre (`0` = all). Handy as a ready-to-play list. */
|
|
24
|
+
const getChartTracks = (genreId = 0, limit = 100, index = 0) => (0, request_1.requestPublicApi)(withParams(`/chart/${genreId}/tracks`, { limit, index }));
|
|
25
|
+
exports.getChartTracks = getChartTracks;
|
|
26
|
+
// ─── Genres & editorial ──────────────────────────────────────────────────────
|
|
27
|
+
/** Deezer's genre list — the `id`s feed {@link getChart}, {@link getGenreArtists}, advanced search. */
|
|
28
|
+
const getGenres = () => (0, request_1.requestPublicApi)('/genre');
|
|
29
|
+
exports.getGenres = getGenres;
|
|
30
|
+
/** Artists Deezer files under a genre. */
|
|
31
|
+
const getGenreArtists = (genreId) => (0, request_1.requestPublicApi)(`/genre/${genreId}/artists`);
|
|
32
|
+
exports.getGenreArtists = getGenreArtists;
|
|
33
|
+
/** Deezer's editorial sections (the `id`s feed {@link getEditorialReleases} / {@link getEditorialSelection}). */
|
|
34
|
+
const getEditorialList = () => (0, request_1.requestPublicApi)('/editorial');
|
|
35
|
+
exports.getEditorialList = getEditorialList;
|
|
36
|
+
/** New releases for an editorial section (`0` = the default section). */
|
|
37
|
+
const getEditorialReleases = (editorialId = 0, limit = 25, index = 0) => (0, request_1.requestPublicApi)(withParams(`/editorial/${editorialId}/releases`, { limit, index }));
|
|
38
|
+
exports.getEditorialReleases = getEditorialReleases;
|
|
39
|
+
/** The albums Deezer's editors are currently pushing for a section. */
|
|
40
|
+
const getEditorialSelection = (editorialId = 0) => (0, request_1.requestPublicApi)(`/editorial/${editorialId}/selection`);
|
|
41
|
+
exports.getEditorialSelection = getEditorialSelection;
|
|
42
|
+
/** The editorial charts for a section — same five-list shape as {@link getChart}. */
|
|
43
|
+
const getEditorialCharts = (editorialId = 0) => (0, request_1.requestPublicApi)(`/editorial/${editorialId}/charts`);
|
|
44
|
+
exports.getEditorialCharts = getEditorialCharts;
|
|
45
|
+
// ─── Artist discovery ────────────────────────────────────────────────────────
|
|
46
|
+
/** An artist's most popular tracks. */
|
|
47
|
+
const getArtistTopTracks = (artistId, limit = 50) => (0, request_1.requestPublicApi)(withParams(`/artist/${artistId}/top`, { limit }));
|
|
48
|
+
exports.getArtistTopTracks = getArtistTopTracks;
|
|
49
|
+
/** Artists Deezer considers related / similar. */
|
|
50
|
+
const getRelatedArtists = (artistId, limit = 20) => (0, request_1.requestPublicApi)(withParams(`/artist/${artistId}/related`, { limit }));
|
|
51
|
+
exports.getRelatedArtists = getRelatedArtists;
|
|
52
|
+
/** An artist's discography (public-API album shape). */
|
|
53
|
+
const getArtistAlbums = (artistId, limit = 50, index = 0) => (0, request_1.requestPublicApi)(withParams(`/artist/${artistId}/albums`, { limit, index }));
|
|
54
|
+
exports.getArtistAlbums = getArtistAlbums;
|
|
55
|
+
/** Playlists featuring an artist. */
|
|
56
|
+
const getArtistPlaylists = (artistId, limit = 25) => (0, request_1.requestPublicApi)(withParams(`/artist/${artistId}/playlists`, { limit }));
|
|
57
|
+
exports.getArtistPlaylists = getArtistPlaylists;
|
|
58
|
+
/** A ready-made radio (track list) seeded from an artist. */
|
|
59
|
+
const getArtistRadioTracks = (artistId) => (0, request_1.requestPublicApi)(`/artist/${artistId}/radio`);
|
|
60
|
+
exports.getArtistRadioTracks = getArtistRadioTracks;
|
|
61
|
+
// ─── ISRC / UPC resolution ───────────────────────────────────────────────────
|
|
62
|
+
/**
|
|
63
|
+
* Resolve an ISRC to the Deezer **public-API** track (with `bpm`, `gain`,
|
|
64
|
+
* `isrc`, `preview`, `contributors`). Unlike the converter's `isrc2deezer`, this
|
|
65
|
+
* does not hydrate a gw track — pass `result.id` to `getTrackInfo` for that.
|
|
66
|
+
*
|
|
67
|
+
* @throws when Deezer has no track for the code
|
|
68
|
+
*/
|
|
69
|
+
const getTrackByISRC = (isrc) => (0, request_1.requestPublicApi)(`/track/isrc:${encodeURIComponent(isrc)}`);
|
|
70
|
+
exports.getTrackByISRC = getTrackByISRC;
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a UPC / EAN barcode to the Deezer **public-API** album (with its
|
|
73
|
+
* `tracks`). A 13-digit barcode with a leading `0` is trimmed to 12, matching
|
|
74
|
+
* Deezer's own lookup.
|
|
75
|
+
*
|
|
76
|
+
* @throws when Deezer has no album for the code
|
|
77
|
+
*/
|
|
78
|
+
const getAlbumByUPC = (upc) => {
|
|
79
|
+
const code = upc.length > 12 && upc.startsWith('0') ? upc.slice(-12) : upc;
|
|
80
|
+
return (0, request_1.requestPublicApi)(`/album/upc:${encodeURIComponent(code)}`);
|
|
81
|
+
};
|
|
82
|
+
exports.getAlbumByUPC = getAlbumByUPC;
|
package/dist/api/index.d.ts
CHANGED
package/dist/api/index.js
CHANGED
|
@@ -17,3 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./api"), exports);
|
|
18
18
|
__exportStar(require("./request"), exports);
|
|
19
19
|
__exportStar(require("./search"), exports);
|
|
20
|
+
__exportStar(require("./browse"), exports);
|
|
21
|
+
__exportStar(require("./preview"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { trackType } from '../types';
|
|
3
|
+
export interface TrackPreview {
|
|
4
|
+
/** direct URL to a ~30 s MP3 clip — no licence, no encryption, no `arl` */
|
|
5
|
+
url: string;
|
|
6
|
+
/** clip length in seconds (Deezer previews are 30 s) */
|
|
7
|
+
duration: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The 30-second preview clip for a track. Deezer exposes it two ways:
|
|
11
|
+
* `song.getData` carries it in `MEDIA` (`{TYPE: 'preview', HREF}`), and the
|
|
12
|
+
* public `/track/` endpoint carries it as `preview`. Pass a gw `track` object
|
|
13
|
+
* (uses `MEDIA`, no extra request) or a track id (one public-API lookup).
|
|
14
|
+
*
|
|
15
|
+
* The clip is a plain MP3 — never Blowfish-encrypted — so it needs no
|
|
16
|
+
* `decryptDownload` and is safe to use in tests and "audition before download"
|
|
17
|
+
* flows.
|
|
18
|
+
*/
|
|
19
|
+
export declare const getTrackPreview: (track: trackType | string | number) => Promise<TrackPreview | null>;
|
|
20
|
+
/** Fetch the 30-second preview clip as a `Buffer` (plain MP3, no decryption needed). */
|
|
21
|
+
export declare const downloadPreview: (track: trackType | string | number) => Promise<Buffer | null>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.downloadPreview = exports.getTrackPreview = void 0;
|
|
4
|
+
const http_1 = require("../lib/http");
|
|
5
|
+
const api_1 = require("./api");
|
|
6
|
+
/**
|
|
7
|
+
* The 30-second preview clip for a track. Deezer exposes it two ways:
|
|
8
|
+
* `song.getData` carries it in `MEDIA` (`{TYPE: 'preview', HREF}`), and the
|
|
9
|
+
* public `/track/` endpoint carries it as `preview`. Pass a gw `track` object
|
|
10
|
+
* (uses `MEDIA`, no extra request) or a track id (one public-API lookup).
|
|
11
|
+
*
|
|
12
|
+
* The clip is a plain MP3 — never Blowfish-encrypted — so it needs no
|
|
13
|
+
* `decryptDownload` and is safe to use in tests and "audition before download"
|
|
14
|
+
* flows.
|
|
15
|
+
*/
|
|
16
|
+
const getTrackPreview = async (track) => {
|
|
17
|
+
var _a;
|
|
18
|
+
if (typeof track === 'object') {
|
|
19
|
+
const media = (_a = track.MEDIA) === null || _a === void 0 ? void 0 : _a[0];
|
|
20
|
+
if ((media === null || media === void 0 ? void 0 : media.HREF) && (!media.TYPE || media.TYPE === 'preview')) {
|
|
21
|
+
return { url: media.HREF, duration: 30 };
|
|
22
|
+
}
|
|
23
|
+
const pub = await (0, api_1.getTrackInfoPublicApi)(track.SNG_ID);
|
|
24
|
+
return pub.preview ? { url: pub.preview, duration: 30 } : null;
|
|
25
|
+
}
|
|
26
|
+
const pub = await (0, api_1.getTrackInfoPublicApi)(String(track));
|
|
27
|
+
return pub.preview ? { url: pub.preview, duration: 30 } : null;
|
|
28
|
+
};
|
|
29
|
+
exports.getTrackPreview = getTrackPreview;
|
|
30
|
+
/** Fetch the 30-second preview clip as a `Buffer` (plain MP3, no decryption needed). */
|
|
31
|
+
const downloadPreview = async (track) => {
|
|
32
|
+
const preview = await (0, exports.getTrackPreview)(track);
|
|
33
|
+
if (!preview)
|
|
34
|
+
return null;
|
|
35
|
+
return (0, http_1.getBuffer)(preview.url);
|
|
36
|
+
};
|
|
37
|
+
exports.downloadPreview = downloadPreview;
|
package/dist/lib/get-url.d.ts
CHANGED
|
@@ -13,8 +13,24 @@ export declare class ExpiredTrackToken extends Error {
|
|
|
13
13
|
readonly sngId: string;
|
|
14
14
|
constructor(sngId: string);
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Every audio format Deezer's `get_url` understands, best → worst. `FLAC`,
|
|
18
|
+
* `MP3_320` and `MP3_128` are the classic `9 / 3 / 1` qualities; the rest are
|
|
19
|
+
* additional tiers (`MP3_256`, `MP3_64`, the `AAC_64` / `MP4_RA*` HE-AAC ladder)
|
|
20
|
+
* that some accounts and regions expose.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEEZER_FORMATS: readonly ["FLAC", "MP3_320", "MP3_256", "MP3_128", "MP3_64", "AAC_64", "MP4_RA3", "MP4_RA2", "MP4_RA1"];
|
|
23
|
+
export type DeezerFormat = (typeof DEEZER_FORMATS)[number];
|
|
24
|
+
/** A quality request: the numeric `1 | 3 | 9` shorthand, or any format string. */
|
|
25
|
+
export type Quality = number | DeezerFormat | string;
|
|
26
|
+
/** Normalise a {@link Quality} (number shorthand or format string) to a format string. */
|
|
27
|
+
export declare const toFormat: (quality: Quality) => string;
|
|
28
|
+
/**
|
|
29
|
+
* Quality code (`1 | 3 | 9`) or format string → the `format` string the media
|
|
30
|
+
* API expects. Format strings pass straight through, so this doubles as a
|
|
31
|
+
* validator-free identity for `'AAC_64'`, `'MP3_256'`, …
|
|
32
|
+
*/
|
|
33
|
+
export declare const formatName: (quality: Quality) => string;
|
|
18
34
|
/**
|
|
19
35
|
* @param track Track info json returned from `getTrackInfo`
|
|
20
36
|
* @param quality 1 = 128kbps, 3 = 320kbps and 9 = flac (around 1411kbps)
|
|
@@ -28,18 +44,21 @@ export interface ResolvedUrl {
|
|
|
28
44
|
trackUrl: string;
|
|
29
45
|
isEncrypted: boolean;
|
|
30
46
|
fileSize: number;
|
|
31
|
-
/** the format Deezer actually returned, e.g. `'FLAC'` */
|
|
47
|
+
/** the format Deezer actually returned, e.g. `'FLAC'`, `'MP3_128'`, `'AAC_64'` */
|
|
32
48
|
format: string;
|
|
49
|
+
/** the cipher Deezer applied — `'BF_CBC_STRIPE'` (stripe-encrypted) or `'NONE'` */
|
|
50
|
+
cipher: string;
|
|
33
51
|
}
|
|
34
52
|
/**
|
|
35
53
|
* Resolve download URLs for many tracks in a **single** `get_url` request.
|
|
36
54
|
*
|
|
37
|
-
* `qualities` is an ordered preference list
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
55
|
+
* `qualities` is an ordered preference list: Deezer returns the best each track
|
|
56
|
+
* is licensed for, so there is no per-quality retry. Entries may be the numeric
|
|
57
|
+
* `1 | 3 | 9` shorthand **or** any format string from {@link DEEZER_FORMATS}
|
|
58
|
+
* (e.g. `['FLAC', 'MP3_320', 'AAC_64']`). The result has one entry per input
|
|
59
|
+
* track, in order; `null` for a track that is geo-blocked, unavailable, or errored.
|
|
41
60
|
*
|
|
42
61
|
* @param tracks from `getTrackInfo` / `parseInfo` (needs `TRACK_TOKEN`, `SNG_ID`, `FILESIZE_*`)
|
|
43
|
-
* @param qualities preference order — 9
|
|
62
|
+
* @param qualities preference order — default `[9, 3, 1]` (FLAC → MP3 320 → MP3 128)
|
|
44
63
|
*/
|
|
45
|
-
export declare const resolveDownloadUrls: (tracks: trackType[], qualities?:
|
|
64
|
+
export declare const resolveDownloadUrls: (tracks: trackType[], qualities?: Quality[]) => Promise<(ResolvedUrl | null)[]>;
|
package/dist/lib/get-url.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveDownloadUrls = exports.getTrackDownloadUrl = exports.formatName = exports.ExpiredTrackToken = exports.GeoBlocked = exports.WrongLicense = void 0;
|
|
6
|
+
exports.resolveDownloadUrls = exports.getTrackDownloadUrl = exports.formatName = exports.toFormat = exports.DEEZER_FORMATS = exports.ExpiredTrackToken = exports.GeoBlocked = exports.WrongLicense = void 0;
|
|
7
7
|
const delay_1 = __importDefault(require("delay"));
|
|
8
8
|
const decrypt_1 = require("../lib/decrypt");
|
|
9
9
|
const http_1 = require("../lib/http");
|
|
@@ -37,17 +37,50 @@ class ExpiredTrackToken extends Error {
|
|
|
37
37
|
}
|
|
38
38
|
exports.ExpiredTrackToken = ExpiredTrackToken;
|
|
39
39
|
let user_data = null;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Every audio format Deezer's `get_url` understands, best → worst. `FLAC`,
|
|
42
|
+
* `MP3_320` and `MP3_128` are the classic `9 / 3 / 1` qualities; the rest are
|
|
43
|
+
* additional tiers (`MP3_256`, `MP3_64`, the `AAC_64` / `MP4_RA*` HE-AAC ladder)
|
|
44
|
+
* that some accounts and regions expose.
|
|
45
|
+
*/
|
|
46
|
+
exports.DEEZER_FORMATS = [
|
|
47
|
+
'FLAC',
|
|
48
|
+
'MP3_320',
|
|
49
|
+
'MP3_256',
|
|
50
|
+
'MP3_128',
|
|
51
|
+
'MP3_64',
|
|
52
|
+
'AAC_64',
|
|
53
|
+
'MP4_RA3',
|
|
54
|
+
'MP4_RA2',
|
|
55
|
+
'MP4_RA1',
|
|
56
|
+
];
|
|
57
|
+
const FORMAT_FILESIZE_KEY = {
|
|
58
|
+
FLAC: 'FILESIZE_FLAC',
|
|
59
|
+
MP3_320: 'FILESIZE_MP3_320',
|
|
60
|
+
MP3_256: 'FILESIZE_MP3_256',
|
|
61
|
+
MP3_128: 'FILESIZE_MP3_128',
|
|
62
|
+
MP3_64: 'FILESIZE_MP3_64',
|
|
63
|
+
MP3_MISC: 'FILESIZE_MP3_MISC',
|
|
64
|
+
AAC_64: 'FILESIZE_AAC_64',
|
|
65
|
+
MP4_RA1: 'FILESIZE_MP4_RA1',
|
|
66
|
+
MP4_RA2: 'FILESIZE_MP4_RA2',
|
|
67
|
+
MP4_RA3: 'FILESIZE_MP4_RA3',
|
|
68
|
+
};
|
|
69
|
+
const NUMERIC_FORMAT = { 9: 'FLAC', 3: 'MP3_320', 1: 'MP3_128' };
|
|
70
|
+
/** Normalise a {@link Quality} (number shorthand or format string) to a format string. */
|
|
71
|
+
const toFormat = (quality) => {
|
|
72
|
+
if (typeof quality === 'number') {
|
|
73
|
+
const f = NUMERIC_FORMAT[quality];
|
|
74
|
+
if (!f)
|
|
75
|
+
throw new Error(`Unknown quality ${quality}`);
|
|
76
|
+
return f;
|
|
50
77
|
}
|
|
78
|
+
return quality;
|
|
79
|
+
};
|
|
80
|
+
exports.toFormat = toFormat;
|
|
81
|
+
const getTrackFileSize = (track, quality) => {
|
|
82
|
+
const key = FORMAT_FILESIZE_KEY[(0, exports.toFormat)(quality)];
|
|
83
|
+
return key ? Number(track[key]) || 0 : 0;
|
|
51
84
|
};
|
|
52
85
|
const dzAuthenticate = async () => {
|
|
53
86
|
const { data } = await request_1.default.get('https://www.deezer.com/ajax/gw-light.php', {
|
|
@@ -66,19 +99,12 @@ const dzAuthenticate = async () => {
|
|
|
66
99
|
return user_data;
|
|
67
100
|
};
|
|
68
101
|
const MEDIA_MAX_RETRIES = 3;
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return 'MP3_320';
|
|
76
|
-
case 1:
|
|
77
|
-
return 'MP3_128';
|
|
78
|
-
default:
|
|
79
|
-
throw new Error(`Unknown quality ${quality}`);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
102
|
+
/**
|
|
103
|
+
* Quality code (`1 | 3 | 9`) or format string → the `format` string the media
|
|
104
|
+
* API expects. Format strings pass straight through, so this doubles as a
|
|
105
|
+
* validator-free identity for `'AAC_64'`, `'MP3_256'`, …
|
|
106
|
+
*/
|
|
107
|
+
const formatName = (quality) => (0, exports.toFormat)(quality);
|
|
82
108
|
exports.formatName = formatName;
|
|
83
109
|
/** POST media.deezer.com/v1/get_url with re-auth + exponential-backoff retry on 403/429/5xx. */
|
|
84
110
|
const mediaGetUrl = async (track_tokens, formats, attempt = 0) => {
|
|
@@ -104,7 +130,7 @@ const mediaGetUrl = async (track_tokens, formats, attempt = 0) => {
|
|
|
104
130
|
};
|
|
105
131
|
/** Parse one `data[i]` entry from a get_url response into a source URL (or null / throw). */
|
|
106
132
|
const parseMediaEntry = (entry, token, country) => {
|
|
107
|
-
var _a, _b;
|
|
133
|
+
var _a, _b, _c, _d;
|
|
108
134
|
if (entry === null || entry === void 0 ? void 0 : entry.errors) {
|
|
109
135
|
const { code } = entry.errors[0];
|
|
110
136
|
if (code === 2002)
|
|
@@ -116,10 +142,11 @@ const parseMediaEntry = (entry, token, country) => {
|
|
|
116
142
|
const media = (_a = entry === null || entry === void 0 ? void 0 : entry.media) === null || _a === void 0 ? void 0 : _a[0];
|
|
117
143
|
if (!((_b = media === null || media === void 0 ? void 0 : media.sources) === null || _b === void 0 ? void 0 : _b.length))
|
|
118
144
|
return null;
|
|
119
|
-
return { url: media.sources[0].url, format: media.format };
|
|
145
|
+
return { url: media.sources[0].url, format: media.format, cipher: (_d = (_c = media.cipher) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : 'BF_CBC_STRIPE' };
|
|
120
146
|
};
|
|
147
|
+
/** Whether a track fetched with the given cipher needs Blowfish stripe decryption. */
|
|
148
|
+
const cipherIsEncrypted = (cipher, url) => cipher ? cipher !== 'NONE' : url.includes('/mobile/') || url.includes('/media/');
|
|
121
149
|
const getTrackUrlFromServer = async (track_token, format) => {
|
|
122
|
-
var _a, _b;
|
|
123
150
|
const user = user_data ? user_data : await dzAuthenticate();
|
|
124
151
|
if ((format === 'FLAC' && !user.can_stream_lossless) || (format === 'MP3_320' && !user.can_stream_hq)) {
|
|
125
152
|
throw new WrongLicense(format);
|
|
@@ -127,7 +154,7 @@ const getTrackUrlFromServer = async (track_token, format) => {
|
|
|
127
154
|
const { data, country } = await mediaGetUrl([track_token], [{ format, cipher: 'BF_CBC_STRIPE' }]);
|
|
128
155
|
if (!data.length)
|
|
129
156
|
return null;
|
|
130
|
-
return
|
|
157
|
+
return parseMediaEntry(data[0], track_token, country);
|
|
131
158
|
};
|
|
132
159
|
/**
|
|
133
160
|
* @param track Track info json returned from `getTrackInfo`
|
|
@@ -148,11 +175,11 @@ const getTrackDownloadUrl = async (track, quality) => {
|
|
|
148
175
|
else {
|
|
149
176
|
// Get URL with the official API
|
|
150
177
|
try {
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
178
|
+
const resolved = await getTrackUrlFromServer(track.TRACK_TOKEN, format);
|
|
179
|
+
if (resolved) {
|
|
153
180
|
return {
|
|
154
|
-
trackUrl: url,
|
|
155
|
-
isEncrypted:
|
|
181
|
+
trackUrl: resolved.url,
|
|
182
|
+
isEncrypted: cipherIsEncrypted(resolved.cipher, resolved.url),
|
|
156
183
|
fileSize: getTrackFileSize(track, quality),
|
|
157
184
|
};
|
|
158
185
|
}
|
|
@@ -213,17 +240,17 @@ const testUrl = async (url) => {
|
|
|
213
240
|
return 0;
|
|
214
241
|
}
|
|
215
242
|
};
|
|
216
|
-
const QUALITY_OF_FORMAT = { FLAC: 9, MP3_320: 3, MP3_128: 1, MP3_256: 3, MP3_64: 1 };
|
|
217
243
|
/**
|
|
218
244
|
* Resolve download URLs for many tracks in a **single** `get_url` request.
|
|
219
245
|
*
|
|
220
|
-
* `qualities` is an ordered preference list
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
246
|
+
* `qualities` is an ordered preference list: Deezer returns the best each track
|
|
247
|
+
* is licensed for, so there is no per-quality retry. Entries may be the numeric
|
|
248
|
+
* `1 | 3 | 9` shorthand **or** any format string from {@link DEEZER_FORMATS}
|
|
249
|
+
* (e.g. `['FLAC', 'MP3_320', 'AAC_64']`). The result has one entry per input
|
|
250
|
+
* track, in order; `null` for a track that is geo-blocked, unavailable, or errored.
|
|
224
251
|
*
|
|
225
252
|
* @param tracks from `getTrackInfo` / `parseInfo` (needs `TRACK_TOKEN`, `SNG_ID`, `FILESIZE_*`)
|
|
226
|
-
* @param qualities preference order — 9
|
|
253
|
+
* @param qualities preference order — default `[9, 3, 1]` (FLAC → MP3 320 → MP3 128)
|
|
227
254
|
*/
|
|
228
255
|
const resolveDownloadUrls = async (tracks, qualities = [9, 3, 1]) => {
|
|
229
256
|
if (!tracks.length)
|
|
@@ -231,7 +258,6 @@ const resolveDownloadUrls = async (tracks, qualities = [9, 3, 1]) => {
|
|
|
231
258
|
const formats = qualities.map((q) => ({ format: (0, exports.formatName)(q), cipher: 'BF_CBC_STRIPE' }));
|
|
232
259
|
const { data, country } = await mediaGetUrl(tracks.map((t) => t.TRACK_TOKEN), formats);
|
|
233
260
|
return tracks.map((track, i) => {
|
|
234
|
-
var _a;
|
|
235
261
|
let parsed;
|
|
236
262
|
try {
|
|
237
263
|
parsed = parseMediaEntry(data[i], track.TRACK_TOKEN, country);
|
|
@@ -242,12 +268,12 @@ const resolveDownloadUrls = async (tracks, qualities = [9, 3, 1]) => {
|
|
|
242
268
|
}
|
|
243
269
|
if (!parsed)
|
|
244
270
|
return null;
|
|
245
|
-
const q = (_a = QUALITY_OF_FORMAT[parsed.format]) !== null && _a !== void 0 ? _a : qualities[0];
|
|
246
271
|
return {
|
|
247
272
|
trackUrl: parsed.url,
|
|
248
|
-
isEncrypted: parsed.
|
|
249
|
-
fileSize: getTrackFileSize(track,
|
|
273
|
+
isEncrypted: cipherIsEncrypted(parsed.cipher, parsed.url),
|
|
274
|
+
fileSize: getTrackFileSize(track, parsed.format),
|
|
250
275
|
format: parsed.format,
|
|
276
|
+
cipher: parsed.cipher,
|
|
251
277
|
};
|
|
252
278
|
});
|
|
253
279
|
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { searchResultAlbum, searchResultArtist, searchResultPlaylist, searchResultTrack } from './search';
|
|
2
|
+
/** The `{data, total, next?, prev?}` envelope every paginated public-API list uses. */
|
|
3
|
+
export interface publicApiList<T> {
|
|
4
|
+
data: T[];
|
|
5
|
+
total?: number;
|
|
6
|
+
/** absolute URL of the next page, when there is one */
|
|
7
|
+
next?: string;
|
|
8
|
+
prev?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface chartTrack extends searchResultTrack {
|
|
11
|
+
/** 1-based position in the chart */
|
|
12
|
+
position: number;
|
|
13
|
+
}
|
|
14
|
+
export interface chartArtist extends searchResultArtist {
|
|
15
|
+
position: number;
|
|
16
|
+
}
|
|
17
|
+
export interface chartAlbum extends searchResultAlbum {
|
|
18
|
+
position: number;
|
|
19
|
+
}
|
|
20
|
+
export interface chartPlaylist extends searchResultPlaylist {
|
|
21
|
+
position: number;
|
|
22
|
+
}
|
|
23
|
+
export interface chartPodcast {
|
|
24
|
+
id: number;
|
|
25
|
+
title: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
available?: boolean;
|
|
28
|
+
fans?: number;
|
|
29
|
+
link?: string;
|
|
30
|
+
share?: string;
|
|
31
|
+
picture: string;
|
|
32
|
+
picture_small?: string;
|
|
33
|
+
picture_medium?: string;
|
|
34
|
+
picture_big?: string;
|
|
35
|
+
picture_xl?: string;
|
|
36
|
+
position: number;
|
|
37
|
+
type: 'podcast';
|
|
38
|
+
}
|
|
39
|
+
/** `/chart/{genreId}` — the five ranked lists Deezer publishes per genre (`0` = all genres). */
|
|
40
|
+
export interface chartType {
|
|
41
|
+
tracks: publicApiList<chartTrack>;
|
|
42
|
+
albums: publicApiList<chartAlbum>;
|
|
43
|
+
artists: publicApiList<chartArtist>;
|
|
44
|
+
playlists: publicApiList<chartPlaylist>;
|
|
45
|
+
podcasts: publicApiList<chartPodcast>;
|
|
46
|
+
}
|
|
47
|
+
export interface genreType {
|
|
48
|
+
id: number;
|
|
49
|
+
name: string;
|
|
50
|
+
picture: string;
|
|
51
|
+
picture_small?: string;
|
|
52
|
+
picture_medium?: string;
|
|
53
|
+
picture_big?: string;
|
|
54
|
+
picture_xl?: string;
|
|
55
|
+
type: 'genre';
|
|
56
|
+
}
|
|
57
|
+
export interface editorialType {
|
|
58
|
+
id: number;
|
|
59
|
+
name: string;
|
|
60
|
+
picture: string;
|
|
61
|
+
picture_small?: string;
|
|
62
|
+
picture_medium?: string;
|
|
63
|
+
picture_big?: string;
|
|
64
|
+
picture_xl?: string;
|
|
65
|
+
type: 'editorial';
|
|
66
|
+
}
|
|
67
|
+
/** An album from `/artist/{id}/albums` — the artist's own discography, public-API shape. */
|
|
68
|
+
export interface artistAlbumResult {
|
|
69
|
+
id: number;
|
|
70
|
+
title: string;
|
|
71
|
+
link: string;
|
|
72
|
+
cover: string;
|
|
73
|
+
cover_small?: string;
|
|
74
|
+
cover_medium?: string;
|
|
75
|
+
cover_big?: string;
|
|
76
|
+
cover_xl?: string;
|
|
77
|
+
md5_image: string;
|
|
78
|
+
genre_id: number;
|
|
79
|
+
fans?: number;
|
|
80
|
+
release_date: string;
|
|
81
|
+
record_type: string;
|
|
82
|
+
tracklist: string;
|
|
83
|
+
explicit_lyrics: boolean;
|
|
84
|
+
type: 'album';
|
|
85
|
+
}
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./album"), exports);
|
|
18
18
|
__exportStar(require("./artist"), exports);
|
|
19
|
+
__exportStar(require("./browse"), exports);
|
|
19
20
|
__exportStar(require("./show"), exports);
|
|
20
21
|
__exportStar(require("./playlist"), exports);
|
|
21
22
|
__exportStar(require("./playlist-channel"), exports);
|
package/dist/types/tracks.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { artistType } from './artist';
|
|
2
2
|
interface mediaType {
|
|
3
|
-
TYPE: 'preview';
|
|
3
|
+
TYPE: 'preview' | string;
|
|
4
4
|
HREF: string;
|
|
5
5
|
}
|
|
6
6
|
export interface lyricsSync {
|
|
@@ -59,15 +59,19 @@ interface songType {
|
|
|
59
59
|
URL_REWRITING: string;
|
|
60
60
|
VERSION?: string;
|
|
61
61
|
MD5_ORIGIN?: string;
|
|
62
|
-
FILESIZE_AAC_64:
|
|
62
|
+
FILESIZE_AAC_64: string;
|
|
63
63
|
FILESIZE_MP3_64: string;
|
|
64
64
|
FILESIZE_MP3_128: string;
|
|
65
|
-
FILESIZE_MP3_256:
|
|
66
|
-
FILESIZE_MP3_320:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
FILESIZE_MP3_256: string;
|
|
66
|
+
FILESIZE_MP3_320: string;
|
|
67
|
+
FILESIZE_MP3_MISC?: string;
|
|
68
|
+
FILESIZE_MP4_RA1: string;
|
|
69
|
+
FILESIZE_MP4_RA2: string;
|
|
70
|
+
FILESIZE_MP4_RA3: string;
|
|
71
|
+
FILESIZE_MHM1_RA1?: string;
|
|
72
|
+
FILESIZE_MHM1_RA2?: string;
|
|
73
|
+
FILESIZE_MHM1_RA3?: string;
|
|
74
|
+
FILESIZE_FLAC: string;
|
|
71
75
|
FILESIZE: string;
|
|
72
76
|
GAIN: string;
|
|
73
77
|
MEDIA_VERSION: string;
|