gerdur 2.16.0 → 2.17.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 +38 -0
- package/README.md +15 -6
- package/dist/package.json +1 -1
- package/dist/src/lib/arl-setup.d.ts +7 -2
- package/dist/src/lib/arl-setup.js +15 -4
- package/dist/src/lib/email-login.d.ts +9 -2
- package/dist/src/lib/email-login.js +24 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.17.0 - 2026-08-31
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`DEEZER_EMAIL` / `DEEZER_PASSWORD` are now read** as aliases for
|
|
8
|
+
`GERDUR_EMAIL` / `GERDUR_PASSWORD`. Those spellings are what people tend to put
|
|
9
|
+
in a `.env` alongside their other Deezer settings, and until now they were
|
|
10
|
+
silently ignored — the login just looked unconfigured.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **A refused login no longer blames your password.** It used to report
|
|
15
|
+
`wrong-credentials` with "check your email and password". That is not something
|
|
16
|
+
this code can know: Deezer's `user_auth.php` returns an identical error
|
|
17
|
+
(code 160) for a real account, a wrong password, **and** an address that does
|
|
18
|
+
not exist. The reason is now `rejected`, and the message says so.
|
|
19
|
+
|
|
20
|
+
Probed against the live endpoints while making this change, and every
|
|
21
|
+
documented password-to-`arl` path is currently refused:
|
|
22
|
+
|
|
23
|
+
| path | result |
|
|
24
|
+
| :--- | :--- |
|
|
25
|
+
| `connect.deezer.com/oauth/user_auth.php` | `authenticate user failed`, code 160 |
|
|
26
|
+
| `www.deezer.com/ajax/action.php` | `error` |
|
|
27
|
+
| `auth.deezer.com/login/arl` | 400 — needs a session the first two can't provide |
|
|
28
|
+
|
|
29
|
+
A deliberately malformed request returns a *different* error (code 150, "wrong
|
|
30
|
+
hash !"), which is how we know the request itself is still well formed and the
|
|
31
|
+
refusal is about authentication. **Paste an `arl` instead.** The code is kept
|
|
32
|
+
in case the flow returns, and `reason: 'rejected'` now carries an explanation
|
|
33
|
+
rather than a misdiagnosis.
|
|
34
|
+
|
|
35
|
+
### Breaking (programmatic API, minor)
|
|
36
|
+
|
|
37
|
+
- `LoginResult`'s `wrong-credentials` reason is renamed **`rejected`**. Anything
|
|
38
|
+
switching exhaustively on `reason` needs updating; `LoginError.reason` carries
|
|
39
|
+
the new value.
|
|
40
|
+
|
|
3
41
|
## 2.16.0 - 2026-08-31
|
|
4
42
|
|
|
5
43
|
### Changed
|
package/README.md
CHANGED
|
@@ -89,11 +89,20 @@ gerdur --set-arl <your_arl>
|
|
|
89
89
|
To copy it manually: open <https://www.deezer.com> logged in → DevTools (`F12`) →
|
|
90
90
|
Application → Cookies → `deezer.com` → copy the `arl` value (192 hex chars).
|
|
91
91
|
|
|
92
|
-
**Log in with email & password** — the guided setup can fetch the `arl`
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
**Log in with email & password** — the guided setup can try to fetch the `arl`
|
|
93
|
+
from your credentials, so you never open DevTools. Only the resulting `arl` is
|
|
94
|
+
stored; your password is never written to disk unless you explicitly opt in when
|
|
95
|
+
prompted. Credentials are also read from `GERDUR_EMAIL` / `GERDUR_PASSWORD`, or
|
|
96
|
+
`DEEZER_EMAIL` / `DEEZER_PASSWORD`.
|
|
97
|
+
|
|
98
|
+
> **This currently does not work.** Every documented password-to-`arl` path is
|
|
99
|
+
> refused: `connect.deezer.com/oauth/user_auth.php` answers `authenticate user
|
|
100
|
+
> failed` (code 160), the web `action.php` login answers `error`, and
|
|
101
|
+
> `auth.deezer.com/login/arl` needs a session you cannot get without the first
|
|
102
|
+
> two. Deezer returns *the same* code 160 for a real account, a wrong password
|
|
103
|
+
> and an address that does not exist, so a failure here says nothing about your
|
|
104
|
+
> credentials. **Paste an `arl` instead** — `gerdur --set-arl <arl>` or
|
|
105
|
+
> `GERDUR_ARL=<arl>`. The code is kept because the flow may come back.
|
|
97
106
|
|
|
98
107
|
**Zero-config / CI** — set `GERDUR_ARL` (or `GERDUR_EMAIL` + `GERDUR_PASSWORD`)
|
|
99
108
|
and skip the config file entirely. `GERDUR_ARL` takes precedence over the config
|
|
@@ -434,7 +443,7 @@ import {loginWithEmail, LoginError, Config, globalConfigPath} from 'gerdur';
|
|
|
434
443
|
|
|
435
444
|
const result = await loginWithEmail('you@example.com', 'password');
|
|
436
445
|
if (result.ok) console.log(result.arl);
|
|
437
|
-
else console.error(result.reason, result.message); // '
|
|
446
|
+
else console.error(result.reason, result.message); // 'rejected' | 'no-arl' | 'network' | 'unknown'
|
|
438
447
|
|
|
439
448
|
const conf = new Config(); // the same config the CLI uses
|
|
440
449
|
conf.set('cookies.arl', 'xxx…');
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gerdur",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.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",
|
|
@@ -17,8 +17,13 @@ export declare const printArlInstructions: () => void;
|
|
|
17
17
|
export declare const promptForArl: (conf: Config) => Promise<string | null>;
|
|
18
18
|
/**
|
|
19
19
|
* Resolve email/password credentials from (in order): explicit config, then the
|
|
20
|
-
* `GERDUR_EMAIL` / `GERDUR_PASSWORD` environment variables
|
|
21
|
-
* login attempts. Returns `null` if
|
|
20
|
+
* `GERDUR_EMAIL` / `GERDUR_PASSWORD` environment variables, then `DEEZER_EMAIL` /
|
|
21
|
+
* `DEEZER_PASSWORD`. Used for non-interactive login attempts. Returns `null` if
|
|
22
|
+
* either half is missing.
|
|
23
|
+
*
|
|
24
|
+
* The `DEEZER_*` spellings are accepted because that is what people tend to put
|
|
25
|
+
* in a `.env` next to their other Deezer settings; before, those were silently
|
|
26
|
+
* ignored and the login simply looked unconfigured.
|
|
22
27
|
*/
|
|
23
28
|
export declare const resolveCredentials: (conf: Config) => {
|
|
24
29
|
email: string;
|
|
@@ -68,12 +68,23 @@ const promptForArl = async (conf) => {
|
|
|
68
68
|
exports.promptForArl = promptForArl;
|
|
69
69
|
/**
|
|
70
70
|
* Resolve email/password credentials from (in order): explicit config, then the
|
|
71
|
-
* `GERDUR_EMAIL` / `GERDUR_PASSWORD` environment variables
|
|
72
|
-
* login attempts. Returns `null` if
|
|
71
|
+
* `GERDUR_EMAIL` / `GERDUR_PASSWORD` environment variables, then `DEEZER_EMAIL` /
|
|
72
|
+
* `DEEZER_PASSWORD`. Used for non-interactive login attempts. Returns `null` if
|
|
73
|
+
* either half is missing.
|
|
74
|
+
*
|
|
75
|
+
* The `DEEZER_*` spellings are accepted because that is what people tend to put
|
|
76
|
+
* in a `.env` next to their other Deezer settings; before, those were silently
|
|
77
|
+
* ignored and the login simply looked unconfigured.
|
|
73
78
|
*/
|
|
74
79
|
const resolveCredentials = (conf) => {
|
|
75
|
-
const email = (conf.get('cookies.email') ||
|
|
76
|
-
|
|
80
|
+
const email = (conf.get('cookies.email') ||
|
|
81
|
+
process.env.GERDUR_EMAIL ||
|
|
82
|
+
process.env.DEEZER_EMAIL ||
|
|
83
|
+
'').trim();
|
|
84
|
+
const password = (conf.get('cookies.password') ||
|
|
85
|
+
process.env.GERDUR_PASSWORD ||
|
|
86
|
+
process.env.DEEZER_PASSWORD ||
|
|
87
|
+
'').trim();
|
|
77
88
|
if (email && password) {
|
|
78
89
|
return { email, password };
|
|
79
90
|
}
|
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
export type LoginResult = {
|
|
3
3
|
ok: true;
|
|
4
4
|
arl: string;
|
|
5
|
-
}
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* `rejected` means Deezer refused the login. It deliberately does **not** say
|
|
8
|
+
* "wrong password": the endpoint returns an identical error (code 160) for a
|
|
9
|
+
* real account, a wrong password and an address that does not exist, so the
|
|
10
|
+
* cause genuinely cannot be told apart from here.
|
|
11
|
+
*/
|
|
12
|
+
| {
|
|
6
13
|
ok: false;
|
|
7
|
-
reason: '
|
|
14
|
+
reason: 'rejected' | 'no-arl' | 'network' | 'unknown';
|
|
8
15
|
message: string;
|
|
9
16
|
};
|
|
10
17
|
/**
|
|
@@ -23,6 +23,13 @@ const got = () => require('got');
|
|
|
23
23
|
const CLIENT_ID = '447462';
|
|
24
24
|
const CLIENT_SECRET = 'a83bf7f38ad2f137e444727cfc3775cf';
|
|
25
25
|
const UA = 'Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0';
|
|
26
|
+
/**
|
|
27
|
+
* Deezer's `user_auth.php` error codes, as far as they can be told apart.
|
|
28
|
+
* `150` proves the request itself was well formed, which is how we know a `160`
|
|
29
|
+
* is about the account rather than the app hash.
|
|
30
|
+
*/
|
|
31
|
+
const AUTH_WRONG_HASH = 150;
|
|
32
|
+
const AUTH_FAILED = 160;
|
|
26
33
|
const md5 = (data) => (0, crypto_1.createHash)('md5').update(Buffer.from(data, 'utf8')).digest('hex');
|
|
27
34
|
/**
|
|
28
35
|
* Merge `set-cookie` response headers into a cookie map (last value wins),
|
|
@@ -80,7 +87,23 @@ const loginWithEmail = async (email, password) => {
|
|
|
80
87
|
accessToken = authBody.access_token;
|
|
81
88
|
}
|
|
82
89
|
if (!accessToken && !jar.has('arl') && (authBody.error || auth.statusCode >= 400)) {
|
|
83
|
-
|
|
90
|
+
const code = authBody?.error?.code;
|
|
91
|
+
if (code === AUTH_WRONG_HASH) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
reason: 'unknown',
|
|
95
|
+
message: 'Deezer rejected the request signature — the login flow has changed. Please paste an arl instead.',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
reason: 'rejected',
|
|
101
|
+
message: code === AUTH_FAILED
|
|
102
|
+
? 'Deezer refused the login. It returns this same response for a wrong password and for an address ' +
|
|
103
|
+
'that does not exist, and it currently refuses valid credentials too, so this is most likely the ' +
|
|
104
|
+
'flow being closed rather than anything wrong with your details. Paste an arl instead.'
|
|
105
|
+
: 'Deezer refused the login. Paste an arl instead.',
|
|
106
|
+
};
|
|
84
107
|
}
|
|
85
108
|
// 3. Read the arl for the now-authenticated session.
|
|
86
109
|
const arlResp = await got()('https://www.deezer.com/ajax/gw-light.php', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gerdur",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.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",
|