gerdur 2.12.0 → 2.14.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 +27 -0
- package/README.md +27 -0
- package/dist/package.json +2 -2
- package/dist/src/gerdur.js +54 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +10 -1
- package/dist/src/lib/download-track.d.ts +8 -1
- package/dist/src/lib/download-track.js +2 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.14.0 - 2026-08-31
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`mix:<trackId>`** — download a "more like this" mix seeded from a track.
|
|
8
|
+
Backed by `gerdur-core@2.17.0`'s `getTrackMix`, which returns tracks with
|
|
9
|
+
their `TRACK_TOKEN`s already attached, so unlike the public radio sources
|
|
10
|
+
there is no per-track lookup before downloading.
|
|
11
|
+
- **`library`** (also `favorites` / `favourites`) — pick from the logged-in
|
|
12
|
+
account's own loved tracks, **private library included**. The public profile
|
|
13
|
+
endpoints can't see these.
|
|
14
|
+
- **`gerdur-core@^2.17.0`** — re-exports the whole private-library surface:
|
|
15
|
+
`getMyPlaylists`, `getMyFavorite{Tracks,TrackIds,Albums,Artists,Playlists,Radios,Shows}`,
|
|
16
|
+
`getTrackMix`.
|
|
17
|
+
|
|
18
|
+
## 2.13.0 - 2026-08-31
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **`--fast`** — skip the expensive per-track metadata: full studio credits, BPM,
|
|
23
|
+
and the Musixmatch fallback for tracks Deezer has no lyrics for. Tagging is
|
|
24
|
+
what actually burns Deezer's rate limit (a 14-track album costs ~54 requests to
|
|
25
|
+
tag against 2 to fetch), and this takes that album to **7 requests — 81% fewer
|
|
26
|
+
against the quota**. Audio, cover, Deezer lyrics and `.lrc` sidecars are
|
|
27
|
+
unchanged; you lose credits, BPM and non-Deezer lyrics.
|
|
28
|
+
- **`gerdur-core@^2.16.0`** for the underlying `lyricsFallback` option.
|
|
29
|
+
|
|
3
30
|
## 2.12.0 - 2026-08-31
|
|
4
31
|
|
|
5
32
|
### Changed
|
package/README.md
CHANGED
|
@@ -166,6 +166,7 @@ All options are optional; the interactive prompt fills in the rest. Provide
|
|
|
166
166
|
| `--search-limit` | | number | How many results to fetch (default 50) |
|
|
167
167
|
| `--preview` | | — | Download 30-second `.preview.mp3` clips — no `--quality`, no `arl` |
|
|
168
168
|
| `--enrich` | | — | Embed a higher-res cover from the Cover Art Archive (by ISRC) |
|
|
169
|
+
| `--fast` | | — | Skip per-track credits, BPM and the Musixmatch lyrics fallback — **~81% fewer Deezer requests** |
|
|
169
170
|
| `--update` | `-U` | — | Self-update (prebuilt binary only) |
|
|
170
171
|
| `--help` | `-h` | — | Full help |
|
|
171
172
|
|
|
@@ -191,6 +192,8 @@ Anywhere a URL is accepted (`-u`, the interactive prompt, or a line in
|
|
|
191
192
|
| `radio:<id>` | a radio's current playlist |
|
|
192
193
|
| `chart` · `chart:<genreId>` | this week's chart (optionally a genre) |
|
|
193
194
|
| `artist-top:<artistId>` | an artist's most popular tracks |
|
|
195
|
+
| `mix:<trackId>` | a "more like this" mix seeded from a track |
|
|
196
|
+
| `library` · `favorites` | your own loved tracks (private library) |
|
|
194
197
|
| `episode:<episodeId>` | a single podcast episode (plain MP3, saved to `Podcasts/`) |
|
|
195
198
|
|
|
196
199
|
All of these work in `--headless` mode too (`gerdur -d -q 320 -u flow`).
|
|
@@ -245,6 +248,30 @@ at 1800 px). No match or the services are down? It silently keeps Deezer's cover
|
|
|
245
248
|
gerdur --enrich -q flac -u https://deezer.com/album/302127
|
|
246
249
|
```
|
|
247
250
|
|
|
251
|
+
### Downloading a lot at once (`--fast`)
|
|
252
|
+
|
|
253
|
+
Tagging, not downloading, is what burns through Deezer's rate limit: a 14-track
|
|
254
|
+
album costs about **54 requests to tag** and 2 to fetch. Most of that is per-track
|
|
255
|
+
— full credits and BPM (`song.getData` + a public lookup each), plus a Musixmatch
|
|
256
|
+
scrape for every track Deezer has no lyrics for, which fails outright on many
|
|
257
|
+
networks.
|
|
258
|
+
|
|
259
|
+
`--fast` skips exactly those. Same audio, same cover, same Deezer lyrics, same
|
|
260
|
+
`.lrc` sidecars:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
gerdur --fast -q flac -u https://deezer.com/album/302127
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
| 14-track album | requests | against Deezer's quota |
|
|
267
|
+
| :--- | ---: | ---: |
|
|
268
|
+
| default | 54 | 36 |
|
|
269
|
+
| `--fast` | **7** | **7** |
|
|
270
|
+
|
|
271
|
+
You lose full studio credits, BPM, and lyrics for tracks Deezer doesn't carry.
|
|
272
|
+
Worth it when you're pulling a large library and getting rate-limited; leave it
|
|
273
|
+
off for a handful of tracks you care about.
|
|
274
|
+
|
|
248
275
|
### Output templates
|
|
249
276
|
|
|
250
277
|
`--output` (or `saveLayout` in the config) is a path template. `{TOKEN}`s are
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gerdur",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Command-line music downloader for Deezer (Spotify/Tidal links resolved via ISRC matching) with automatic MP3/FLAC tagging, synced lyrics and a side-effect-free programmatic API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deezer",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"adm-zip": "^0.5.16",
|
|
64
64
|
"chalk": "^4.1.2",
|
|
65
65
|
"commander": "^9.5.0",
|
|
66
|
-
"gerdur-core": "^2.
|
|
66
|
+
"gerdur-core": "^2.17.0",
|
|
67
67
|
"dot-prop": "^6.0.1",
|
|
68
68
|
"got": "^11.8.6",
|
|
69
69
|
"gradient-string": "^2.0.2",
|
package/dist/src/gerdur.js
CHANGED
|
@@ -60,7 +60,8 @@ const cmd = new commander_1.Command()
|
|
|
60
60
|
.option('--dur-max <seconds>', 'Search filter: maximum duration')
|
|
61
61
|
.option('--search-limit <n>', 'Max search results to fetch', '50')
|
|
62
62
|
.option('--preview', 'Download the 30-second preview clips instead of full tracks', false)
|
|
63
|
-
.option('--enrich', 'Embed a higher-res cover from the Cover Art Archive (looked up by ISRC)', false)
|
|
63
|
+
.option('--enrich', 'Embed a higher-res cover from the Cover Art Archive (looked up by ISRC)', false)
|
|
64
|
+
.option('--fast', 'Skip per-track credits, BPM and the Musixmatch lyrics fallback (~81% fewer Deezer requests)', false);
|
|
64
65
|
if (process.pkg) {
|
|
65
66
|
cmd.option('-U, --update', 'Update this program to latest version');
|
|
66
67
|
}
|
|
@@ -147,6 +148,36 @@ const pickAndHydrate = async (results, id) => {
|
|
|
147
148
|
}
|
|
148
149
|
return { info: { type: 'track', id }, linktype: 'track', linkinfo: {}, tracks };
|
|
149
150
|
};
|
|
151
|
+
/**
|
|
152
|
+
* Like `pickAndHydrate`, but for gateway tracks — the library and mix endpoints
|
|
153
|
+
* already return full tracks with `TRACK_TOKEN`s, so there is nothing to fetch.
|
|
154
|
+
*/
|
|
155
|
+
const pickGwTracks = async (results, id) => {
|
|
156
|
+
if (!results.length) {
|
|
157
|
+
console.log(signale_1.default.warn('Nothing to download.'));
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
let picks = results;
|
|
161
|
+
if (!options.headless) {
|
|
162
|
+
const choice = await (0, prompts_1.default)([
|
|
163
|
+
{
|
|
164
|
+
type: 'multiselect',
|
|
165
|
+
name: 'items',
|
|
166
|
+
message: `Select tracks to download. ${results.length} available.`,
|
|
167
|
+
choices: results.map((r) => ({
|
|
168
|
+
title: `${r.SNG_TITLE} — ${r.ART_NAME}`,
|
|
169
|
+
value: r,
|
|
170
|
+
description: `Album: ${r.ALB_TITLE ?? 'Unknown'} · ${(0, util_1.formatSecondsReadable)(Number(r.DURATION) || 0)}`,
|
|
171
|
+
})),
|
|
172
|
+
},
|
|
173
|
+
], { onCancel });
|
|
174
|
+
picks = choice.items ?? [];
|
|
175
|
+
}
|
|
176
|
+
if (!picks.length) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return { info: { type: 'track', id }, linktype: 'track', linkinfo: {}, tracks: picks.map(stampVersion) };
|
|
180
|
+
};
|
|
150
181
|
const resolveAdvancedSearch = async (filters) => {
|
|
151
182
|
const limit = toFiniteNumber(options.searchLimit) ?? 50;
|
|
152
183
|
const { data: results, query, usedFallback } = await (0, search_1.searchAdvancedTracks)(filters, { limit });
|
|
@@ -234,7 +265,7 @@ const startDownload = async (saveLayout, url, skipPrompt) => {
|
|
|
234
265
|
{
|
|
235
266
|
type: 'text',
|
|
236
267
|
name: 'query',
|
|
237
|
-
message: 'Enter a URL, a search term, or a prefix (search: isrc: upc: flow radio: chart artist-top: episode:):',
|
|
268
|
+
message: 'Enter a URL, a search term, or a prefix (search: isrc: upc: flow radio: chart artist-top: mix: library episode:):',
|
|
238
269
|
validate: (value) => (value ? true : false),
|
|
239
270
|
},
|
|
240
271
|
], { onCancel });
|
|
@@ -306,6 +337,26 @@ const startDownload = async (saveLayout, url, skipPrompt) => {
|
|
|
306
337
|
throw new Error(`Artist ${aid} returned no top tracks.`);
|
|
307
338
|
}
|
|
308
339
|
}
|
|
340
|
+
// `mix:<trackId>` — a "more like this" mix. Tokens come attached, so unlike
|
|
341
|
+
// the public radio sources there is no per-track lookup before downloading.
|
|
342
|
+
if (!searchData && url && url.startsWith('mix:')) {
|
|
343
|
+
const sid = url.slice('mix:'.length).trim();
|
|
344
|
+
console.log(signale_1.default.info(`Building a mix from track ${sid}…`));
|
|
345
|
+
const { data } = await (0, gerdur_core_1.getTrackMix)(sid, toFiniteNumber(options.searchLimit) ?? 40);
|
|
346
|
+
searchData = await pickGwTracks(data, `mix:${sid}`);
|
|
347
|
+
if (!searchData) {
|
|
348
|
+
throw new Error(`Track ${sid} returned no mix.`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// `library` — the logged-in account's loved tracks, private ones included.
|
|
352
|
+
if (!searchData && url && (url === 'library' || url === 'favorites' || url === 'favourites')) {
|
|
353
|
+
console.log(signale_1.default.info('Fetching your loved tracks…'));
|
|
354
|
+
const { data, total } = await (0, gerdur_core_1.getMyFavoriteTracks)(undefined, toFiniteNumber(options.searchLimit) ?? 100);
|
|
355
|
+
if (!data.length) {
|
|
356
|
+
throw new Error(`Your library has no loved tracks (total ${total}).`);
|
|
357
|
+
}
|
|
358
|
+
searchData = await pickGwTracks(data, 'library');
|
|
359
|
+
}
|
|
309
360
|
// `episode:<id>` — a podcast episode (a plain MP3: no licence, decryption or tagging).
|
|
310
361
|
if (!searchData && url && url.startsWith('episode:')) {
|
|
311
362
|
const eid = url.slice('episode:'.length).trim();
|
|
@@ -486,6 +537,7 @@ const startDownload = async (saveLayout, url, skipPrompt) => {
|
|
|
486
537
|
message: `(${index}/${data.tracks.length})`,
|
|
487
538
|
lrc,
|
|
488
539
|
enrich: Boolean(options.enrich),
|
|
540
|
+
fast: Boolean(options.fast),
|
|
489
541
|
prefetched: prefetchedUrls.get(track.SNG_ID) ?? null,
|
|
490
542
|
});
|
|
491
543
|
// Add to saved list
|
package/dist/src/index.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export type { MBRecording, MBRelease, MBArtistCredit, CoverArt, CoverArtImage }
|
|
|
63
63
|
export { getTrackBuffer, getTaggedTrack, downloadTrackToFile } from './lib/api-download';
|
|
64
64
|
export type { Quality, GetTrackBufferOptions, DownloadTrackOptions, DownloadResult } from './lib/api-download';
|
|
65
65
|
export { default as Config, globalConfigPath, resolveConfigFile } from './lib/config';
|
|
66
|
-
export { initDeezerApi, parseInfo, searchMusic, searchFacets, searchPublicApi, searchTracks, searchAlbums, searchArtists, searchPlaylists, buildAdvancedQuery, suggest, getGenres, getChart, getChartTracks, getGenreArtists, getEditorialList, getEditorialReleases, getEditorialSelection, getEditorialCharts, getArtistTopTracks, getRelatedArtists, getArtistAlbums, getArtistPlaylists, getArtistRadioTracks, getUserFlow, getUserFavoriteTracks, getUserFavoriteAlbums, getUserFavoriteArtists, getUserPlaylists, getUserRadios, getUserChartTracks, getRadios, getRadioTracks, getRadioGenres, getEpisode, getShowEpisodes, refreshTrackTokens, getTrackByISRC, getAlbumByUPC, getTrackPreview, downloadPreview, formatName, toFormat, DEEZER_FORMATS, getUser, getTrackInfo, getAlbumInfo, getAlbumTracks, getPlaylistInfo, getPlaylistTracks, getArtistInfo, getDiscography, getLyrics, getTrackDownloadUrl, resolveDownloadUrls, streamTrackDownload, downloadTrackBuffer, createDecryptStream, getStream, addTrackTags, getRichAlbum, normalizeContributors, toLrc, GeoBlocked, DeezerError, } from 'gerdur-core';
|
|
66
|
+
export { initDeezerApi, parseInfo, searchMusic, searchFacets, searchPublicApi, searchTracks, searchAlbums, searchArtists, searchPlaylists, buildAdvancedQuery, suggest, getGenres, getChart, getChartTracks, getGenreArtists, getEditorialList, getEditorialReleases, getEditorialSelection, getEditorialCharts, getArtistTopTracks, getRelatedArtists, getArtistAlbums, getArtistPlaylists, getArtistRadioTracks, getUserFlow, getUserFavoriteTracks, getUserFavoriteAlbums, getUserFavoriteArtists, getUserPlaylists, getUserRadios, getUserChartTracks, getRadios, getRadioTracks, getRadioGenres, getEpisode, getShowEpisodes, getMyPlaylists, getMyFavoriteTracks, getMyFavoriteTrackIds, getMyFavoriteAlbums, getMyFavoriteArtists, getMyFavoritePlaylists, getMyFavoriteRadios, getMyFavoriteShows, getTrackMix, refreshTrackTokens, getTrackByISRC, getAlbumByUPC, getTrackPreview, downloadPreview, formatName, toFormat, DEEZER_FORMATS, getUser, getTrackInfo, getAlbumInfo, getAlbumTracks, getPlaylistInfo, getPlaylistTracks, getArtistInfo, getDiscography, getLyrics, getTrackDownloadUrl, resolveDownloadUrls, streamTrackDownload, downloadTrackBuffer, createDecryptStream, getStream, addTrackTags, getRichAlbum, normalizeContributors, toLrc, GeoBlocked, DeezerError, } from 'gerdur-core';
|
|
67
67
|
export type { AddTrackTagsOptions, TaggedTrack, TrackTagModel, RichAlbum, ResolvedUrl, NormalizedContributors, } from 'gerdur-core';
|
|
68
68
|
export type { advancedSearchFilters, searchOrder, searchEntity, publicApiSearchOptions, publicApiSearchResponse, searchResultTrack, searchResultAlbum, searchResultArtist, searchResultPlaylist, suggestResult, publicApiList, chartType, chartTrack, chartAlbum, chartArtist, chartPlaylist, chartPodcast, genreType, editorialType, artistAlbumResult, userFavoriteTrack, userFavoriteAlbum, userFavoriteArtist, userPlaylistResult, radioResult, radioGenre, } from 'gerdur-core/types';
|
|
69
69
|
export type { Quality as MediaFormat, DeezerFormat, TrackPreview, StreamTrackOptions, TrackStream, StreamResponse, DeezerErrorPayload, } from 'gerdur-core';
|
package/dist/src/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
34
34
|
};
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getUserFavoriteAlbums = exports.getUserFavoriteTracks = exports.getUserFlow = 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.searchFacets = exports.searchMusic = exports.parseInfo = exports.initDeezerApi = exports.resolveConfigFile = exports.globalConfigPath = exports.Config = exports.downloadTrackToFile = exports.getTaggedTrack = exports.getTrackBuffer = exports.PoliteJsonClient = exports.getCoverArtByISRC = exports.getRecordingCoverArt = exports.getBestCoverArtUrl = exports.getCoverArt = exports.getMusicBrainzRelease = exports.getMusicBrainzRecording = exports.lookupRecordingByISRC = exports.configureMusicBrainz = exports.defaultSession = exports.CoreSession = exports.createCoreSession = exports.Session = exports.createSession = exports.getArl = exports.LoginError = exports.loginWithEmail = void 0;
|
|
37
|
-
exports.DeezerError = exports.GeoBlocked = exports.toLrc = exports.normalizeContributors = exports.getRichAlbum = exports.addTrackTags = exports.getStream = exports.createDecryptStream = exports.downloadTrackBuffer = exports.streamTrackDownload = exports.resolveDownloadUrls = exports.getTrackDownloadUrl = 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.refreshTrackTokens = exports.getShowEpisodes = exports.getEpisode = exports.getRadioGenres = exports.getRadioTracks = exports.getRadios = exports.getUserChartTracks = exports.getUserRadios = exports.getUserPlaylists = exports.getUserFavoriteArtists = void 0;
|
|
37
|
+
exports.DeezerError = exports.GeoBlocked = exports.toLrc = exports.normalizeContributors = exports.getRichAlbum = exports.addTrackTags = exports.getStream = exports.createDecryptStream = exports.downloadTrackBuffer = exports.streamTrackDownload = exports.resolveDownloadUrls = exports.getTrackDownloadUrl = 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.refreshTrackTokens = exports.getTrackMix = exports.getMyFavoriteShows = exports.getMyFavoriteRadios = exports.getMyFavoritePlaylists = exports.getMyFavoriteArtists = exports.getMyFavoriteAlbums = exports.getMyFavoriteTrackIds = exports.getMyFavoriteTracks = exports.getMyPlaylists = exports.getShowEpisodes = exports.getEpisode = exports.getRadioGenres = exports.getRadioTracks = exports.getRadios = exports.getUserChartTracks = exports.getUserRadios = exports.getUserPlaylists = exports.getUserFavoriteArtists = void 0;
|
|
38
38
|
// ─── Authentication ────────────────────────────────────────────────────────
|
|
39
39
|
const email_login_1 = require("./lib/email-login");
|
|
40
40
|
Object.defineProperty(exports, "loginWithEmail", { enumerable: true, get: function () { return email_login_1.loginWithEmail; } });
|
|
@@ -142,6 +142,15 @@ Object.defineProperty(exports, "getRadioTracks", { enumerable: true, get: functi
|
|
|
142
142
|
Object.defineProperty(exports, "getRadioGenres", { enumerable: true, get: function () { return gerdur_core_3.getRadioGenres; } });
|
|
143
143
|
Object.defineProperty(exports, "getEpisode", { enumerable: true, get: function () { return gerdur_core_3.getEpisode; } });
|
|
144
144
|
Object.defineProperty(exports, "getShowEpisodes", { enumerable: true, get: function () { return gerdur_core_3.getShowEpisodes; } });
|
|
145
|
+
Object.defineProperty(exports, "getMyPlaylists", { enumerable: true, get: function () { return gerdur_core_3.getMyPlaylists; } });
|
|
146
|
+
Object.defineProperty(exports, "getMyFavoriteTracks", { enumerable: true, get: function () { return gerdur_core_3.getMyFavoriteTracks; } });
|
|
147
|
+
Object.defineProperty(exports, "getMyFavoriteTrackIds", { enumerable: true, get: function () { return gerdur_core_3.getMyFavoriteTrackIds; } });
|
|
148
|
+
Object.defineProperty(exports, "getMyFavoriteAlbums", { enumerable: true, get: function () { return gerdur_core_3.getMyFavoriteAlbums; } });
|
|
149
|
+
Object.defineProperty(exports, "getMyFavoriteArtists", { enumerable: true, get: function () { return gerdur_core_3.getMyFavoriteArtists; } });
|
|
150
|
+
Object.defineProperty(exports, "getMyFavoritePlaylists", { enumerable: true, get: function () { return gerdur_core_3.getMyFavoritePlaylists; } });
|
|
151
|
+
Object.defineProperty(exports, "getMyFavoriteRadios", { enumerable: true, get: function () { return gerdur_core_3.getMyFavoriteRadios; } });
|
|
152
|
+
Object.defineProperty(exports, "getMyFavoriteShows", { enumerable: true, get: function () { return gerdur_core_3.getMyFavoriteShows; } });
|
|
153
|
+
Object.defineProperty(exports, "getTrackMix", { enumerable: true, get: function () { return gerdur_core_3.getTrackMix; } });
|
|
145
154
|
Object.defineProperty(exports, "refreshTrackTokens", { enumerable: true, get: function () { return gerdur_core_3.refreshTrackTokens; } });
|
|
146
155
|
Object.defineProperty(exports, "getTrackByISRC", { enumerable: true, get: function () { return gerdur_core_3.getTrackByISRC; } });
|
|
147
156
|
Object.defineProperty(exports, "getAlbumByUPC", { enumerable: true, get: function () { return gerdur_core_3.getAlbumByUPC; } });
|
|
@@ -27,6 +27,13 @@ interface downloadTrackProps {
|
|
|
27
27
|
* to Deezer's cover if there's no match. Off by default.
|
|
28
28
|
*/
|
|
29
29
|
enrich?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Skip the expensive per-track metadata: full credits, BPM, and the Musixmatch
|
|
32
|
+
* fallback for tracks Deezer has no lyrics for. Measured on a 14-track album
|
|
33
|
+
* that is 54 requests down to 7 — 81% fewer against Deezer's rate limit — at
|
|
34
|
+
* the cost of those fields. Off by default.
|
|
35
|
+
*/
|
|
36
|
+
fast?: boolean;
|
|
30
37
|
/**
|
|
31
38
|
* A download URL already resolved for this track by a batch `resolveDownloadUrls`
|
|
32
39
|
* pass. When set, the per-track `get_url` request and quality-fallback round-trips
|
|
@@ -40,5 +47,5 @@ interface downloadTrackProps {
|
|
|
40
47
|
format: string;
|
|
41
48
|
} | null;
|
|
42
49
|
}
|
|
43
|
-
declare const downloadTrack: ({ track, quality, info, coverSizes, path, totalTracks, trackNumber, fallbackTrack, fallbackQuality, isFallback, isQualityFallback, overwrite, message, lrc, enrich, prefetched, }: downloadTrackProps) => Promise<string | undefined>;
|
|
50
|
+
declare const downloadTrack: ({ track, quality, info, coverSizes, path, totalTracks, trackNumber, fallbackTrack, fallbackQuality, isFallback, isQualityFallback, overwrite, message, lrc, enrich, fast, prefetched, }: downloadTrackProps) => Promise<string | undefined>;
|
|
44
51
|
export default downloadTrack;
|
|
@@ -17,7 +17,7 @@ const pipeline = (0, util_1.promisify)(stream_1.default.pipeline);
|
|
|
17
17
|
const simulate = process.env.SIMULATE;
|
|
18
18
|
/** media-API format string -> gerdur's numeric quality */
|
|
19
19
|
const FORMAT_TO_QUALITY = { FLAC: 9, MP3_320: 3, MP3_256: 3, MP3_128: 1, MP3_64: 1 };
|
|
20
|
-
const downloadTrack = async ({ track, quality, info, coverSizes, path, totalTracks, trackNumber = true, fallbackTrack = true, fallbackQuality = true, isFallback = false, isQualityFallback = false, overwrite = false, message = '', lrc = true, enrich = false, prefetched = null, }) => {
|
|
20
|
+
const downloadTrack = async ({ track, quality, info, coverSizes, path, totalTracks, trackNumber = true, fallbackTrack = true, fallbackQuality = true, isFallback = false, isQualityFallback = false, overwrite = false, message = '', lrc = true, enrich = false, fast = false, prefetched = null, }) => {
|
|
21
21
|
(0, log_update_1.default)(signale_1.default.pending(track.SNG_TITLE + ' by ' + track.ART_NAME + ' from ' + track.ALB_TITLE));
|
|
22
22
|
try {
|
|
23
23
|
let ext = '.mp3', fileSize = 0, downloaded = 0, coverSize = 500;
|
|
@@ -159,6 +159,7 @@ const downloadTrack = async ({ track, quality, info, coverSizes, path, totalTrac
|
|
|
159
159
|
// written by a stream, so the track is never held in memory.
|
|
160
160
|
const model = await (0, gerdur_core_1.resolveTagModel)(track, {
|
|
161
161
|
coverSize,
|
|
162
|
+
...(fast ? { richCredits: false, lyricsFallback: false } : {}),
|
|
162
163
|
...(enrichedCover ? { cover: enrichedCover } : {}),
|
|
163
164
|
});
|
|
164
165
|
(0, log_update_1.default)(signale_1.default.pending('Saving ' + track.SNG_TITLE + ' by ' + track.ART_NAME));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gerdur",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Command-line music downloader for Deezer (Spotify/Tidal links resolved via ISRC matching) with automatic MP3/FLAC tagging, synced lyrics and a side-effect-free programmatic API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deezer",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"adm-zip": "^0.5.16",
|
|
64
64
|
"chalk": "^4.1.2",
|
|
65
65
|
"commander": "^9.5.0",
|
|
66
|
-
"gerdur-core": "^2.
|
|
66
|
+
"gerdur-core": "^2.17.0",
|
|
67
67
|
"dot-prop": "^6.0.1",
|
|
68
68
|
"got": "^11.8.6",
|
|
69
69
|
"gradient-string": "^2.0.2",
|