deezer-js/deezer/gw.js

479 lines
13 KiB
JavaScript
Raw Normal View History

2021-03-12 13:58:48 +00:00
const got = require('got')
const {map_artist_album, map_user_track, map_user_artist, map_user_album, map_user_playlist} = require('./utils.js')
2021-08-02 18:38:50 +00:00
const { GWAPIError } = require('./errors.js')
2021-03-12 13:58:48 +00:00
2021-03-13 12:18:22 +00:00
const PlaylistStatus = {
PUBLIC: 0,
PRIVATE: 1,
COLLABORATIVE: 2,
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
const EMPTY_TRACK_OBJ = {
2021-03-12 13:58:48 +00:00
SNG_ID: 0,
SNG_TITLE: '',
DURATION: 0,
MD5_ORIGIN: 0,
MEDIA_VERSION: 0,
FILESIZE: 0,
ALB_TITLE: "",
ALB_PICTURE: "",
ART_ID: 0,
ART_NAME: ""
}
2021-03-13 12:18:22 +00:00
class GW{
2021-03-12 13:58:48 +00:00
constructor(cookie_jar, headers){
this.http_headers = headers
this.cookie_jar = cookie_jar
2021-12-22 13:04:59 +00:00
this.api_token = null
2021-03-12 13:58:48 +00:00
}
async api_call(method, args, params){
2021-04-09 17:27:12 +00:00
if (typeof args === undefined) args = {}
if (typeof params === undefined) params = {}
2021-12-22 13:04:59 +00:00
if (!this.api_token && method != 'deezer.getUserData') this.api_token = await this._get_token()
2021-03-13 12:18:22 +00:00
let p = {
2021-03-12 13:58:48 +00:00
api_version: "1.0",
2021-12-22 13:04:59 +00:00
api_token: method == 'deezer.getUserData' ? 'null' : this.api_token,
2021-03-12 13:58:48 +00:00
input: '3',
method: method,
2021-03-13 12:18:22 +00:00
...params
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
let result_json
2021-03-12 13:58:48 +00:00
try{
2021-03-13 12:18:22 +00:00
result_json = await got.post("http://www.deezer.com/ajax/gw-light.php", {
2021-03-12 13:58:48 +00:00
searchParams: p,
json: args,
cookieJar: this.cookie_jar,
headers: this.http_headers,
2021-09-30 15:30:28 +00:00
https: {
rejectUnauthorized: false
},
2021-03-12 13:58:48 +00:00
timeout: 30000
}).json()
2021-03-13 12:18:22 +00:00
}catch (e){
2021-11-03 14:17:45 +00:00
console.debug("[ERROR] deezer.gw", method, args, e.name, e.message)
2021-12-07 11:01:08 +00:00
if (["ECONNABORTED", "ECONNREFUSED", "ECONNRESET", "ENETRESET", "ETIMEDOUT"].includes(e.code)){
await new Promise(r => setTimeout(r, 2000)) // sleep(2000ms)
2021-12-22 11:28:09 +00:00
return this.api_call(method, args, params)
2021-12-07 11:01:08 +00:00
}
2021-11-03 14:17:45 +00:00
throw new GWAPIError(`${method} ${args}:: ${e.name}: ${e.message}`)
2021-03-12 13:58:48 +00:00
}
2021-12-22 13:04:59 +00:00
if (result_json.error.length || Object.keys(result_json.error).length) {
if (
JSON.stringify(result_json.error) == '{"GATEWAY_ERROR":"invalid api token"}' ||
JSON.stringify(result_json.error) == '{"VALID_TOKEN_REQUIRED":"Invalid CSRF token"}'
){
this.api_token = await this._get_token()
return this.api_call(method, args, params)
}
2021-12-22 11:28:09 +00:00
if (result_json.payload && result_json.payload.FALLBACK){
Object.keys(result_json.payload.FALLBACK).forEach(key => {
args[key] = result_json.payload.FALLBACK[key]
})
return this.api_call(method, args, params)
}
throw new GWAPIError(JSON.stringify(result_json.error))
2021-12-22 11:28:09 +00:00
}
2021-12-23 10:55:08 +00:00
if (!this.api_token && method == 'deezer.getUserData') this.api_token = result_json.results.checkForm
2021-03-12 13:58:48 +00:00
return result_json.results
}
async _get_token(){
let token_data = await this.get_user_data()
return token_data.checkForm
}
2021-03-13 12:18:22 +00:00
get_user_data(){
return this.api_call('deezer.getUserData')
2021-03-12 13:58:48 +00:00
}
2021-04-22 08:30:52 +00:00
get_user_profile_page(user_id, tab, options={}){
2021-04-21 18:01:06 +00:00
const limit = options.limit || 10
2021-12-22 11:28:09 +00:00
return this.api_call('deezer.pageProfile', {USER_ID: user_id, tab, nb: limit})
2021-03-12 13:58:48 +00:00
}
get_user_favorite_ids(checksum = null, options={}){
const limit = options.limit || 10000
const start = options.start || 0
return this.api_call('song.getFavoriteIds', {nb: limit, start, checksum})
}
2021-03-13 12:18:22 +00:00
get_child_accounts(){
return this.api_call('deezer.getChildAccounts')
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
get_track(sng_id){
2021-12-22 11:28:09 +00:00
return this.api_call('song.getData', {SNG_ID: sng_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
get_track_page(sng_id){
2021-12-22 11:28:09 +00:00
return this.api_call('deezer.pageTrack', {SNG_ID: sng_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
get_track_lyrics(sng_id){
2021-12-22 11:28:09 +00:00
return this.api_call('song.getLyrics', {SNG_ID: sng_id})
2021-03-12 13:58:48 +00:00
}
async get_tracks(sng_ids){
2021-03-12 13:58:48 +00:00
let tracks_array = []
2021-12-23 22:21:37 +00:00
let body = await this.api_call('song.getListData', {SNG_IDS: sng_ids})
2021-03-12 13:58:48 +00:00
let errors = 0
for (let i = 0; i < sng_ids.length; i++){
if (sng_ids[0] != 0){
tracks_array.push(body.data[i - errors])
} else {
errors++
tracks_array.push(EMPTY_TRACK_OBJ)
}
}
return tracks_array
}
2021-03-13 12:18:22 +00:00
get_album(alb_id){
2021-12-22 11:28:09 +00:00
return this.api_call('album.getData', {ALB_ID: alb_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
get_album_page(alb_id){
return this.api_call('deezer.pageAlbum', {
2021-12-22 11:28:09 +00:00
ALB_ID: alb_id,
2021-03-12 13:58:48 +00:00
lang: 'en',
2021-04-09 17:27:12 +00:00
header: true,
2021-03-12 13:58:48 +00:00
tab: 0
})
}
async get_album_tracks(alb_id){
let tracks_array = []
2021-12-22 11:28:09 +00:00
let body = await this.api_call('song.getListByAlbum', {ALB_ID: alb_id, nb: -1})
2021-03-12 13:58:48 +00:00
body.data.forEach(track => {
let _track = track
_track.POSITION = body.data.indexOf(track)
tracks_array.push(_track)
})
return tracks_array
}
2021-03-13 12:18:22 +00:00
get_artist(art_id){
2021-12-22 11:28:09 +00:00
return this.api_call('artist.getData', {ART_ID: art_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
get_artist_page(art_id){
2021-03-12 13:58:48 +00:00
return this.api_call('deezer.pageArtist', {
2021-12-22 11:28:09 +00:00
ART_ID: art_id,
2021-03-12 13:58:48 +00:00
lang: 'en',
2021-04-09 17:27:12 +00:00
header: true,
2021-03-12 13:58:48 +00:00
tab: 0
})
}
2021-04-22 08:30:52 +00:00
async get_artist_top_tracks(art_id, options={}){
2021-04-21 18:01:06 +00:00
const limit = options.limit || 100
2021-03-12 13:58:48 +00:00
let tracks_array = []
2021-12-22 11:28:09 +00:00
let body = await this.api_call('artist.getTopTrack', {ART_ID: art_id, nb: limit})
2021-03-12 13:58:48 +00:00
body.data.forEach(track => {
track.POSITION = body.data.indexOf(track)
2021-04-09 17:27:12 +00:00
tracks_array.push(track)
2021-03-12 13:58:48 +00:00
})
return tracks_array
}
2021-04-22 08:30:52 +00:00
get_artist_discography(art_id, options={}){
2021-04-21 18:01:06 +00:00
const index = options.index || 0
const limit = options.limit || 25
2021-03-13 12:18:22 +00:00
return this.api_call('album.getDiscography', {
2021-12-22 11:28:09 +00:00
ART_ID: art_id,
2021-03-12 13:58:48 +00:00
discography_mode:"all",
nb: limit,
nb_songs: 0,
start: index
})
}
2021-03-13 12:18:22 +00:00
get_playlist(playlist_id){
return this.get_playlist_page(playlist_id)
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
get_playlist_page(playlist_id){
return this.api_call('deezer.pagePlaylist', {
2021-12-22 11:28:09 +00:00
PLAYLIST_ID: playlist_id,
2021-03-12 13:58:48 +00:00
lang: 'en',
2021-04-09 17:27:12 +00:00
header: true,
2021-03-12 13:58:48 +00:00
tab: 0
})
}
async get_playlist_tracks(playlist_id){
let tracks_array = []
2021-12-22 11:28:09 +00:00
let body = await this.api_call('playlist.getSongs', {PLAYLIST_ID: playlist_id, nb: -1})
2021-03-12 13:58:48 +00:00
body.data.forEach(track => {
track.POSITION = body.data.indexOf(track)
2021-04-09 17:27:12 +00:00
tracks_array.push(track)
2021-03-12 13:58:48 +00:00
})
return tracks_array
}
2021-03-13 12:18:22 +00:00
create_playlist(title, status=PlaylistStatus.PUBLIC, description, songs=[]){
2021-04-09 17:27:12 +00:00
let newSongs = []
2021-03-12 13:58:48 +00:00
songs.forEach(song => {
newSongs.push([song, 0])
});
2021-03-13 12:18:22 +00:00
return this.api_call('playlist.create', {
2021-03-12 13:58:48 +00:00
title,
status,
description,
songs: newSongs
})
}
2021-03-13 12:18:22 +00:00
edit_playlist(playlist_id, title, status, description, songs=[]){
2021-04-09 17:27:12 +00:00
let newSongs = []
2021-03-12 13:58:48 +00:00
songs.forEach(song => {
newSongs.push([song, 0])
});
2021-03-13 12:18:22 +00:00
return this.api_call('playlist.update', {
2021-12-22 11:28:09 +00:00
PLAYLIST_ID: playlist_id,
2021-03-12 13:58:48 +00:00
title,
status,
description,
songs: newSongs
})
}
2021-03-13 12:18:22 +00:00
add_songs_to_playlist(playlist_id, songs, offset=-1){
2021-04-09 17:27:12 +00:00
let newSongs = []
2021-03-12 13:58:48 +00:00
songs.forEach(song => {
newSongs.push([song, 0])
});
2021-03-13 12:18:22 +00:00
return this.api_call('playlist.addSongs', {
2021-12-22 11:28:09 +00:00
PLAYLIST_ID: playlist_id,
2021-03-12 13:58:48 +00:00
songs: newSongs,
offset
})
}
2021-03-13 12:18:22 +00:00
add_song_to_playlist(playlist_id, sng_id, offset=-1){
return this.add_songs_to_playlist(playlist_id, [sng_id], offset)
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
remove_songs_from_playlist(playlist_id, songs){
2021-04-09 17:27:12 +00:00
let newSongs = []
2021-03-12 13:58:48 +00:00
songs.forEach(song => {
newSongs.push([song, 0])
});
2021-03-13 12:18:22 +00:00
return this.api_call('playlist.deleteSongs', {
2021-12-22 11:28:09 +00:00
PLAYLIST_ID: playlist_id,
2021-03-12 13:58:48 +00:00
songs: newSongs
})
}
2021-03-13 12:18:22 +00:00
remove_song_from_playlist(playlist_id, sng_id){
return this.remove_songs_from_playlist(playlist_id, [sng_id])
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
delete_playlist(playlist_id){
2021-12-22 11:28:09 +00:00
return this.api_call('playlist.delete', {PLAYLIST_ID: playlist_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
add_song_to_favorites(sng_id){
2021-12-22 11:28:09 +00:00
return this.gw_api_call('favorite_song.add', {SNG_ID: sng_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
remove_song_from_favorites(sng_id){
2021-12-22 11:28:09 +00:00
return this.gw_api_call('favorite_song.remove', {SNG_ID: sng_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
add_album_to_favorites(alb_id){
2021-12-22 11:28:09 +00:00
return this.gw_api_call('album.addFavorite', {ALB_ID: alb_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
remove_album_from_favorites(alb_id){
2021-12-22 11:28:09 +00:00
return this.gw_api_call('album.deleteFavorite', {ALB_ID: alb_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
add_artist_to_favorites(art_id){
2021-12-22 11:28:09 +00:00
return this.gw_api_call('artist.addFavorite', {ART_ID: art_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
remove_artist_from_favorites(art_id){
2021-12-22 11:28:09 +00:00
return this.gw_api_call('artist.deleteFavorite', {ART_ID: art_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
add_playlist_to_favorites(playlist_id){
return this.gw_api_call('playlist.addFavorite', {PARENT_PLAYLIST_ID: playlist_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
remove_playlist_from_favorites(playlist_id){
return this.gw_api_call('playlist.deleteFavorite', {PLAYLIST_ID: playlist_id})
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
get_page(page){
2021-03-12 13:58:48 +00:00
let params = {
gateway_input: JSON.stringify({
PAGE: page,
VERSION: '2.3',
SUPPORT: {
grid: [
'channel',
'album'
],
2021-03-13 12:18:22 +00:00
'horizontal-grid': [
2021-03-12 13:58:48 +00:00
'album'
],
},
LANG: 'en'
})
}
2021-04-09 17:27:12 +00:00
return this.api_call('page.get', {}, params)
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
search(query, index=0, limit=10, suggest=true, artist_suggest=true, top_tracks=true){
return this.api_call('deezer.pageSearch', {
2021-03-12 13:58:48 +00:00
query,
start: index,
nb: limit,
suggest,
artist_suggest,
top_tracks
})
}
2021-04-22 08:30:52 +00:00
search_music(query, type, options={}){
2021-04-21 18:01:06 +00:00
const index = options.index || 0
const limit = options.limit || 10
2021-03-13 12:18:22 +00:00
return this.api_call('search.music', {
2021-03-12 13:58:48 +00:00
query,
filter: "ALL",
output: type,
start: index,
nb: limit
})
}
// Extra calls
2021-04-22 08:30:52 +00:00
async get_artist_discography_tabs(art_id, options={}){
2021-04-21 18:01:06 +00:00
const limit = options.limit || 100
2021-03-12 13:58:48 +00:00
let index = 0
let releases = []
let result = {all: []}
let ids = []
// Get all releases
2021-04-09 17:27:12 +00:00
let response
2021-03-12 13:58:48 +00:00
do {
2021-04-30 12:03:57 +00:00
response = await this.get_artist_discography(art_id, {index, limit})
2021-04-30 12:13:18 +00:00
releases = releases.concat(response.data)
2021-03-12 13:58:48 +00:00
index += limit
} while (index < response.total)
releases.forEach(release => {
if (ids.indexOf(release.ALB_ID) == -1){
ids.push(release.ALB_ID)
2021-04-09 17:27:12 +00:00
let obj = map_artist_album(release)
2021-03-12 13:58:48 +00:00
if ((release.ART_ID == art_id || release.ART_ID != art_id && release.ROLE_ID == 0) && release.ARTISTS_ALBUMS_IS_OFFICIAL){
// Handle all base record types
if (!result[obj.record_type]) result[obj.record_type] = []
result[obj.record_type].push(obj)
result.all.push(obj)
2021-04-09 17:27:12 +00:00
} else {
if (release.ROLE_ID == 5) { // Handle albums where the artist is featured
if (!result.featured) result.featured = []
result.featured.push(obj)
} else if (release.ROLE_ID == 0) { // Handle "more" albums
if (!result.more) result.more = []
result.more.push(obj)
result.all.push(obj)
}
2021-03-12 13:58:48 +00:00
}
}
})
return result
}
async get_track_with_fallback(sng_id){
let body
2021-04-08 15:25:24 +00:00
if (parseInt(sng_id) > 0){
2021-03-12 13:58:48 +00:00
try{ body = await this.get_track_page(sng_id) }
2021-04-09 17:27:12 +00:00
catch (e) { /*nothing*/ }
2021-03-12 13:58:48 +00:00
}
if (body){
if (body.LYRICS) body.DATA.LYRICS = body.LYRICS
2021-12-22 11:28:09 +00:00
if (body.ISRC) body.DATA.ALBUM_FALLBACK = body.ISRC
2021-03-12 13:58:48 +00:00
body = body.DATA
} else {
body = await this.get_track(sng_id)
}
return body
}
2021-04-22 08:30:52 +00:00
async get_user_playlists(user_id, options={}){
2021-04-21 18:01:06 +00:00
const limit = options.limit || 25
2021-04-30 12:03:57 +00:00
let user_profile_page = await this.get_user_profile_page(user_id, 'playlists', {limit})
2021-03-12 13:58:48 +00:00
let blog_name = user_profile_page.DATA.USER.BLOG_NAME || "Unknown"
let data = user_profile_page.TAB.playlists.data
let result = []
data.forEach(playlist => {
result.push(map_user_playlist(playlist, blog_name))
})
return result
}
2021-04-22 08:30:52 +00:00
async get_user_albums(user_id, options={}){
2021-04-21 18:01:06 +00:00
const limit = options.limit || 25
2021-04-30 12:03:57 +00:00
let data = await this.get_user_profile_page(user_id, 'albums', {limit})
data = data.TAB.albums.data
2021-03-12 13:58:48 +00:00
let result = []
data.forEach(album => {
result.push(map_user_album(album))
})
return result
}
2021-04-22 08:30:52 +00:00
async get_user_artists(user_id, options={}){
2021-04-21 18:01:06 +00:00
const limit = options.limit || 25
let data = await this.get_user_profile_page(user_id, 'artists', {limit})
2021-04-30 12:03:57 +00:00
data = data.TAB.artists.data
2021-03-12 13:58:48 +00:00
let result = []
data.forEach(artist => {
result.push(map_user_artist(artist))
})
return result
}
2021-04-22 08:30:52 +00:00
async get_user_tracks(user_id, options={}){
let user_data = await this.get_user_data()
if (user_data.USER.USER_ID == user_id) return this.get_my_favorite_tracks(options)
2021-04-21 18:01:06 +00:00
const limit = options.limit || 25
2021-04-30 12:03:57 +00:00
let data = this.get_user_profile_page(user_id, 'loved', {limit})
data = data.TAB.loved.data
2021-03-12 13:58:48 +00:00
let result = []
data.forEach(track => {
result.push(map_user_track(track))
})
return result
}
async get_my_favorite_tracks(options={}){
const limit = options.limit || 25
const ids_raw = await this.get_user_favorite_ids(null, {limit})
const ids = ids_raw.data.map(x => x.SNG_ID)
if (!ids.length) return []
let data = await this.get_tracks(ids)
let result = []
data.forEach((track, i) => {
track = {...track, ...ids_raw.data[i]}
result.push(map_user_track(track))
})
return result
}
2021-03-12 13:58:48 +00:00
}
2021-03-13 12:18:22 +00:00
module.exports = {
PlaylistStatus,
EMPTY_TRACK_OBJ,
2021-08-02 18:38:50 +00:00
GW
2021-03-13 12:18:22 +00:00
}