gerdur 2.4.0 → 2.5.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 +19 -0
- package/README.md +10 -4
- package/dist/package.json +2 -2
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +5 -1
- package/dist/src/lib/decrypt.d.ts +7 -2
- package/dist/src/lib/decrypt.js +15 -2
- package/dist/src/lib/download-track.js +4 -1
- package/dist/src/lib/session.d.ts +8 -1
- package/dist/src/lib/session.js +9 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.5.0 - 2026-08-31
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **`gerdur-core@^2.5.0`.** Track decryption in the download pipeline is now
|
|
8
|
+
**streamed** to a temp file (`decryptFileToFile`) instead of read fully into
|
|
9
|
+
memory twice — peak memory during decrypt drops to ~one 2048-byte stripe.
|
|
10
|
+
- Gateway failures now surface as `DeezerError` (re-exported) with `code` /
|
|
11
|
+
`keys` / `retryable`, and gateway retries are bounded (no more infinite spin
|
|
12
|
+
on a persistently failing endpoint).
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Re-exports: `streamTrackDownload`, `createDecryptStream`, `getStream`,
|
|
17
|
+
`DeezerError` + the `StreamTrackOptions` / `TrackStream` / `StreamResponse` types.
|
|
18
|
+
- **`Session.streamTrack(track, quality?, options?)`** — download a track as a
|
|
19
|
+
constant-memory stream of decrypted audio (`{stream, size, startedAt}`),
|
|
20
|
+
with `onProgress` and `resumeFrom`.
|
|
21
|
+
|
|
3
22
|
## 2.4.0 - 2026-08-31
|
|
4
23
|
|
|
5
24
|
### Added
|
package/README.md
CHANGED
|
@@ -258,13 +258,17 @@ Session methods:
|
|
|
258
258
|
- **browse** — `genres`, `chart`, `chartTracks`, `editorialSections`,
|
|
259
259
|
`artistTopTracks`, `relatedArtists`, `artistAlbums`, `artistRadio`,
|
|
260
260
|
`trackByISRC`, `albumByUPC`
|
|
261
|
-
- **user / download** — `getUser`, `getTrackBuffer`, `
|
|
262
|
-
`downloadTracks`, `downloadUrl`, `trackPreview`, `downloadPreview`
|
|
261
|
+
- **user / download** — `getUser`, `getTrackBuffer`, `streamTrack`,
|
|
262
|
+
`downloadTrack`, `downloadTracks`, `downloadUrl`, `trackPreview`, `downloadPreview`
|
|
263
263
|
|
|
264
264
|
Every download call is silent; `downloadTracks` / `downloadUrl` accept
|
|
265
265
|
`concurrency` and an `onProgress` callback and return one `{path, written} | null`
|
|
266
266
|
per track.
|
|
267
267
|
|
|
268
|
+
`streamTrack(track, quality?, options?)` returns `{stream, size, startedAt,
|
|
269
|
+
isEncrypted}` — decrypted audio at constant memory, with `onProgress(got, total)`
|
|
270
|
+
and `resumeFrom` (bytes). Pipe `stream` to a file or your own tag muxer.
|
|
271
|
+
|
|
268
272
|
`searchAdvanced(filters, options?)` builds a Deezer advanced-search query from
|
|
269
273
|
`{query?, artist?, album?, track?, label?, durMin?, durMax?, bpmMin?, bpmMax?}`
|
|
270
274
|
and takes `{order?, strict?, limit?, index?, fallback?}`. Deezer's operators are
|
|
@@ -315,8 +319,10 @@ or `createSession`):
|
|
|
315
319
|
`getEditorialCharts`, `getArtistTopTracks`, `getRelatedArtists`,
|
|
316
320
|
`getArtistAlbums`, `getArtistPlaylists`, `getArtistRadioTracks`,
|
|
317
321
|
`getTrackByISRC`, `getAlbumByUPC`
|
|
318
|
-
- **Download** — `getTrackDownloadUrl`, `resolveDownloadUrls`,
|
|
319
|
-
`
|
|
322
|
+
- **Download** — `getTrackDownloadUrl`, `resolveDownloadUrls`,
|
|
323
|
+
`streamTrackDownload`, `createDecryptStream`, `getStream`, `getTrackPreview`,
|
|
324
|
+
`downloadPreview`, `formatName`, `toFormat`, `DEEZER_FORMATS`
|
|
325
|
+
- **Errors** — `DeezerError` (`code` / `keys` / `retryable`), `GeoBlocked`
|
|
320
326
|
|
|
321
327
|
### Auth & config helpers
|
|
322
328
|
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gerdur",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.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.
|
|
51
|
+
"gerdur-core": "^2.5.0",
|
|
52
52
|
"dot-prop": "^6.0.1",
|
|
53
53
|
"got": "^11.8.6",
|
|
54
54
|
"gradient-string": "^2.0.2",
|
package/dist/src/index.d.ts
CHANGED
|
@@ -59,7 +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, 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';
|
|
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, streamTrackDownload, createDecryptStream, getStream, addTrackTags, getRichAlbum, normalizeContributors, toLrc, GeoBlocked, DeezerError, } from 'gerdur-core';
|
|
63
63
|
export type { AddTrackTagsOptions, TaggedTrack, TrackTagModel, RichAlbum, ResolvedUrl, NormalizedContributors, } from 'gerdur-core';
|
|
64
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';
|
|
65
|
+
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.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
|
+
exports.DeezerError = exports.GeoBlocked = exports.toLrc = exports.normalizeContributors = exports.getRichAlbum = exports.addTrackTags = exports.getStream = exports.createDecryptStream = exports.streamTrackDownload = exports.resolveDownloadUrls = exports.getTrackDownloadUrl = 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; } });
|
|
@@ -128,8 +128,12 @@ Object.defineProperty(exports, "getDiscography", { enumerable: true, get: functi
|
|
|
128
128
|
Object.defineProperty(exports, "getLyrics", { enumerable: true, get: function () { return gerdur_core_1.getLyrics; } });
|
|
129
129
|
Object.defineProperty(exports, "getTrackDownloadUrl", { enumerable: true, get: function () { return gerdur_core_1.getTrackDownloadUrl; } });
|
|
130
130
|
Object.defineProperty(exports, "resolveDownloadUrls", { enumerable: true, get: function () { return gerdur_core_1.resolveDownloadUrls; } });
|
|
131
|
+
Object.defineProperty(exports, "streamTrackDownload", { enumerable: true, get: function () { return gerdur_core_1.streamTrackDownload; } });
|
|
132
|
+
Object.defineProperty(exports, "createDecryptStream", { enumerable: true, get: function () { return gerdur_core_1.createDecryptStream; } });
|
|
133
|
+
Object.defineProperty(exports, "getStream", { enumerable: true, get: function () { return gerdur_core_1.getStream; } });
|
|
131
134
|
Object.defineProperty(exports, "addTrackTags", { enumerable: true, get: function () { return gerdur_core_1.addTrackTags; } });
|
|
132
135
|
Object.defineProperty(exports, "getRichAlbum", { enumerable: true, get: function () { return gerdur_core_1.getRichAlbum; } });
|
|
133
136
|
Object.defineProperty(exports, "normalizeContributors", { enumerable: true, get: function () { return gerdur_core_1.normalizeContributors; } });
|
|
134
137
|
Object.defineProperty(exports, "toLrc", { enumerable: true, get: function () { return gerdur_core_1.toLrc; } });
|
|
135
138
|
Object.defineProperty(exports, "GeoBlocked", { enumerable: true, get: function () { return gerdur_core_1.GeoBlocked; } });
|
|
139
|
+
Object.defineProperty(exports, "DeezerError", { enumerable: true, get: function () { return gerdur_core_1.DeezerError; } });
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
export { decryptDownload, TrackDecryptStream } from 'gerdur-core';
|
|
3
|
-
/** Decrypt a track that was streamed to a temp file. */
|
|
2
|
+
export { decryptDownload, createDecryptStream, TrackDecryptStream } from 'gerdur-core';
|
|
3
|
+
/** Decrypt a track that was streamed to a temp file (buffered — reads the file into memory). */
|
|
4
4
|
export declare const decryptDownloadFile: (tmpfile: string, trackId: string) => Buffer;
|
|
5
|
+
/**
|
|
6
|
+
* Decrypt `src` → `dest` as a stream — peak memory is ~one 2048-byte stripe,
|
|
7
|
+
* not the whole file twice. Use this over `decryptDownloadFile` for large FLACs.
|
|
8
|
+
*/
|
|
9
|
+
export declare const decryptFileToFile: (src: string, dest: string, trackId: string) => Promise<void>;
|
package/dist/src/lib/decrypt.js
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.decryptDownloadFile = exports.TrackDecryptStream = exports.decryptDownload = void 0;
|
|
6
|
+
exports.decryptFileToFile = exports.decryptDownloadFile = exports.TrackDecryptStream = exports.createDecryptStream = exports.decryptDownload = void 0;
|
|
4
7
|
/**
|
|
5
8
|
* Track decryption. As of gerdur-core@1.0.2 the core `decryptDownload` is a
|
|
6
9
|
* correct, dependency-free Blowfish that works on every Node (it no longer
|
|
7
10
|
* touches OpenSSL's removed `bf-cbc`), at ~290 MiB/s — so there is nothing left
|
|
8
11
|
* to wrap or fall back to here.
|
|
9
12
|
*/
|
|
13
|
+
const stream_1 = __importDefault(require("stream"));
|
|
14
|
+
const util_1 = require("util");
|
|
10
15
|
const fs_1 = require("fs");
|
|
11
16
|
const gerdur_core_1 = require("gerdur-core");
|
|
12
17
|
var gerdur_core_2 = require("gerdur-core");
|
|
13
18
|
Object.defineProperty(exports, "decryptDownload", { enumerable: true, get: function () { return gerdur_core_2.decryptDownload; } });
|
|
19
|
+
Object.defineProperty(exports, "createDecryptStream", { enumerable: true, get: function () { return gerdur_core_2.createDecryptStream; } });
|
|
14
20
|
Object.defineProperty(exports, "TrackDecryptStream", { enumerable: true, get: function () { return gerdur_core_2.TrackDecryptStream; } });
|
|
15
|
-
|
|
21
|
+
const pipeline = (0, util_1.promisify)(stream_1.default.pipeline);
|
|
22
|
+
/** Decrypt a track that was streamed to a temp file (buffered — reads the file into memory). */
|
|
16
23
|
const decryptDownloadFile = (tmpfile, trackId) => (0, gerdur_core_1.decryptDownload)((0, fs_1.readFileSync)(tmpfile), trackId);
|
|
17
24
|
exports.decryptDownloadFile = decryptDownloadFile;
|
|
25
|
+
/**
|
|
26
|
+
* Decrypt `src` → `dest` as a stream — peak memory is ~one 2048-byte stripe,
|
|
27
|
+
* not the whole file twice. Use this over `decryptDownloadFile` for large FLACs.
|
|
28
|
+
*/
|
|
29
|
+
const decryptFileToFile = (src, dest, trackId) => pipeline((0, fs_1.createReadStream)(src), (0, gerdur_core_1.createDecryptStream)(trackId), (0, fs_1.createWriteStream)(dest));
|
|
30
|
+
exports.decryptFileToFile = decryptFileToFile;
|
|
@@ -145,7 +145,10 @@ const downloadTrack = async ({ track, quality, info, coverSizes, path, totalTrac
|
|
|
145
145
|
let outFile;
|
|
146
146
|
if (trackData.isEncrypted) {
|
|
147
147
|
(0, log_update_1.default)(signale_1.default.pending('Decrypting ' + track.SNG_TITLE + ' by ' + track.ART_NAME));
|
|
148
|
-
|
|
148
|
+
const decfile = tmpfile + '.dec';
|
|
149
|
+
await (0, decrypt_1.decryptFileToFile)(tmpfile, decfile, track.SNG_ID); // streamed — ~one stripe in memory
|
|
150
|
+
outFile = (0, fs_1.readFileSync)(decfile);
|
|
151
|
+
(0, fs_1.unlinkSync)(decfile);
|
|
149
152
|
}
|
|
150
153
|
else {
|
|
151
154
|
outFile = (0, fs_1.readFileSync)(tmpfile);
|
|
@@ -3,7 +3,7 @@ import { parseInfo, searchMusic } from 'gerdur-core';
|
|
|
3
3
|
import type { AdvancedSearchOptions } from './search';
|
|
4
4
|
import type { Quality, DownloadTrackOptions, DownloadResult } from './api-download';
|
|
5
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
|
+
import type { TrackPreview, StreamTrackOptions, TrackStream } from 'gerdur-core';
|
|
7
7
|
/** Search result categories accepted by {@link Session.search}. */
|
|
8
8
|
export type SearchType = 'ALBUM' | 'ARTIST' | 'TRACK' | 'PLAYLIST' | 'RADIO' | 'SHOW' | 'USER' | 'LIVESTREAM' | 'CHANNEL';
|
|
9
9
|
export interface SessionOptions {
|
|
@@ -92,6 +92,13 @@ export declare class Session {
|
|
|
92
92
|
downloadPreview(track: trackType | string | number): Promise<Buffer | null>;
|
|
93
93
|
/** Return a fully tagged audio Buffer for a track (nothing written to disk). */
|
|
94
94
|
getTrackBuffer(track: trackType, quality?: Quality, options?: {}): Promise<Buffer | null>;
|
|
95
|
+
/**
|
|
96
|
+
* Download a track as a **stream** of decrypted audio — constant memory,
|
|
97
|
+
* regardless of file size or concurrency. `{stream, size, startedAt,
|
|
98
|
+
* isEncrypted}`; pipe `stream` to a file or your own tag muxer.
|
|
99
|
+
* `options.onProgress(received, total)`, `options.resumeFrom` (bytes).
|
|
100
|
+
*/
|
|
101
|
+
streamTrack(track: trackType, quality?: 1 | 3 | 9, options?: StreamTrackOptions): Promise<TrackStream>;
|
|
95
102
|
/** Download a single track to disk. */
|
|
96
103
|
downloadTrack(track: trackType, quality?: Quality, options?: DownloadTrackOptions): Promise<DownloadResult | null>;
|
|
97
104
|
/**
|
package/dist/src/lib/session.js
CHANGED
|
@@ -128,6 +128,15 @@ class Session {
|
|
|
128
128
|
getTrackBuffer(track, quality = '320', options = {}) {
|
|
129
129
|
return (0, api_download_1.getTrackBuffer)(track, quality, options);
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Download a track as a **stream** of decrypted audio — constant memory,
|
|
133
|
+
* regardless of file size or concurrency. `{stream, size, startedAt,
|
|
134
|
+
* isEncrypted}`; pipe `stream` to a file or your own tag muxer.
|
|
135
|
+
* `options.onProgress(received, total)`, `options.resumeFrom` (bytes).
|
|
136
|
+
*/
|
|
137
|
+
streamTrack(track, quality = 3, options = {}) {
|
|
138
|
+
return (0, gerdur_core_1.streamTrackDownload)(track, quality, options);
|
|
139
|
+
}
|
|
131
140
|
/** Download a single track to disk. */
|
|
132
141
|
downloadTrack(track, quality = '320', options = {}) {
|
|
133
142
|
return (0, api_download_1.downloadTrackToFile)(track, quality, options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gerdur",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.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.
|
|
51
|
+
"gerdur-core": "^2.5.0",
|
|
52
52
|
"dot-prop": "^6.0.1",
|
|
53
53
|
"got": "^11.8.6",
|
|
54
54
|
"gradient-string": "^2.0.2",
|