gerdur 2.2.0 → 2.4.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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.4.0 - 2026-08-31
4
+
5
+ ### Added
6
+
7
+ - **`gerdur-core@^2.3.0`** — re-exports `getTrackPreview`, `downloadPreview`,
8
+ `formatName`, `toFormat`, `DEEZER_FORMATS` and the `DeezerFormat` /
9
+ `TrackPreview` types.
10
+ - **`--preview`** — download the 30-second clip for each track as
11
+ `<name>.preview.mp3` instead of the full file. Plain licence-free MP3s: no
12
+ `--quality`, no decryption, no tagging. Works with every source and headless.
13
+ - **`Session.trackPreview(track)`** and **`Session.downloadPreview(track)`**.
14
+
15
+ ## 2.3.0 - 2026-08-31
16
+
17
+ ### Added
18
+
19
+ - **`gerdur-core@^2.2.0`** — the browse / discovery surface is re-exported:
20
+ `getGenres`, `getChart`, `getChartTracks`, `getGenreArtists`,
21
+ `getEditorialList` / `getEditorialReleases` / `getEditorialSelection` /
22
+ `getEditorialCharts`, `getArtistTopTracks`, `getRelatedArtists`,
23
+ `getArtistAlbums`, `getArtistPlaylists`, `getArtistRadioTracks`,
24
+ `getTrackByISRC`, `getAlbumByUPC`, plus their types.
25
+ - **`Session` browse methods**: `genres`, `chart`, `chartTracks`,
26
+ `editorialSections`, `artistTopTracks`, `relatedArtists`, `artistAlbums`,
27
+ `artistRadio`, `trackByISRC`, `albumByUPC`.
28
+ - **CLI `isrc:` / `upc:` prefixes** — `gerdur -u isrc:GBDUW0000059` downloads the
29
+ exact track for an ISRC; `gerdur -u upc:0724384960650` downloads the album for
30
+ a barcode. Both work in `--headless` mode and from the interactive prompt.
31
+
3
32
  ## 2.2.0 - 2026-08-31
4
33
 
5
34
  ### Added
package/README.md CHANGED
@@ -142,12 +142,13 @@ All options are optional. You can suppress prompts via providing `quality` and `
142
142
  | `--bpm-min` / `--bpm-max` | *None* | number | Search filter — tempo range (beats per minute) |
143
143
  | `--dur-min` / `--dur-max` | *None* | number (seconds) | Search filter — track duration range |
144
144
  | `--search-limit` | *None* | number (default 50) | How many search results to fetch |
145
+ | `--preview` | *None* | *Nothing* | Download the 30-second preview clips (`.preview.mp3`) instead of full tracks — no `--quality` needed |
145
146
 
146
147
  ## Search
147
148
 
148
149
  ### From the interactive prompt
149
150
 
150
- When `gerdur` asks for *"a URL, a search term, or `search:<advanced query>`"*:
151
+ When `gerdur` asks for a URL, a search term, or a prefixed query:
151
152
 
152
153
  | Input | Does |
153
154
  | :--- | :--- |
@@ -156,6 +157,10 @@ When `gerdur` asks for *"a URL, a search term, or `search:<advanced query>`"*:
156
157
  | `album:discovery` | album search — pick an album |
157
158
  | `playlist:deep focus` | playlist search — pick a playlist |
158
159
  | `search:artist:"daft punk" bpm_min:120` | advanced track search (see operators below) |
160
+ | `isrc:GBDUW0000059` | download the exact track for an ISRC |
161
+ | `upc:0724384960650` | download the album for a UPC / EAN barcode |
162
+
163
+ `isrc:` and `upc:` also work in `--headless` mode (`-u isrc:…` / `-u upc:…`).
159
164
 
160
165
  ### From flags (works headless)
161
166
 
@@ -194,6 +199,18 @@ only on **track** search:
194
199
  | `dur_min:` / `dur_max:` | `dur_min:200` (seconds) |
195
200
  | `bpm_min:` / `bpm_max:` | `bpm_min:120` |
196
201
 
202
+ ## Previews
203
+
204
+ `--preview` writes the 30-second clip for each track as `<name>.preview.mp3`
205
+ instead of downloading the full file. The clips are plain, licence-free MP3s —
206
+ no `--quality`, no decryption, no tagging. Works with every source (URL, search,
207
+ `isrc:` / `upc:`) and in `--headless` mode.
208
+
209
+ ```bash
210
+ gerdur --preview -u https://deezer.com/album/302127 # 14 clips
211
+ gerdur -d --preview --artist "Justice" --search-limit 10 # audition a search
212
+ ```
213
+
197
214
  ## Programmatic API
198
215
 
199
216
  When installed as a dependency, `gerdur` exposes a side-effect-free API
@@ -235,10 +252,18 @@ const hits = await session.searchAdvanced(
235
252
  const suggestions = await session.suggest('daft'); // autocomplete
236
253
  ```
237
254
 
238
- Session methods: `parseUrl`, `search`, `searchAdvanced`, `suggest`, `getUser`,
239
- `getTrackBuffer`, `downloadTrack`, `downloadTracks`, `downloadUrl`. Every download
240
- call is silent; `downloadTracks` / `downloadUrl` accept `concurrency` and an
241
- `onProgress` callback and return one `{path, written} | null` per track.
255
+ Session methods:
256
+
257
+ - **resolve / search** `parseUrl`, `search`, `searchAdvanced`, `suggest`
258
+ - **browse** `genres`, `chart`, `chartTracks`, `editorialSections`,
259
+ `artistTopTracks`, `relatedArtists`, `artistAlbums`, `artistRadio`,
260
+ `trackByISRC`, `albumByUPC`
261
+ - **user / download** — `getUser`, `getTrackBuffer`, `downloadTrack`,
262
+ `downloadTracks`, `downloadUrl`, `trackPreview`, `downloadPreview`
263
+
264
+ Every download call is silent; `downloadTracks` / `downloadUrl` accept
265
+ `concurrency` and an `onProgress` callback and return one `{path, written} | null`
266
+ per track.
242
267
 
243
268
  `searchAdvanced(filters, options?)` builds a Deezer advanced-search query from
244
269
  `{query?, artist?, album?, track?, label?, durMin?, durMax?, bpmMin?, bpmMax?}`
@@ -247,6 +272,17 @@ unreliable, so an empty result is retried as a plain free-text query unless
247
272
  `fallback: false`. It returns public-API track objects — fetch a hit's gw track
248
273
  with `getTrackInfo(id)` before downloading it.
249
274
 
275
+ ```ts
276
+ // Browse: this week's electro chart, download the top 5
277
+ const {tracks} = await session.chart(106, 5); // 106 = "Dance" genre
278
+ const full = await Promise.all(tracks.data.map((t) => getTrackInfo(String(t.id))));
279
+ await session.downloadTracks(full, 'flac');
280
+
281
+ // Find & grab an exact recording by barcode
282
+ const t = await session.trackByISRC('GBDUW0000059');
283
+ await session.downloadTrack(await getTrackInfo(String(t.id)), '320');
284
+ ```
285
+
250
286
  ### Low-level: primitives
251
287
 
252
288
  ```ts
@@ -269,11 +305,18 @@ const {path, written} = (await downloadTrackToFile(track, 'flac', {output: '{ART
269
305
  So you don't need `gerdur-core` as a second dependency (call after `initDeezerApi`
270
306
  or `createSession`):
271
307
 
272
- `parseInfo`, `searchMusic`, `searchPublicApi`, `searchTracks`, `searchAlbums`,
273
- `searchArtists`, `searchPlaylists`, `buildAdvancedQuery`, `suggest`, `getUser`,
274
- `getTrackInfo`, `getAlbumInfo`, `getAlbumTracks`, `getPlaylistInfo`,
275
- `getPlaylistTracks`, `getArtistInfo`, `getDiscography`, `getLyrics`,
276
- `getTrackDownloadUrl`, `resolveDownloadUrls`, `GeoBlocked`.
308
+ - **Query** — `parseInfo`, `getUser`, `getTrackInfo`, `getAlbumInfo`,
309
+ `getAlbumTracks`, `getPlaylistInfo`, `getPlaylistTracks`, `getArtistInfo`,
310
+ `getDiscography`, `getLyrics`
311
+ - **Search** — `searchMusic`, `searchPublicApi`, `searchTracks`, `searchAlbums`,
312
+ `searchArtists`, `searchPlaylists`, `buildAdvancedQuery`, `suggest`
313
+ - **Browse** — `getGenres`, `getChart`, `getChartTracks`, `getGenreArtists`,
314
+ `getEditorialList`, `getEditorialReleases`, `getEditorialSelection`,
315
+ `getEditorialCharts`, `getArtistTopTracks`, `getRelatedArtists`,
316
+ `getArtistAlbums`, `getArtistPlaylists`, `getArtistRadioTracks`,
317
+ `getTrackByISRC`, `getAlbumByUPC`
318
+ - **Download** — `getTrackDownloadUrl`, `resolveDownloadUrls`, `getTrackPreview`,
319
+ `downloadPreview`, `formatName`, `toFormat`, `DEEZER_FORMATS`, `GeoBlocked`
277
320
 
278
321
  ### Auth & config helpers
279
322
 
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gerdur",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "The crucial streaming module for dabox systems.",
5
5
  "author": "Christian",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -48,7 +48,7 @@
48
48
  "adm-zip": "^0.5.16",
49
49
  "chalk": "^4.1.2",
50
50
  "commander": "^9.5.0",
51
- "gerdur-core": "^2.1.0",
51
+ "gerdur-core": "^2.3.0",
52
52
  "dot-prop": "^6.0.1",
53
53
  "got": "^11.8.6",
54
54
  "gradient-string": "^2.0.2",
@@ -57,7 +57,8 @@ const cmd = new commander_1.Command()
57
57
  .option('--bpm-max <n>', 'Search filter: maximum BPM')
58
58
  .option('--dur-min <seconds>', 'Search filter: minimum duration')
59
59
  .option('--dur-max <seconds>', 'Search filter: maximum duration')
60
- .option('--search-limit <n>', 'Max search results to fetch', '50');
60
+ .option('--search-limit <n>', 'Max search results to fetch', '50')
61
+ .option('--preview', 'Download the 30-second preview clips instead of full tracks', false);
61
62
  if (process.pkg) {
62
63
  cmd.option('-U, --update', 'Update this program to latest version');
63
64
  }
@@ -79,7 +80,7 @@ const toFiniteNumber = (value) => {
79
80
  };
80
81
  const advancedFilters = (0, search_1.advancedFiltersFromFlags)(options);
81
82
  let advancedFiltersConsumed = false;
82
- if (options.headless && !options.quality) {
83
+ if (options.headless && !options.quality && !options.preview) {
83
84
  console.error(signale_1.default.error('Missing parameters --quality'));
84
85
  console.error(signale_1.default.note('Quality must be provided with headless mode'));
85
86
  process.exit(1);
@@ -152,10 +153,48 @@ const resolveAdvancedSearch = async (filters) => {
152
153
  }
153
154
  return { info: { type: 'track', id: query }, linktype: 'track', linkinfo: {}, tracks };
154
155
  };
156
+ /**
157
+ * `--preview`: fetch the 30-second clip for each selected track and write it as
158
+ * `<saveLayout path>.preview.mp3`. Bypasses the get_url / decrypt / tag pipeline
159
+ * entirely — previews are plain, licence-free MP3s.
160
+ */
161
+ const downloadPreviews = async (tracks, info, template, totalTracks, trackNumber, overwrite) => {
162
+ const saved = [];
163
+ await queue.addAll(tracks.map((track, index) => async () => {
164
+ const rel = (0, util_1.saveLayout)({
165
+ track,
166
+ album: info,
167
+ path: template,
168
+ trackNumber,
169
+ minimumIntegerDigits: totalTracks >= 100 ? 3 : 2,
170
+ }) + '.preview.mp3';
171
+ (0, log_update_1.default)(signale_1.default.pending(`(${index + 1}/${tracks.length}) ${track.ART_NAME} - ${track.SNG_TITLE}`));
172
+ if (!overwrite && (0, fs_1.existsSync)(rel)) {
173
+ saved.push(rel);
174
+ return;
175
+ }
176
+ try {
177
+ const clip = await (0, gerdur_core_1.downloadPreview)(track);
178
+ if (!clip) {
179
+ (0, log_update_1.default)(signale_1.default.warn(`No preview for ${track.SNG_TITLE}`));
180
+ return;
181
+ }
182
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(rel), { recursive: true });
183
+ (0, fs_1.writeFileSync)(rel, clip);
184
+ saved.push(rel);
185
+ (0, log_update_1.default)(signale_1.default.success(`${track.ART_NAME} - ${track.SNG_TITLE} (preview)`));
186
+ }
187
+ catch (err) {
188
+ (0, log_update_1.default)(signale_1.default.error(`${track.SNG_TITLE}: ${err.message}`));
189
+ }
190
+ }));
191
+ log_update_1.default.done();
192
+ return saved;
193
+ };
155
194
  const startDownload = async (saveLayout, url, skipPrompt) => {
156
195
  try {
157
196
  url = url ?? '';
158
- if (!options.quality) {
197
+ if (!options.quality && !options.preview) {
159
198
  const { musicQuality } = await (0, prompts_1.default)([
160
199
  {
161
200
  type: 'select',
@@ -189,7 +228,7 @@ const startDownload = async (saveLayout, url, skipPrompt) => {
189
228
  {
190
229
  type: 'text',
191
230
  name: 'query',
192
- message: 'Enter a URL, a search term, or `search:<advanced query>`:',
231
+ message: 'Enter a URL, a search term, or a `search:` / `isrc:` / `upc:` query:',
193
232
  validate: (value) => (value ? true : false),
194
233
  },
195
234
  ], { onCancel });
@@ -202,6 +241,25 @@ const startDownload = async (saveLayout, url, skipPrompt) => {
202
241
  throw new Error('No tracks matched that search.');
203
242
  }
204
243
  }
244
+ // `isrc:` / `upc:` — resolve a barcode to a Deezer track / album (works headless).
245
+ if (!searchData && url && url.startsWith('isrc:')) {
246
+ const code = url.slice('isrc:'.length).trim();
247
+ const found = await (0, gerdur_core_1.getTrackByISRC)(code).catch(() => null);
248
+ if (!found) {
249
+ throw new Error(`No Deezer track for ISRC ${code}`);
250
+ }
251
+ const track = await (0, gerdur_core_1.getTrackInfo)(String(found.id));
252
+ searchData = { info: { type: 'track', id: code }, linktype: 'track', linkinfo: {}, tracks: [stampVersion(track)] };
253
+ }
254
+ if (!searchData && url && url.startsWith('upc:')) {
255
+ const code = url.slice('upc:'.length).trim();
256
+ const found = await (0, gerdur_core_1.getAlbumByUPC)(code).catch(() => null);
257
+ if (!found) {
258
+ throw new Error(`No Deezer album for UPC ${code}`);
259
+ }
260
+ console.log(signale_1.default.info(`UPC ${code} → ${found.title} (${found.nb_tracks} tracks)`));
261
+ url = `https://www.deezer.com/album/${found.id}`;
262
+ }
205
263
  if (!searchData && !url.match(urlRegex)) {
206
264
  if (options.headless) {
207
265
  throw new Error('Please provide a valid URL. Unknown URL: ' + url);
@@ -308,56 +366,63 @@ const startDownload = async (saveLayout, url, skipPrompt) => {
308
366
  const resolveFullPath = options.resolveFullPath ?? conf.get('playlist.resolveFullPath');
309
367
  const savedFiles = [];
310
368
  let m3u8 = [];
311
- // One batched get_url for the whole selection instead of a request per track.
312
- // Deezer returns the best licensed format, so pass an ordered preference list.
313
- const QUALITY_PREF = {
314
- '1': [1],
315
- '128': [1],
316
- '3': [3, 1],
317
- '320': [3, 1],
318
- '9': [9, 3, 1],
319
- flac: [9, 3, 1],
320
- };
321
- const prefetchedUrls = new Map();
322
- if (data.tracks.length > 1 && !process.env.SIMULATE) {
323
- try {
324
- const base = QUALITY_PREF[String(options.quality).toLowerCase()] ?? [3, 1];
325
- const pref = fallbackQuality ? base : [base[0]];
326
- const resolved = await (0, gerdur_core_1.resolveDownloadUrls)(data.tracks, pref);
327
- resolved.forEach((r, i) => {
328
- if (r) {
329
- prefetchedUrls.set(data.tracks[i].SNG_ID, r);
330
- }
331
- });
332
- }
333
- catch {
334
- // batch resolve failed wholesale — every track falls back to per-track resolution
335
- }
369
+ const layoutTemplate = options.output ? options.output : saveLayout[data.linktype];
370
+ if (options.preview) {
371
+ const clips = await downloadPreviews(data.tracks, data.linkinfo, layoutTemplate, data.tracks.length, trackNumber, overwrite);
372
+ savedFiles.push(...clips);
336
373
  }
337
- await queue.addAll(data.tracks.map((track, index) => {
338
- return async () => {
339
- const savedPath = await (0, download_track_1.default)({
340
- track,
341
- quality: options.quality,
342
- info: data.linkinfo,
343
- coverSizes,
344
- path: options.output ? options.output : saveLayout[data.linktype],
345
- totalTracks: data ? data.tracks.length : 10,
346
- trackNumber,
347
- fallbackTrack,
348
- fallbackQuality,
349
- overwrite,
350
- message: `(${index}/${data.tracks.length})`,
351
- lrc,
352
- prefetched: prefetchedUrls.get(track.SNG_ID) ?? null,
353
- });
354
- // Add to saved list
355
- if (savedPath) {
356
- m3u8.push((0, path_1.resolve)(process.env.SIMULATE ? savedPath : (0, true_case_path_1.trueCasePathSync)(savedPath)));
357
- savedFiles.push(savedPath);
358
- }
374
+ else {
375
+ // One batched get_url for the whole selection instead of a request per track.
376
+ // Deezer returns the best licensed format, so pass an ordered preference list.
377
+ const QUALITY_PREF = {
378
+ '1': [1],
379
+ '128': [1],
380
+ '3': [3, 1],
381
+ '320': [3, 1],
382
+ '9': [9, 3, 1],
383
+ flac: [9, 3, 1],
359
384
  };
360
- }));
385
+ const prefetchedUrls = new Map();
386
+ if (data.tracks.length > 1 && !process.env.SIMULATE) {
387
+ try {
388
+ const base = QUALITY_PREF[String(options.quality).toLowerCase()] ?? [3, 1];
389
+ const pref = fallbackQuality ? base : [base[0]];
390
+ const resolved = await (0, gerdur_core_1.resolveDownloadUrls)(data.tracks, pref);
391
+ resolved.forEach((r, i) => {
392
+ if (r) {
393
+ prefetchedUrls.set(data.tracks[i].SNG_ID, r);
394
+ }
395
+ });
396
+ }
397
+ catch {
398
+ // batch resolve failed wholesale — every track falls back to per-track resolution
399
+ }
400
+ }
401
+ await queue.addAll(data.tracks.map((track, index) => {
402
+ return async () => {
403
+ const savedPath = await (0, download_track_1.default)({
404
+ track,
405
+ quality: options.quality,
406
+ info: data.linkinfo,
407
+ coverSizes,
408
+ path: layoutTemplate,
409
+ totalTracks: data ? data.tracks.length : 10,
410
+ trackNumber,
411
+ fallbackTrack,
412
+ fallbackQuality,
413
+ overwrite,
414
+ message: `(${index}/${data.tracks.length})`,
415
+ lrc,
416
+ prefetched: prefetchedUrls.get(track.SNG_ID) ?? null,
417
+ });
418
+ // Add to saved list
419
+ if (savedPath) {
420
+ m3u8.push((0, path_1.resolve)(process.env.SIMULATE ? savedPath : (0, true_case_path_1.trueCasePathSync)(savedPath)));
421
+ savedFiles.push(savedPath);
422
+ }
423
+ };
424
+ }));
425
+ }
361
426
  // Display downloaded location
362
427
  if (savedFiles.length > 0) {
363
428
  const savedIn = new Set(savedFiles.map((l) => (0, path_1.dirname)(l)));
@@ -59,6 +59,7 @@ export type { SessionOptions, DownloadTracksOptions, SearchType } from './lib/se
59
59
  export { getTrackBuffer, getTaggedTrack, downloadTrackToFile } from './lib/api-download';
60
60
  export type { Quality, GetTrackBufferOptions, DownloadTrackOptions, DownloadResult } from './lib/api-download';
61
61
  export { default as Config, globalConfigPath, resolveConfigFile } from './lib/config';
62
- export { initDeezerApi, parseInfo, searchMusic, searchPublicApi, searchTracks, searchAlbums, searchArtists, searchPlaylists, buildAdvancedQuery, suggest, getUser, getTrackInfo, getAlbumInfo, getAlbumTracks, getPlaylistInfo, getPlaylistTracks, getArtistInfo, getDiscography, getLyrics, getTrackDownloadUrl, resolveDownloadUrls, addTrackTags, getRichAlbum, normalizeContributors, toLrc, GeoBlocked, } from 'gerdur-core';
62
+ export { initDeezerApi, parseInfo, searchMusic, searchPublicApi, searchTracks, searchAlbums, searchArtists, searchPlaylists, buildAdvancedQuery, suggest, getGenres, getChart, getChartTracks, getGenreArtists, getEditorialList, getEditorialReleases, getEditorialSelection, getEditorialCharts, getArtistTopTracks, getRelatedArtists, getArtistAlbums, getArtistPlaylists, getArtistRadioTracks, getTrackByISRC, getAlbumByUPC, getTrackPreview, downloadPreview, formatName, toFormat, DEEZER_FORMATS, getUser, getTrackInfo, getAlbumInfo, getAlbumTracks, getPlaylistInfo, getPlaylistTracks, getArtistInfo, getDiscography, getLyrics, getTrackDownloadUrl, resolveDownloadUrls, addTrackTags, getRichAlbum, normalizeContributors, toLrc, GeoBlocked, } from 'gerdur-core';
63
63
  export type { AddTrackTagsOptions, TaggedTrack, TrackTagModel, RichAlbum, ResolvedUrl, NormalizedContributors, } from 'gerdur-core';
64
- export type { advancedSearchFilters, searchOrder, searchEntity, publicApiSearchOptions, publicApiSearchResponse, searchResultTrack, searchResultAlbum, searchResultArtist, searchResultPlaylist, suggestResult, } from 'gerdur-core/types';
64
+ export type { advancedSearchFilters, searchOrder, searchEntity, publicApiSearchOptions, publicApiSearchResponse, searchResultTrack, searchResultAlbum, searchResultArtist, searchResultPlaylist, suggestResult, publicApiList, chartType, chartTrack, chartAlbum, chartArtist, chartPlaylist, chartPodcast, genreType, editorialType, artistAlbumResult, } from 'gerdur-core/types';
65
+ export type { Quality as MediaFormat, DeezerFormat, TrackPreview } from 'gerdur-core';
package/dist/src/index.js CHANGED
@@ -33,7 +33,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
33
  return (mod && mod.__esModule) ? mod : { "default": mod };
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.GeoBlocked = exports.toLrc = exports.normalizeContributors = exports.getRichAlbum = exports.addTrackTags = exports.resolveDownloadUrls = exports.getTrackDownloadUrl = exports.getLyrics = exports.getDiscography = exports.getArtistInfo = exports.getPlaylistTracks = exports.getPlaylistInfo = exports.getAlbumTracks = exports.getAlbumInfo = exports.getTrackInfo = exports.getUser = exports.suggest = exports.buildAdvancedQuery = exports.searchPlaylists = exports.searchArtists = exports.searchAlbums = exports.searchTracks = exports.searchPublicApi = exports.searchMusic = exports.parseInfo = exports.initDeezerApi = exports.resolveConfigFile = exports.globalConfigPath = exports.Config = exports.downloadTrackToFile = exports.getTaggedTrack = exports.getTrackBuffer = exports.Session = exports.createSession = exports.getArl = exports.LoginError = exports.loginWithEmail = void 0;
36
+ exports.getLyrics = exports.getDiscography = exports.getArtistInfo = exports.getPlaylistTracks = exports.getPlaylistInfo = exports.getAlbumTracks = exports.getAlbumInfo = exports.getTrackInfo = exports.getUser = exports.DEEZER_FORMATS = exports.toFormat = exports.formatName = exports.downloadPreview = exports.getTrackPreview = 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.getChartTracks = exports.getChart = exports.getGenres = exports.suggest = exports.buildAdvancedQuery = exports.searchPlaylists = exports.searchArtists = exports.searchAlbums = exports.searchTracks = exports.searchPublicApi = exports.searchMusic = exports.parseInfo = exports.initDeezerApi = exports.resolveConfigFile = exports.globalConfigPath = exports.Config = exports.downloadTrackToFile = exports.getTaggedTrack = exports.getTrackBuffer = exports.Session = exports.createSession = exports.getArl = exports.LoginError = exports.loginWithEmail = void 0;
37
+ exports.GeoBlocked = exports.toLrc = exports.normalizeContributors = exports.getRichAlbum = exports.addTrackTags = exports.resolveDownloadUrls = exports.getTrackDownloadUrl = void 0;
37
38
  // ─── Authentication ────────────────────────────────────────────────────────
38
39
  const email_login_1 = require("./lib/email-login");
39
40
  Object.defineProperty(exports, "loginWithEmail", { enumerable: true, get: function () { return email_login_1.loginWithEmail; } });
@@ -96,6 +97,26 @@ Object.defineProperty(exports, "searchArtists", { enumerable: true, get: functio
96
97
  Object.defineProperty(exports, "searchPlaylists", { enumerable: true, get: function () { return gerdur_core_1.searchPlaylists; } });
97
98
  Object.defineProperty(exports, "buildAdvancedQuery", { enumerable: true, get: function () { return gerdur_core_1.buildAdvancedQuery; } });
98
99
  Object.defineProperty(exports, "suggest", { enumerable: true, get: function () { return gerdur_core_1.suggest; } });
100
+ Object.defineProperty(exports, "getGenres", { enumerable: true, get: function () { return gerdur_core_1.getGenres; } });
101
+ Object.defineProperty(exports, "getChart", { enumerable: true, get: function () { return gerdur_core_1.getChart; } });
102
+ Object.defineProperty(exports, "getChartTracks", { enumerable: true, get: function () { return gerdur_core_1.getChartTracks; } });
103
+ Object.defineProperty(exports, "getGenreArtists", { enumerable: true, get: function () { return gerdur_core_1.getGenreArtists; } });
104
+ Object.defineProperty(exports, "getEditorialList", { enumerable: true, get: function () { return gerdur_core_1.getEditorialList; } });
105
+ Object.defineProperty(exports, "getEditorialReleases", { enumerable: true, get: function () { return gerdur_core_1.getEditorialReleases; } });
106
+ Object.defineProperty(exports, "getEditorialSelection", { enumerable: true, get: function () { return gerdur_core_1.getEditorialSelection; } });
107
+ Object.defineProperty(exports, "getEditorialCharts", { enumerable: true, get: function () { return gerdur_core_1.getEditorialCharts; } });
108
+ Object.defineProperty(exports, "getArtistTopTracks", { enumerable: true, get: function () { return gerdur_core_1.getArtistTopTracks; } });
109
+ Object.defineProperty(exports, "getRelatedArtists", { enumerable: true, get: function () { return gerdur_core_1.getRelatedArtists; } });
110
+ Object.defineProperty(exports, "getArtistAlbums", { enumerable: true, get: function () { return gerdur_core_1.getArtistAlbums; } });
111
+ Object.defineProperty(exports, "getArtistPlaylists", { enumerable: true, get: function () { return gerdur_core_1.getArtistPlaylists; } });
112
+ Object.defineProperty(exports, "getArtistRadioTracks", { enumerable: true, get: function () { return gerdur_core_1.getArtistRadioTracks; } });
113
+ Object.defineProperty(exports, "getTrackByISRC", { enumerable: true, get: function () { return gerdur_core_1.getTrackByISRC; } });
114
+ Object.defineProperty(exports, "getAlbumByUPC", { enumerable: true, get: function () { return gerdur_core_1.getAlbumByUPC; } });
115
+ Object.defineProperty(exports, "getTrackPreview", { enumerable: true, get: function () { return gerdur_core_1.getTrackPreview; } });
116
+ Object.defineProperty(exports, "downloadPreview", { enumerable: true, get: function () { return gerdur_core_1.downloadPreview; } });
117
+ Object.defineProperty(exports, "formatName", { enumerable: true, get: function () { return gerdur_core_1.formatName; } });
118
+ Object.defineProperty(exports, "toFormat", { enumerable: true, get: function () { return gerdur_core_1.toFormat; } });
119
+ Object.defineProperty(exports, "DEEZER_FORMATS", { enumerable: true, get: function () { return gerdur_core_1.DEEZER_FORMATS; } });
99
120
  Object.defineProperty(exports, "getUser", { enumerable: true, get: function () { return gerdur_core_1.getUser; } });
100
121
  Object.defineProperty(exports, "getTrackInfo", { enumerable: true, get: function () { return gerdur_core_1.getTrackInfo; } });
101
122
  Object.defineProperty(exports, "getAlbumInfo", { enumerable: true, get: function () { return gerdur_core_1.getAlbumInfo; } });
@@ -2,7 +2,8 @@
2
2
  import { parseInfo, searchMusic } from 'gerdur-core';
3
3
  import type { AdvancedSearchOptions } from './search';
4
4
  import type { Quality, DownloadTrackOptions, DownloadResult } from './api-download';
5
- import type { trackType, userType, advancedSearchFilters, publicApiSearchResponse, searchResultTrack, suggestResult } from 'gerdur-core/types';
5
+ import type { trackType, userType, advancedSearchFilters, publicApiSearchResponse, searchResultTrack, suggestResult, chartType, genreType, editorialType, artistAlbumResult, searchResultArtist, publicApiList, trackTypePublicApi, albumTypePublicApi } from 'gerdur-core/types';
6
+ import type { TrackPreview } from 'gerdur-core';
6
7
  /** Search result categories accepted by {@link Session.search}. */
7
8
  export type SearchType = 'ALBUM' | 'ARTIST' | 'TRACK' | 'PLAYLIST' | 'RADIO' | 'SHOW' | 'USER' | 'LIVESTREAM' | 'CHANNEL';
8
9
  export interface SessionOptions {
@@ -65,6 +66,30 @@ export declare class Session {
65
66
  searchAdvanced(filters: advancedSearchFilters, options?: AdvancedSearchOptions): Promise<publicApiSearchResponse<searchResultTrack>>;
66
67
  /** `deezer.suggest` autocomplete — for "as you type" UIs. `nb` caps items per type. */
67
68
  suggest(query: string, nb?: number): Promise<suggestResult>;
69
+ /** Deezer's genre list (`id` `0` = "All"). */
70
+ genres(): Promise<publicApiList<genreType>>;
71
+ /** The five ranked lists for a genre (`0` = all): `{tracks, albums, artists, playlists, podcasts}`. */
72
+ chart(genreId?: number | string, limit?: number): Promise<chartType>;
73
+ /** Just the track chart for a genre — a ready-to-download list. */
74
+ chartTracks(genreId?: number | string, limit?: number, index?: number): Promise<publicApiList<searchResultTrack>>;
75
+ /** Deezer's editorial sections. */
76
+ editorialSections(): Promise<publicApiList<editorialType>>;
77
+ /** An artist's most popular tracks (public-API shape). */
78
+ artistTopTracks(artistId: number | string, limit?: number): Promise<publicApiList<searchResultTrack>>;
79
+ /** Artists Deezer considers related / similar. */
80
+ relatedArtists(artistId: number | string, limit?: number): Promise<publicApiList<searchResultArtist>>;
81
+ /** An artist's discography (public-API album shape). */
82
+ artistAlbums(artistId: number | string, limit?: number, index?: number): Promise<publicApiList<artistAlbumResult>>;
83
+ /** A ready-made radio (track list) seeded from an artist. */
84
+ artistRadio(artistId: number | string): Promise<publicApiList<searchResultTrack>>;
85
+ /** Resolve an ISRC to the public-API track. Pass `.id` to `getTrackInfo` to download. */
86
+ trackByISRC(isrc: string): Promise<trackTypePublicApi>;
87
+ /** Resolve a UPC/EAN barcode to the public-API album (with its `tracks`). */
88
+ albumByUPC(upc: string): Promise<albumTypePublicApi>;
89
+ /** The 30-second preview clip URL for a track (plain MP3 — no licence, no decryption). */
90
+ trackPreview(track: trackType | string | number): Promise<TrackPreview | null>;
91
+ /** Fetch a track's 30-second preview clip as a `Buffer` (plain MP3). */
92
+ downloadPreview(track: trackType | string | number): Promise<Buffer | null>;
68
93
  /** Return a fully tagged audio Buffer for a track (nothing written to disk). */
69
94
  getTrackBuffer(track: trackType, quality?: Quality, options?: {}): Promise<Buffer | null>;
70
95
  /** Download a single track to disk. */
@@ -75,6 +75,55 @@ class Session {
75
75
  suggest(query, nb) {
76
76
  return (0, gerdur_core_1.suggest)(query, nb);
77
77
  }
78
+ // ─── Browse & discovery (public REST) ──────────────────────────────────────
79
+ /** Deezer's genre list (`id` `0` = "All"). */
80
+ genres() {
81
+ return (0, gerdur_core_1.getGenres)();
82
+ }
83
+ /** The five ranked lists for a genre (`0` = all): `{tracks, albums, artists, playlists, podcasts}`. */
84
+ chart(genreId = 0, limit = 10) {
85
+ return (0, gerdur_core_1.getChart)(genreId, limit);
86
+ }
87
+ /** Just the track chart for a genre — a ready-to-download list. */
88
+ chartTracks(genreId = 0, limit = 100, index = 0) {
89
+ return (0, gerdur_core_1.getChartTracks)(genreId, limit, index);
90
+ }
91
+ /** Deezer's editorial sections. */
92
+ editorialSections() {
93
+ return (0, gerdur_core_1.getEditorialList)();
94
+ }
95
+ /** An artist's most popular tracks (public-API shape). */
96
+ artistTopTracks(artistId, limit = 50) {
97
+ return (0, gerdur_core_1.getArtistTopTracks)(artistId, limit);
98
+ }
99
+ /** Artists Deezer considers related / similar. */
100
+ relatedArtists(artistId, limit = 20) {
101
+ return (0, gerdur_core_1.getRelatedArtists)(artistId, limit);
102
+ }
103
+ /** An artist's discography (public-API album shape). */
104
+ artistAlbums(artistId, limit = 50, index = 0) {
105
+ return (0, gerdur_core_1.getArtistAlbums)(artistId, limit, index);
106
+ }
107
+ /** A ready-made radio (track list) seeded from an artist. */
108
+ artistRadio(artistId) {
109
+ return (0, gerdur_core_1.getArtistRadioTracks)(artistId);
110
+ }
111
+ /** Resolve an ISRC to the public-API track. Pass `.id` to `getTrackInfo` to download. */
112
+ trackByISRC(isrc) {
113
+ return (0, gerdur_core_1.getTrackByISRC)(isrc);
114
+ }
115
+ /** Resolve a UPC/EAN barcode to the public-API album (with its `tracks`). */
116
+ albumByUPC(upc) {
117
+ return (0, gerdur_core_1.getAlbumByUPC)(upc);
118
+ }
119
+ /** The 30-second preview clip URL for a track (plain MP3 — no licence, no decryption). */
120
+ trackPreview(track) {
121
+ return (0, gerdur_core_1.getTrackPreview)(track);
122
+ }
123
+ /** Fetch a track's 30-second preview clip as a `Buffer` (plain MP3). */
124
+ downloadPreview(track) {
125
+ return (0, gerdur_core_1.downloadPreview)(track);
126
+ }
78
127
  /** Return a fully tagged audio Buffer for a track (nothing written to disk). */
79
128
  getTrackBuffer(track, quality = '320', options = {}) {
80
129
  return (0, api_download_1.getTrackBuffer)(track, quality, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gerdur",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "The crucial streaming module for dabox systems.",
5
5
  "author": "Christian",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -48,7 +48,7 @@
48
48
  "adm-zip": "^0.5.16",
49
49
  "chalk": "^4.1.2",
50
50
  "commander": "^9.5.0",
51
- "gerdur-core": "^2.1.0",
51
+ "gerdur-core": "^2.3.0",
52
52
  "dot-prop": "^6.0.1",
53
53
  "got": "^11.8.6",
54
54
  "gradient-string": "^2.0.2",