@@ -22,6 +22,7 @@ const sleep = ms => new Promise(res => setTimeout(res, ms));
22
22
23
23
export class DeezerCore {
24
24
legacyApiUrl = 'https://api.deezer.com' ;
25
+ altApiUrl = 'https://www.deezer.com/ajax/gw-light.php' ;
25
26
26
27
requestObject = got . extend ( {
27
28
responseType : 'json' ,
@@ -32,8 +33,13 @@ export class DeezerCore {
32
33
33
34
#retrySymbol = Symbol ( 'DeezerCoreTrialCount' ) ;
34
35
35
- #getIfHasError = response =>
36
- response . body && typeof response . body === 'object' && 'error' in response . body && response . body . error ;
36
+ #getIfHasError = response => {
37
+ if ( ! ( response . body && typeof response . body === 'object' && 'error' in response . body ) ) return null ;
38
+
39
+ if ( Array . isArray ( response . body . error ) ) return response . body . error . length > 0 ? response . body . error [ 0 ] : null ;
40
+
41
+ return response . body . error ;
42
+ } ;
37
43
38
44
validatorQueue = new AsyncQueue ( 'validatorQueue' , 1 , async now => {
39
45
if ( this . #validatorData. queries . length === 50 )
@@ -63,8 +69,8 @@ export class DeezerCore {
63
69
64
70
totalTrials = 5 ;
65
71
66
- async legacyApiCall ( ref , opts ) {
67
- const response = await this . #sendRequest ( ref , opts , this . totalTrials || 5 ) . catch ( err => {
72
+ async wrappedCall ( called ) {
73
+ const response = await called . catch ( err => {
68
74
throw new WebapiError (
69
75
`${ err . syscall ? `${ err . syscall } ` : '' } ${ err . code } ${ err . hostname || err . host } ` ,
70
76
err . response ? err . response . statusCode : null ,
@@ -81,8 +87,31 @@ export class DeezerCore {
81
87
return response . body ;
82
88
}
83
89
90
+ #altAuth = { token : null , sessionId : null } ;
91
+
92
+ async altApiCall ( method , opts ) {
93
+ if ( ! this . #altAuth. token ) {
94
+ let result = await this . _altApiCall ( 'deezer.getUserData' ) ;
95
+ this . #altAuth = { token : result . checkForm , sessionId : result . SESSION_ID } ;
96
+ }
97
+
98
+ return this . _altApiCall ( method , opts ) ;
99
+ }
100
+
101
+ async _altApiCall ( method , opts ) {
102
+ const response = await this . wrappedCall (
103
+ this . requestObject . post ( this . altApiUrl , {
104
+ headers : { ...( this . #altAuth?. sessionId && { cookie : `sid=${ this . #altAuth. sessionId } ` } ) } ,
105
+ searchParams : { method, api_version : '1.0' , api_token : this . #altAuth. token ?? '' } ,
106
+ json : { lang : 'en' , ...opts } ,
107
+ } ) ,
108
+ ) ;
109
+
110
+ return response . results ;
111
+ }
112
+
84
113
processID ( gnFn ) {
85
- return ( id , opts ) => this . legacyApiCall ( gnFn ( id ) , opts ) ;
114
+ return ( id , opts ) => this . wrappedCall ( this . #sendRequest ( gnFn ( id ) , opts , this . totalTrials || 5 ) ) ;
86
115
}
87
116
88
117
processList ( gnFn ) {
@@ -207,6 +236,7 @@ export default class Deezer {
207
236
total_tracks : albumInfo . ntracks ,
208
237
release_date : new Date ( trackInfo . release_date ) ,
209
238
disc_number : trackInfo . disk_number ,
239
+ total_discs : albumInfo . tracks . reduce ( ( acc , track ) => Math . max ( acc , track . altData . DISK_NUMBER ) , 1 ) ,
210
240
contentRating : ! ! trackInfo . explicit_lyrics ,
211
241
isrc : trackInfo . isrc ,
212
242
genres : albumInfo . genres ,
@@ -218,8 +248,9 @@ export default class Deezer {
218
248
} ;
219
249
}
220
250
221
- wrapAlbumData ( albumObject ) {
251
+ wrapAlbumData ( albumObject , altAlbumObject ) {
222
252
const artistObject = albumObject . artist || { } ;
253
+ let altTracks = Object . fromEntries ( ( altAlbumObject . SONGS ?. data || [ ] ) . map ( track => [ track . SNG_ID , track ] ) ) ;
223
254
return {
224
255
id : albumObject . id ,
225
256
uri : albumObject . link ,
@@ -232,12 +263,12 @@ export default class Deezer {
232
263
? 'single'
233
264
: 'album' ,
234
265
genres : ( ( albumObject . genres || { } ) . data || [ ] ) . map ( genre => genre . name ) ,
235
- copyrights : [ { type : 'P' , text : albumObject . copyright } ] , // find workaround
266
+ copyrights : [ { type : 'P' , text : altAlbumObject . DATA . PRODUCER_LINE } ] ,
236
267
images : [ albumObject . cover_small , albumObject . cover_medium , albumObject . cover_big , albumObject . cover_xl ] ,
237
268
label : albumObject . label ,
238
269
release_date : new Date ( albumObject . release_date ) ,
239
270
ntracks : albumObject . nb_tracks ,
240
- tracks : albumObject . tracks ,
271
+ tracks : albumObject . tracks . data . map ( track => ( { ... track , altData : altTracks [ track . id ] } ) ) ,
241
272
getImage ( width , height ) {
242
273
const min = ( val , max ) => Math . min ( max , val ) || max ;
243
274
return this . images
@@ -298,7 +329,13 @@ export default class Deezer {
298
329
albumQueue = new AsyncQueue (
299
330
'deezer:albumQueue' ,
300
331
4 ,
301
- this . createDataProcessor ( async id => this . wrapAlbumData ( await this . #store. core . getAlbum ( id ) ) ) ,
332
+ this . createDataProcessor ( async id => {
333
+ let [ album , altAlbumData ] = await Promise . all ( [
334
+ this . #store. core . getAlbum ( id ) ,
335
+ this . #store. core . altApiCall ( 'deezer.pageAlbum' , { alb_id : id } ) ,
336
+ ] ) ;
337
+ return this . wrapAlbumData ( album , altAlbumData ) ;
338
+ } ) ,
302
339
) ;
303
340
304
341
async getAlbum ( uris ) {
@@ -327,7 +364,7 @@ export default class Deezer {
327
364
328
365
async getAlbumTracks ( uri ) {
329
366
const album = await this . getAlbum ( uri ) ;
330
- return this . trackQueue . push ( album . tracks . data . map ( track => track . link ) ) ;
367
+ return this . trackQueue . push ( album . tracks . map ( track => track . link ) ) ;
331
368
}
332
369
333
370
async getArtistAlbums ( uris ) {
0 commit comments