gerdur-core 2.0.0 → 2.2.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 +51 -0
- package/README.md +69 -0
- package/dist/api/api.js +4 -1
- 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/search.d.ts +49 -0
- package/dist/api/search.js +110 -0
- 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/search.d.ts +157 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.0 - 2026-08-31
|
|
4
|
+
|
|
5
|
+
Phase 2.4 — browse & discovery. All additive, all on the public REST API (no
|
|
6
|
+
`arl` needed), all memoised.
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Charts / editorial**: `getGenres`, `getChart(genreId, limit)` (the five
|
|
11
|
+
ranked lists), `getChartTracks`, `getGenreArtists`, `getEditorialList`,
|
|
12
|
+
`getEditorialReleases`, `getEditorialSelection`, `getEditorialCharts`.
|
|
13
|
+
- **Artist discovery**: `getArtistTopTracks`, `getRelatedArtists`,
|
|
14
|
+
`getArtistAlbums`, `getArtistPlaylists`, `getArtistRadioTracks`.
|
|
15
|
+
- **ISRC / UPC resolution**: `getTrackByISRC(isrc)` and `getAlbumByUPC(upc)` —
|
|
16
|
+
raw public-API track/album (with `bpm`, `gain`, `preview`, embedded `tracks`).
|
|
17
|
+
Complements the converter's `isrc2deezer` / `upc2deezer`, which hydrate a gw
|
|
18
|
+
track instead.
|
|
19
|
+
- New exported types: `chartType`, `chartTrack`/`chartAlbum`/`chartArtist`/
|
|
20
|
+
`chartPlaylist`/`chartPodcast`, `genreType`, `editorialType`,
|
|
21
|
+
`artistAlbumResult`, `publicApiList<T>`.
|
|
22
|
+
|
|
23
|
+
## 2.1.0 - 2026-08-31
|
|
24
|
+
|
|
25
|
+
Phase 2.5 — search, properly. All additive.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **`searchPublicApi(query, options?)`** — search the public REST API
|
|
30
|
+
(`api.deezer.com/search`) rather than the internal `pageSearch` gateway.
|
|
31
|
+
Returns clean public-API objects (`isrc`, `preview`, `rank`, numeric ids) and
|
|
32
|
+
takes `type` (`track` | `album` | `artist` | `playlist` | `user` | `radio` |
|
|
33
|
+
`podcast`), `order`, `strict`, and `limit` / `index` paging. No auth required.
|
|
34
|
+
- **`searchTracks` / `searchAlbums` / `searchArtists` / `searchPlaylists`** —
|
|
35
|
+
thin typed wrappers over `searchPublicApi` with the entity fixed.
|
|
36
|
+
- **`buildAdvancedQuery(filters)`** — pure helper that composes Deezer's advanced
|
|
37
|
+
operators into one query string: `{artist, album, track, label}` →
|
|
38
|
+
`artist:"…"`, `{durMin, durMax, bpmMin, bpmMax}` → `dur_min:NNN` / `bpm_min:NNN`,
|
|
39
|
+
free-text `query` first. Reliable on the track index only (Deezer's
|
|
40
|
+
`/search/album` and `/search/artist` ignore the operators).
|
|
41
|
+
- **`suggest(query, nb?)`** — the `deezer.suggest` autocomplete endpoint, for
|
|
42
|
+
"as you type" UIs; cheaper than a full `searchMusic`.
|
|
43
|
+
- New exported types: `advancedSearchFilters`, `searchOrder`, `searchEntity`,
|
|
44
|
+
`publicApiSearchOptions`, `publicApiSearchResponse<T>`, `searchResultTrack`,
|
|
45
|
+
`searchResultAlbum`, `searchResultArtist`, `searchResultPlaylist`,
|
|
46
|
+
`suggestResult`.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- **`getPlaylistChannel`** returned `MISSING_PARAMETER_PAGE` — the nested
|
|
51
|
+
`gateway_input` was serialised as `[object Object]`. It is now JSON-stringified
|
|
52
|
+
before the request, so channel pages resolve again.
|
|
53
|
+
|
|
3
54
|
## 2.0.0 - 2026-08-31
|
|
4
55
|
|
|
5
56
|
Metadata overhaul — extract everything Deezer actually exposes for a track.
|
package/README.md
CHANGED
|
@@ -162,6 +162,75 @@ All method returns `Object` or throws `Error`. Make sure to catch error on your
|
|
|
162
162
|
| `types` | No | `array` | ['TRACK'] | array of search types |
|
|
163
163
|
| `limit` | No | `number` | 15 | maximum item to fetch per types |
|
|
164
164
|
|
|
165
|
+
### `.searchPublicApi(query, options?)` — and `.searchTracks` / `.searchAlbums` / `.searchArtists` / `.searchPlaylists`
|
|
166
|
+
|
|
167
|
+
Hits the **public** REST API (`api.deezer.com/search`) instead of the internal
|
|
168
|
+
`pageSearch` gateway. Returns clean public-API objects (`isrc`, `preview`,
|
|
169
|
+
`rank`, numeric ids), accepts the advanced query operators, an `order`, and
|
|
170
|
+
`limit` / `index` paging. No auth required.
|
|
171
|
+
|
|
172
|
+
| Parameters | Required | Type | Description |
|
|
173
|
+
| ---------------- | :------: | --------- | --------------------------------------------------------------------------- |
|
|
174
|
+
| `query` | Yes | `string` | plain text, or the output of `buildAdvancedQuery` |
|
|
175
|
+
| `options.type` | No | `string` | `'track'` (default), `'album'`, `'artist'`, `'playlist'`, `'user'`, `'radio'` |
|
|
176
|
+
| `options.order` | No | `string` | `RANKING`, `TRACK_ASC`, `RATING_DESC`, `DURATION_DESC`, … |
|
|
177
|
+
| `options.strict` | No | `boolean` | send Deezer's `strict=on` (disables the fuzzy fallback) |
|
|
178
|
+
| `options.limit` | No | `number` | page size (Deezer caps near 100) |
|
|
179
|
+
| `options.index` | No | `number` | offset into the result set |
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
const {data} = await searchTracks(buildAdvancedQuery({artist: 'daft punk', durMin: 200}), {limit: 25});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `.buildAdvancedQuery(filters)`
|
|
186
|
+
|
|
187
|
+
Pure helper — composes Deezer's advanced operators into one query string.
|
|
188
|
+
`{artist, album, track, label}` become `artist:"…"`; `{durMin, durMax, bpmMin, bpmMax}`
|
|
189
|
+
become `dur_min:NNN` / `bpm_min:NNN`; a free-text `query` is emitted first.
|
|
190
|
+
Reliable only on the **track** index — `/search/album` and `/search/artist`
|
|
191
|
+
ignore the operators, so pass a plain string there.
|
|
192
|
+
|
|
193
|
+
### `.suggest(query, nb?)`
|
|
194
|
+
|
|
195
|
+
`deezer.suggest` autocomplete — cheaper and faster than `searchMusic`, for
|
|
196
|
+
"as you type" UIs. `nb` (default 5) caps items per type. Needs an initialised
|
|
197
|
+
session (`initDeezerApi`).
|
|
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
|
+
|
|
165
234
|
### `.getTrackDownloadUrl(track, quality);`
|
|
166
235
|
|
|
167
236
|
| Parameters | Required | Type | Description |
|
package/dist/api/api.js
CHANGED
|
@@ -177,6 +177,9 @@ const getPlaylistChannel = async (page) => {
|
|
|
177
177
|
lang: 'en',
|
|
178
178
|
timezone_offset: '6',
|
|
179
179
|
};
|
|
180
|
-
|
|
180
|
+
// `gateway_input` is a nested object; it must reach the gateway as a JSON
|
|
181
|
+
// string, otherwise it serialises to `[object Object]` and Deezer reports the
|
|
182
|
+
// inner `page` as missing (`MISSING_PARAMETER_PAGE`).
|
|
183
|
+
return await (0, request_1.requestGet)('app_page_get', { gateway_input: JSON.stringify(gateway_input) }, page);
|
|
181
184
|
};
|
|
182
185
|
exports.getPlaylistChannel = getPlaylistChannel;
|
|
@@ -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
|
@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./api"), exports);
|
|
18
18
|
__exportStar(require("./request"), exports);
|
|
19
|
+
__exportStar(require("./search"), exports);
|
|
20
|
+
__exportStar(require("./browse"), exports);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { advancedSearchFilters, publicApiSearchOptions, publicApiSearchResponse, searchResultAlbum, searchResultArtist, searchResultPlaylist, searchResultTrack, suggestResult } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Compose Deezer's advanced search operators into one query string.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* buildAdvancedQuery({artist: 'daft punk', durMin: 200, durMax: 400});
|
|
7
|
+
* // => 'artist:"daft punk" dur_min:200 dur_max:400'
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* Free-text `query` is emitted first and bare. Deezer treats the operators as
|
|
11
|
+
* ranking hints rather than hard filters (and honours `track:` only now and
|
|
12
|
+
* then) — this only builds the string their docs describe. Pair it with
|
|
13
|
+
* `searchPublicApi(query, {strict: true})` to tighten what Deezer allows.
|
|
14
|
+
*
|
|
15
|
+
* The operators are reliable only on the **track** index (`searchPublicApi` /
|
|
16
|
+
* `searchTracks`). `/search/album` and `/search/artist` ignore or mishandle
|
|
17
|
+
* `artist:` / `album:` — use a plain string there.
|
|
18
|
+
*/
|
|
19
|
+
export declare const buildAdvancedQuery: (filters: advancedSearchFilters) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Search the **public** Deezer REST API (`api.deezer.com/search`).
|
|
22
|
+
*
|
|
23
|
+
* Unlike `searchMusic` (which drives the internal `deezer.pageSearch` gateway),
|
|
24
|
+
* this returns clean public-API objects — with `isrc`, `preview`, `rank`, real
|
|
25
|
+
* numeric ids — and accepts the advanced query operators (see
|
|
26
|
+
* `buildAdvancedQuery`), an `order`, and `limit` / `index` paging. No auth
|
|
27
|
+
* required; results are memoised by the api layer.
|
|
28
|
+
*
|
|
29
|
+
* @param query a plain string, or the output of `buildAdvancedQuery`
|
|
30
|
+
* @param options `type` (`'track'` default), `order`, `strict`, `limit`, `index`
|
|
31
|
+
*/
|
|
32
|
+
export declare const searchPublicApi: <T = searchResultTrack>(query: string, options?: publicApiSearchOptions) => Promise<publicApiSearchResponse<T>>;
|
|
33
|
+
/** `searchPublicApi` fixed to tracks. */
|
|
34
|
+
export declare const searchTracks: (query: string, options?: Omit<publicApiSearchOptions, 'type'>) => Promise<publicApiSearchResponse<searchResultTrack>>;
|
|
35
|
+
/** `searchPublicApi` fixed to albums. */
|
|
36
|
+
export declare const searchAlbums: (query: string, options?: Omit<publicApiSearchOptions, 'type'>) => Promise<publicApiSearchResponse<searchResultAlbum>>;
|
|
37
|
+
/** `searchPublicApi` fixed to artists. */
|
|
38
|
+
export declare const searchArtists: (query: string, options?: Omit<publicApiSearchOptions, 'type'>) => Promise<publicApiSearchResponse<searchResultArtist>>;
|
|
39
|
+
/** `searchPublicApi` fixed to playlists. */
|
|
40
|
+
export declare const searchPlaylists: (query: string, options?: Omit<publicApiSearchOptions, 'type'>) => Promise<publicApiSearchResponse<searchResultPlaylist>>;
|
|
41
|
+
/**
|
|
42
|
+
* `deezer.suggest` — the internal autocomplete endpoint. Cheaper and faster than
|
|
43
|
+
* a full `searchMusic`, and it powers "as you type" suggestion UIs. Needs an
|
|
44
|
+
* initialised session (`initDeezerApi`), like every gateway call.
|
|
45
|
+
*
|
|
46
|
+
* @param query partial query text
|
|
47
|
+
* @param nb max items per type (default 5)
|
|
48
|
+
*/
|
|
49
|
+
export declare const suggest: (query: string, nb?: number) => Promise<suggestResult>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.suggest = exports.searchPlaylists = exports.searchArtists = exports.searchAlbums = exports.searchTracks = exports.searchPublicApi = exports.buildAdvancedQuery = void 0;
|
|
4
|
+
const request_1 = require("./request");
|
|
5
|
+
/**
|
|
6
|
+
* Compose Deezer's advanced search operators into one query string.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* buildAdvancedQuery({artist: 'daft punk', durMin: 200, durMax: 400});
|
|
10
|
+
* // => 'artist:"daft punk" dur_min:200 dur_max:400'
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Free-text `query` is emitted first and bare. Deezer treats the operators as
|
|
14
|
+
* ranking hints rather than hard filters (and honours `track:` only now and
|
|
15
|
+
* then) — this only builds the string their docs describe. Pair it with
|
|
16
|
+
* `searchPublicApi(query, {strict: true})` to tighten what Deezer allows.
|
|
17
|
+
*
|
|
18
|
+
* The operators are reliable only on the **track** index (`searchPublicApi` /
|
|
19
|
+
* `searchTracks`). `/search/album` and `/search/artist` ignore or mishandle
|
|
20
|
+
* `artist:` / `album:` — use a plain string there.
|
|
21
|
+
*/
|
|
22
|
+
const buildAdvancedQuery = (filters) => {
|
|
23
|
+
const { query, artist, album, track, label, durMin, durMax, bpmMin, bpmMax } = filters;
|
|
24
|
+
const parts = [];
|
|
25
|
+
if (query && query.trim()) {
|
|
26
|
+
parts.push(query.trim());
|
|
27
|
+
}
|
|
28
|
+
const textOps = [
|
|
29
|
+
['artist', artist],
|
|
30
|
+
['album', album],
|
|
31
|
+
['track', track],
|
|
32
|
+
['label', label],
|
|
33
|
+
];
|
|
34
|
+
for (const [op, value] of textOps) {
|
|
35
|
+
if (value && value.trim()) {
|
|
36
|
+
parts.push(`${op}:"${value.trim().replace(/"/g, '')}"`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const rangeOps = [
|
|
40
|
+
['dur_min', durMin],
|
|
41
|
+
['dur_max', durMax],
|
|
42
|
+
['bpm_min', bpmMin],
|
|
43
|
+
['bpm_max', bpmMax],
|
|
44
|
+
];
|
|
45
|
+
for (const [op, value] of rangeOps) {
|
|
46
|
+
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
|
|
47
|
+
parts.push(`${op}:${Math.round(value)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return parts.join(' ');
|
|
51
|
+
};
|
|
52
|
+
exports.buildAdvancedQuery = buildAdvancedQuery;
|
|
53
|
+
const SEARCH_ENTITIES = ['track', 'album', 'artist', 'playlist', 'user', 'radio', 'podcast'];
|
|
54
|
+
/**
|
|
55
|
+
* Search the **public** Deezer REST API (`api.deezer.com/search`).
|
|
56
|
+
*
|
|
57
|
+
* Unlike `searchMusic` (which drives the internal `deezer.pageSearch` gateway),
|
|
58
|
+
* this returns clean public-API objects — with `isrc`, `preview`, `rank`, real
|
|
59
|
+
* numeric ids — and accepts the advanced query operators (see
|
|
60
|
+
* `buildAdvancedQuery`), an `order`, and `limit` / `index` paging. No auth
|
|
61
|
+
* required; results are memoised by the api layer.
|
|
62
|
+
*
|
|
63
|
+
* @param query a plain string, or the output of `buildAdvancedQuery`
|
|
64
|
+
* @param options `type` (`'track'` default), `order`, `strict`, `limit`, `index`
|
|
65
|
+
*/
|
|
66
|
+
const searchPublicApi = (query, options = {}) => {
|
|
67
|
+
const { type, order, strict, limit, index } = options;
|
|
68
|
+
const segment = type && type !== 'track' && SEARCH_ENTITIES.includes(type) ? `/${type}` : '';
|
|
69
|
+
const search = new URLSearchParams({ q: query });
|
|
70
|
+
if (strict) {
|
|
71
|
+
search.set('strict', 'on');
|
|
72
|
+
}
|
|
73
|
+
if (order) {
|
|
74
|
+
search.set('order', order);
|
|
75
|
+
}
|
|
76
|
+
if (typeof limit === 'number') {
|
|
77
|
+
search.set('limit', String(limit));
|
|
78
|
+
}
|
|
79
|
+
if (typeof index === 'number') {
|
|
80
|
+
search.set('index', String(index));
|
|
81
|
+
}
|
|
82
|
+
return (0, request_1.requestPublicApi)(`/search${segment}?${search.toString()}`);
|
|
83
|
+
};
|
|
84
|
+
exports.searchPublicApi = searchPublicApi;
|
|
85
|
+
/** `searchPublicApi` fixed to tracks. */
|
|
86
|
+
const searchTracks = (query, options = {}) => (0, exports.searchPublicApi)(query, { ...options, type: 'track' });
|
|
87
|
+
exports.searchTracks = searchTracks;
|
|
88
|
+
/** `searchPublicApi` fixed to albums. */
|
|
89
|
+
const searchAlbums = (query, options = {}) => (0, exports.searchPublicApi)(query, { ...options, type: 'album' });
|
|
90
|
+
exports.searchAlbums = searchAlbums;
|
|
91
|
+
/** `searchPublicApi` fixed to artists. */
|
|
92
|
+
const searchArtists = (query, options = {}) => (0, exports.searchPublicApi)(query, { ...options, type: 'artist' });
|
|
93
|
+
exports.searchArtists = searchArtists;
|
|
94
|
+
/** `searchPublicApi` fixed to playlists. */
|
|
95
|
+
const searchPlaylists = (query, options = {}) => (0, exports.searchPublicApi)(query, { ...options, type: 'playlist' });
|
|
96
|
+
exports.searchPlaylists = searchPlaylists;
|
|
97
|
+
/**
|
|
98
|
+
* `deezer.suggest` — the internal autocomplete endpoint. Cheaper and faster than
|
|
99
|
+
* a full `searchMusic`, and it powers "as you type" suggestion UIs. Needs an
|
|
100
|
+
* initialised session (`initDeezerApi`), like every gateway call.
|
|
101
|
+
*
|
|
102
|
+
* @param query partial query text
|
|
103
|
+
* @param nb max items per type (default 5)
|
|
104
|
+
*/
|
|
105
|
+
const suggest = (query, nb = 5) => (0, request_1.requestLight)({
|
|
106
|
+
QUERY: query,
|
|
107
|
+
NB: nb,
|
|
108
|
+
TYPES: { ALBUM: true, ARTIST: true, TRACK: true, PLAYLIST: true, RADIO: true, SHOW: true },
|
|
109
|
+
}, 'deezer.suggest');
|
|
110
|
+
exports.suggest = suggest;
|
|
@@ -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/search.d.ts
CHANGED
|
@@ -47,6 +47,163 @@ export interface discographyType {
|
|
|
47
47
|
start: number;
|
|
48
48
|
nb: number;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Deezer's advanced search operators. Compose these into one query string with
|
|
52
|
+
* `buildAdvancedQuery()` — e.g. `{artist: 'daft punk', durMin: 200}` becomes
|
|
53
|
+
* `artist:"daft punk" dur_min:200`.
|
|
54
|
+
*
|
|
55
|
+
* Deezer applies these as ranking hints, not hard filters, and honours `track:`
|
|
56
|
+
* only intermittently on their side — this type just mirrors the syntax they
|
|
57
|
+
* document.
|
|
58
|
+
*/
|
|
59
|
+
export interface advancedSearchFilters {
|
|
60
|
+
/** free-text terms, emitted first and bare */
|
|
61
|
+
query?: string;
|
|
62
|
+
artist?: string;
|
|
63
|
+
album?: string;
|
|
64
|
+
track?: string;
|
|
65
|
+
label?: string;
|
|
66
|
+
/** minimum duration, seconds */
|
|
67
|
+
durMin?: number;
|
|
68
|
+
/** maximum duration, seconds */
|
|
69
|
+
durMax?: number;
|
|
70
|
+
/** minimum beats-per-minute */
|
|
71
|
+
bpmMin?: number;
|
|
72
|
+
/** maximum beats-per-minute */
|
|
73
|
+
bpmMax?: number;
|
|
74
|
+
}
|
|
75
|
+
export type searchOrder = 'RANKING' | 'TRACK_ASC' | 'TRACK_DESC' | 'ARTIST_ASC' | 'ARTIST_DESC' | 'ALBUM_ASC' | 'ALBUM_DESC' | 'RATING_ASC' | 'RATING_DESC' | 'DURATION_ASC' | 'DURATION_DESC';
|
|
76
|
+
export type searchEntity = 'track' | 'album' | 'artist' | 'playlist' | 'user' | 'radio' | 'podcast';
|
|
77
|
+
export interface publicApiSearchOptions {
|
|
78
|
+
/** which index to hit — `track` (default), `album`, `artist`, `playlist`, `user`, `radio`, `podcast` */
|
|
79
|
+
type?: searchEntity;
|
|
80
|
+
order?: searchOrder;
|
|
81
|
+
/** send Deezer's `strict=on` — disables the fuzzy fallback */
|
|
82
|
+
strict?: boolean;
|
|
83
|
+
/** page size; Deezer caps this near 100 */
|
|
84
|
+
limit?: number;
|
|
85
|
+
/** offset into the result set */
|
|
86
|
+
index?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface publicApiSearchResponse<T> {
|
|
89
|
+
data: T[];
|
|
90
|
+
total: number;
|
|
91
|
+
/** absolute URL of the next page, when there is one */
|
|
92
|
+
next?: string;
|
|
93
|
+
prev?: string;
|
|
94
|
+
}
|
|
95
|
+
interface searchArtistRef {
|
|
96
|
+
id: number;
|
|
97
|
+
name: string;
|
|
98
|
+
link?: string;
|
|
99
|
+
picture?: string;
|
|
100
|
+
picture_small?: string;
|
|
101
|
+
picture_medium?: string;
|
|
102
|
+
picture_big?: string;
|
|
103
|
+
picture_xl?: string;
|
|
104
|
+
tracklist?: string;
|
|
105
|
+
type: 'artist';
|
|
106
|
+
}
|
|
107
|
+
interface searchAlbumRef {
|
|
108
|
+
id: number;
|
|
109
|
+
title: string;
|
|
110
|
+
cover?: string;
|
|
111
|
+
cover_small?: string;
|
|
112
|
+
cover_medium?: string;
|
|
113
|
+
cover_big?: string;
|
|
114
|
+
cover_xl?: string;
|
|
115
|
+
md5_image?: string;
|
|
116
|
+
tracklist?: string;
|
|
117
|
+
type: 'album';
|
|
118
|
+
}
|
|
119
|
+
export interface searchResultTrack {
|
|
120
|
+
id: number;
|
|
121
|
+
readable: boolean;
|
|
122
|
+
title: string;
|
|
123
|
+
title_short: string;
|
|
124
|
+
title_version?: string;
|
|
125
|
+
link: string;
|
|
126
|
+
duration: number;
|
|
127
|
+
rank: number;
|
|
128
|
+
explicit_lyrics: boolean;
|
|
129
|
+
explicit_content_lyrics: number;
|
|
130
|
+
explicit_content_cover: number;
|
|
131
|
+
preview: string;
|
|
132
|
+
md5_image: string;
|
|
133
|
+
isrc?: string;
|
|
134
|
+
artist: searchArtistRef;
|
|
135
|
+
album: searchAlbumRef;
|
|
136
|
+
type: 'track';
|
|
137
|
+
}
|
|
138
|
+
export interface searchResultAlbum {
|
|
139
|
+
id: number;
|
|
140
|
+
title: string;
|
|
141
|
+
link: string;
|
|
142
|
+
cover: string;
|
|
143
|
+
cover_small?: string;
|
|
144
|
+
cover_medium?: string;
|
|
145
|
+
cover_big?: string;
|
|
146
|
+
cover_xl?: string;
|
|
147
|
+
md5_image: string;
|
|
148
|
+
genre_id: number;
|
|
149
|
+
nb_tracks: number;
|
|
150
|
+
record_type: string;
|
|
151
|
+
explicit_lyrics: boolean;
|
|
152
|
+
artist: searchArtistRef;
|
|
153
|
+
type: 'album';
|
|
154
|
+
}
|
|
155
|
+
export interface searchResultArtist {
|
|
156
|
+
id: number;
|
|
157
|
+
name: string;
|
|
158
|
+
link: string;
|
|
159
|
+
picture: string;
|
|
160
|
+
picture_small?: string;
|
|
161
|
+
picture_medium?: string;
|
|
162
|
+
picture_big?: string;
|
|
163
|
+
picture_xl?: string;
|
|
164
|
+
nb_album: number;
|
|
165
|
+
nb_fan: number;
|
|
166
|
+
radio: boolean;
|
|
167
|
+
tracklist: string;
|
|
168
|
+
type: 'artist';
|
|
169
|
+
}
|
|
170
|
+
export interface searchResultPlaylist {
|
|
171
|
+
id: number;
|
|
172
|
+
title: string;
|
|
173
|
+
public: boolean;
|
|
174
|
+
nb_tracks: number;
|
|
175
|
+
link: string;
|
|
176
|
+
picture: string;
|
|
177
|
+
picture_small?: string;
|
|
178
|
+
picture_medium?: string;
|
|
179
|
+
picture_big?: string;
|
|
180
|
+
picture_xl?: string;
|
|
181
|
+
checksum?: string;
|
|
182
|
+
tracklist: string;
|
|
183
|
+
creation_date?: string;
|
|
184
|
+
user?: {
|
|
185
|
+
id: number;
|
|
186
|
+
name: string;
|
|
187
|
+
tracklist?: string;
|
|
188
|
+
type: 'user';
|
|
189
|
+
};
|
|
190
|
+
type: 'playlist';
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* `deezer.suggest` — lightweight autocomplete off the internal gateway. Each
|
|
194
|
+
* per-type array is gw-shaped (uppercase keys), same as `searchMusic`'s.
|
|
195
|
+
*/
|
|
196
|
+
export interface suggestResult {
|
|
197
|
+
QUERY: string;
|
|
198
|
+
TOP_RESULT: unknown[];
|
|
199
|
+
ORDER: string[];
|
|
200
|
+
ALBUM?: albumTypeMinimal[];
|
|
201
|
+
ARTIST?: artistInfoTypeMinimal[];
|
|
202
|
+
TRACK?: trackType[];
|
|
203
|
+
PLAYLIST?: playlistInfoMinimal[];
|
|
204
|
+
SHOW?: unknown[];
|
|
205
|
+
RADIO?: radioType[];
|
|
206
|
+
}
|
|
50
207
|
export interface searchType {
|
|
51
208
|
QUERY: string;
|
|
52
209
|
FUZZINNESS: boolean;
|