First commit

This commit is contained in:
RemixDev 2021-03-12 14:58:48 +01:00
commit f2ac5665e6
7 changed files with 1785 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules

423
deezer/api.js Normal file
View file

@ -0,0 +1,423 @@
const got = require('got')
// Possible values for order parameter in search
export const SearchOrder = {
RANKING : "RANKING"
TRACK_ASC : "TRACK_ASC"
TRACK_DESC : "TRACK_DESC"
ARTIST_ASC : "ARTIST_ASC"
ARTIST_DESC : "ARTIST_DESC"
ALBUM_ASC : "ALBUM_ASC"
ALBUM_DESC : "ALBUM_DESC"
RATING_ASC : "RATING_ASC"
RATING_DESC : "RATING_DESC"
DURATION_ASC : "DURATION_ASC"
DURATION_DESC : "DURATION_DESC"
}
export class API{
constructor(cookie_jar, headers){
this.http_headers = headers
this.cookie_jar = cookie_jar
this.access_token = null
}
async api_call(method, args){
if (typeof args == "undefined") args = {}
if this.access_token: args.access_token = this.access_token
try {
const result_json = await got.get("https://api.deezer.com/" + method, {
searchParams: args,
cookieJar: this.cookie_jar,
headers: this.http_headers,
timeout: 30000
}).json()
} catch (error) {
await new Promise(r => setTimeout(r, 2000)) // sleep(2000ms)
return await this.api_call(method, args)
}
if (result_json.error){
if (result_json.error.code){
if ([4, 700].indexOf(result_json.error.code) != -1) {
await new Promise(r => setTimeout(r, 5000)) // sleep(5000ms)
return await this.api_call(method, args)
}
if (result_json.error.code == 100) throw new ItemsLimitExceededException(`ItemsLimitExceededException: ${method} ${result_json.error.message || ""}`)
if (result_json.error.code == 200) throw new PermissionException(`PermissionException: ${method} ${result_json.error.message || ""}`)
if (result_json.error.code == 300) throw new InvalidTokenException(`InvalidTokenException: ${method} ${result_json.error.message || ""}`)
if (result_json.error.code == 500) throw new WrongParameterException(`ParameterException: ${method} ${result_json.error.message || ""}`)
if (result_json.error.code == 501) throw new MissingParameterException(`MissingParameterException: ${method} ${result_json.error.message || ""}`)
if (result_json.error.code == 600) throw new InvalidQueryException(`InvalidQueryException: ${method} ${result_json.error.message || ""}`)
if (result_json.error.code == 800) throw new DataException(`DataException: ${method} ${result_json.error.message || ""}`)
if (result_json.error.code == 901) throw new IndividualAccountChangedNotAllowedException(`IndividualAccountChangedNotAllowedException: ${method} ${result_json.error.message || ""}`)
}
throw APIError(result_json.error))
}
return result_json
}
async get_album(album_id){
return await this.api_call(`album/${album_id}`)
}
async get_album_by_UPC(upc){
return await this.get_album(`upc:${upc}`)
}
async get_album_comments(album_id, index=0, limit=10){
return await this.api_call(`album/${album_id}/comments`, {index, limit})
}
async get_album_fans(album_id, index=0, limit=100){
return await this.api_call(`album/${album_id}/fans`, {index, limit})
}
async get_album_tracks(album_id, index=0, limit=-1){
return await this.api_call(`album/${album_id}/tracks`, {index, limit})
}
async get_artist(artist_id){
return await this.api_call(`artist/${artist_id}`)
}
async get_artist_top(artist_id, index=0, limit=10){
return await this.api_call(`artist/${artist_id}/top`, {index, limit})
}
async get_artist_albums(artist_id, index=0, limit=-1){
return await this.api_call(`artist/${artist_id}/albums`, {index, limit})
}
async get_artist_comments(artist_id, index=0, limit=10){
return await this.api_call(`artist/${artist_id}/comments`, {index, limit})
}
async get_artist_fans(artist_id, index=0, limit=100){
return await this.api_call(`artist/${artist_id}/fans`, {index, limit})
}
async get_artist_related(artist_id, index=0, limit=20){
return await this.api_call(`artist/${artist_id}/related`, {index, limit})
}
async get_artist_radio(artist_id, index=0, limit=25){
return await this.api_call(`artist/${artist_id}/radio`, {index, limit})
}
async get_artist_playlists(artist_id, index=0, limit=-1){
return await this.api_call(`artist/${artist_id}/playlists`, {index, limit})
}
async get_chart(genre_id=0, index=0, limit=10){
return await this.api_call(`chart/${genre_id}`, {index, limit})
}
async get_chart_tracks(genre_id=0, index=0, limit=10){
return await this.api_call(`chart/${genre_id}/tracks`, {index, limit})
}
async get_chart_albums(genre_id=0, index=0, limit=10){
return await this.api_call(`chart/${genre_id}/albums`, {index, limit})
}
async get_chart_artists(genre_id=0, index=0, limit=10){
return await this.api_call(`chart/${genre_id}/artists`, {index, limit})
}
async get_chart_playlists(genre_id=0, index=0, limit=10){
return await this.api_call(`chart/${genre_id}/playlists`, {index, limit})
}
async get_chart_podcasts(genre_id=0, index=0, limit=10){
return await this.api_call(`chart/${genre_id}/podcasts`, {index, limit})
}
async get_comment(comment_id){
return await this.api_call(`comment/${comment_id}`)
}
async get_editorials(index=0, limit=10){
return await this.api_call('editorial', {index, limit})
}
async get_editorial(genre_id=0){
return await this.api_call(`editorial/${genre_id}`)
}
async get_editorial_selection(genre_id=0, index=0, limit=10){
return await this.api_call(`editorial/${genre_id}/selection`, {index, limit})
}
async get_editorial_charts(genre_id=0, index=0, limit=10){
return await this.api_call(`editorial/${genre_id}/charts`, {index, limit})
}
async get_editorial_releases(genre_id=0, index=0, limit=10){
return await this.api_call(`editorial/${genre_id}/releases`, {index, limit})
}
async get_genres(index=0, limit=10){
return await this.api_call('genre', {index, limit})
}
async get_genre(genre_id=0){
return await this.api_call(`genre/${genre_id}`)
}
async get_genre_artists(genre_id=0, index=0, limit=10){
return await this.api_call(`genre/${genre_id}/artists`, {index, limit})
}
async get_genre_radios( genre_id=0, index=0, limit=10){
return await this.api_call(`genre/${genre_id}/radios`, {index, limit})
}
async get_infos(){
return await this.api_call('infos')
}
async get_options(){
return await this.api_call('options')
}
async get_playlist(playlist_id){
return await this.api_call(`playlist/${playlist_id}`)
}
async get_playlist_comments(album_id, index=0, limit=10){
return await this.api_call(`playlist/${album_id}/comments`, {index, limit})
}
async get_playlist_fans(album_id, index=0, limit=100){
return await this.api_call(`playlist/${album_id}/fans`, {index, limit})
}
async get_playlist_tracks(album_id, index=0, limit=-1){
return await this.api_call(`playlist/${album_id}/tracks`, {index, limit})
}
async get_playlist_radio(album_id, index=0, limit=100){
return await this.api_call(`playlist/${album_id}/radio`, {index, limit})
}
async get_radios(index=0, limit=10){
return await this.api_call('radio', {index, limit})
}
async get_radios_genres(index=0, limit=25){
return await this.api_call('radio/genres', {index, limit})
}
async get_radios_top(index=0, limit=50){
return await this.api_call('radio/top', {index, limit})
}
async get_radios_lists(index=0, limit=25){
return await this.api_call('radio/lists', {index, limit})
}
async get_radio(radio_id){
return await this.api_call(`radio/${radio_id}`)
}
async get_radio_tracks(radio_id, index=0, limit=40){
return await this.api_call(`radio/${radio_id}/tracks`, {index, limit})
}
_generate_search_advanced_query(artist="", album="", track="", label="", dur_min=0, dur_max=0, bpm_min=0, bpm_max=0){
let query = ""
if (artist != "") query += `artist:"${artist}" `
if (album != "") query += `album:"${album}" `
if (track != "") query += `track:"${track}" `
if (label != "") query += `label:"${label}" `
if (dur_min != 0) query += `dur_min:"${dur_min}" `
if (dur_max != 0) query += `dur_max:"${dur_max}" `
if (bpm_min != 0) query += `bpm_min:"${bpm_min}" `
if (bpm_max != 0) query += `bpm_max:"${bpm_max}" `
return query.trim()
}
_generate_search_args(query, strict=false, order, index=0, limit=25){
let args = {q: query, index, limit}
if (strict) args.strict = 'on'
if (order) args.order = order
return args
}
async search(query, strict=false, order, index=0, limit=25){
const args = this._generate_search_args(query, strict, order, index, limit)
return await this.api_call('search', args)
}
async advanced_search(artist="", album="", track="", label="", dur_min=0, dur_max=0, bpm_min=0, bpm_max=0, strict=false, order, index=0, limit=25){
const query = this._generate_search_advanced_query(artist, album, track, label, dur_min, dur_max, bpm_min, bpm_max)
return await this.search(query, strict, order, index, limit)
}
async search_album(query, strict=false, order, index=0, limit=25){
const args = this._generate_search_args(query, strict, order, index, limit)
return await this.api_call('search/album', args)
}
async search_artist(query, strict=false, order, index=0, limit=25){
const args = this._generate_search_args(query, strict, order, index, limit)
return await this.api_call('search/artist', args)
}
async search_playlist(query, strict=false, order, index=0, limit=25){
const args = this._generate_search_args(query, strict, order, index, limit)
return await this.api_call('search/playlist', args)
}
async search_radio(query, strict=false, order, index=0, limit=25){
const args = this._generate_search_args(query, strict, order, index, limit)
return await this.api_call('search/radio', args)
}
async search_track(query, strict=false, order, index=0, limit=25){
const args = this._generate_search_args(query, strict, order, index, limit)
return await this.api_call('search/track', args)
}
async search_user(query, strict=false, order, index=0, limit=25){
const args = this._generate_search_args(query, strict, order, index, limit)
return await this.api_call('search/user', args)
}
async get_track(song_id){
return await this.api_call(`track/${song_id}`)
}
async get_track_by_ISRC(isrc){
return await this.get_track(`isrc:${isrc}`)
}
async get_user(user_id){
return await this.api_call(`user/${user_id}`)
}
async get_user_albums(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/albums`, {index, limit})
}
async get_user_artists(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/artists`, {index, limit})
}
async get_user_artists(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/artists`, {index, limit})
}
async get_user_flow(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/flow`, {index, limit})
}
async get_user_following(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/followings`, {index, limit})
}
async get_user_followers(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/followers`, {index, limit})
}
async get_user_playlists(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/playlists`, {index, limit})
}
async get_user_radios(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/radios`, {index, limit})
}
async get_user_tracks(user_id, index=0, limit=25){
return await this.api_call(`user/${user_id}/tracks`, {index, limit})
}
// Extra calls
async get_countries_charts(){
let temp = await this.get_user_playlists('637006841', limit=-1)['data']
result = temp.sort((a, b) => a.title.localeCompare(b.title)) // Sort all playlists
if (!result[0].title.startsWith('Top')) result.shift() // Remove loved tracks playlist
return result
}
async get_track_id_from_metadata(artist, track, album){
artist = artist.replace("", "-").replace("", "'")
track = track.replace("", "-").replace("", "'")
album = album.replace("", "-").replace("", "'")
let resp = await this.advanced_search(artist=artist, track=track, album=album, limit=1)
if (resp.data.length > 0) return resp.data[0].id
resp = await this.advanced_search(artist=artist, track=track, limit=1)
if (resp.data.length > 0) return resp.data[0].id
// Try removing version
if ( track.indexOf("(") != -1 && track.indexOf(")") != -1 && track.indexOf("(") < track.indexOf(")") ){
resp = await this.advanced_search(artist=artist, track=track.split("(")[0], limit=1)
if (resp.data.length > 0) return resp.data[0].id
} else if ( track.indexOf(" - ") != -1) {
resp = await this.advanced_search(artist=artist, track=track.split(" - ")[0], limit=1)
if (resp.data.length > 0) return resp.data[0].id
}
return "0"
}
}
// Base class for Deezer exceptions
export class APIError extends Error {
constructor(message) {
super(message);
this.name = "APIError";
}
}
export class ItemsLimitExceededException extends APIError {
constructor(message) {
super(message);
this.name = "ItemsLimitExceededException";
}
}
export class PermissionException extends APIError {
constructor(message) {
super(message);
this.name = "PermissionException";
}
}
export class InvalidTokenException extends APIError {
constructor(message) {
super(message);
this.name = "InvalidTokenException";
}
}
export class WrongParameterException extends APIError {
constructor(message) {
super(message);
this.name = "WrongParameterException";
}
}
export class MissingParameterException extends APIError {
constructor(message) {
super(message);
this.name = "MissingParameterException";
}
}
export class InvalidQueryException extends APIError {
constructor(message) {
super(message);
this.name = "InvalidQueryException";
}
}
export class DataException extends APIError {
constructor(message) {
super(message);
this.name = "DataException";
}
}
export class IndividualAccountChangedNotAllowedException extends APIError {
constructor(message) {
super(message);
this.name = "IndividualAccountChangedNotAllowedException";
}
}

429
deezer/gw.js Normal file
View file

@ -0,0 +1,429 @@
const got = require('got')
const {map_artist_album, map_user_track, map_user_artist, map_user_album, map_user_playlist} = require('./utils.js')
// Explicit Content Lyrics
export const LyricsStatus = {
NOT_EXPLICIT: 0 // Not Explicit
EXPLICIT: 1 // Explicit
UNKNOWN: 2 // Unknown
EDITED: 3 // Edited
PARTIALLY_EXPLICIT: 4 // Partially Explicit (Album "lyrics" only)
PARTIALLY_UNKNOWN: 5 // Partially Unknown (Album "lyrics" only)
NO_ADVICE: 6 // No Advice Available
PARTIALLY_NO_ADVICE: 7 // Partially No Advice Available (Album "lyrics" only)
}
export const PlaylistStatus = {
PUBLIC: 0
PRIVATE: 1
COLLABORATIVE: 2
}
export const EMPTY_TRACK_OBJ = {
SNG_ID: 0,
SNG_TITLE: '',
DURATION: 0,
MD5_ORIGIN: 0,
MEDIA_VERSION: 0,
FILESIZE: 0,
ALB_TITLE: "",
ALB_PICTURE: "",
ART_ID: 0,
ART_NAME: ""
}
export class GW{
constructor(cookie_jar, headers){
this.http_headers = headers
this.cookie_jar = cookie_jar
}
async api_call(method, args, params){
if (typeof args == "unasyncined") args = {}
if (typeof params == "unasyncined") params = {}
p = {
api_version: "1.0",
api_token: method == 'deezer.getUserData' ? 'null' : this._get_token(),
input: '3',
method: method,
..params
}
try{
const result_json = await got.get("http://www.deezer.com/ajax/gw-light.php", {
searchParams: p,
json: args,
cookieJar: this.cookie_jar,
headers: this.http_headers,
timeout: 30000
}).json()
}catch{
await new Promise(r => setTimeout(r, 2000)) // sleep(2000ms)
return await this.api_call(method, args, params)
}
if (result_json.error.length):
throw new GWAPIError(result_json.error)
return result_json.results
}
async _get_token(){
let token_data = await this.get_user_data()
return token_data.checkForm
}
async get_user_data(){
return await this.api_call('deezer.getUserData')
}
async get_user_profile_page(user_id, tab, limit=10){
return await this.api_call('deezer.pageProfile', {user_id, tab, nb: limit})
}
async get_child_accounts(){
return await this.api_call('deezer.getChildAccounts')
}
async get_track(sng_id){
return await this.api_call('song.getData', {sng_id})
}
async get_track_page(sng_id){
return await this.api_call('deezer.pageTrack', {sng_id})
}
async get_track_lyrics(sng_id){
return await this.api_call('song.getLyrics', {sng_id})
}
async get_tracks_gw(sng_ids){
let tracks_array = []
let body = await this.api_call('song.getListData', {sng_ids})
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
}
async get_album(alb_id){
return await this.api_call('album.getData', {alb_id})
}
async get_album_page(alb_id){
return await this.api_call('deezer.pageAlbum', {
alb_id,
lang: 'en',
header: True,
tab: 0
})
}
async get_album_tracks(alb_id){
let tracks_array = []
let body = await this.api_call('song.getListByAlbum', {alb_id, nb: -1})
body.data.forEach(track => {
let _track = track
_track.POSITION = body.data.indexOf(track)
tracks_array.push(_track)
})
return tracks_array
}
async get_artist(art_id){
return await this.api_call('artist.getData', {art_id})
}
async get_artist_page(art_id){
return this.api_call('deezer.pageArtist', {
art_id,
lang: 'en',
header: True,
tab: 0
})
}
async get_artist_top_tracks(art_id, limit=100){
let tracks_array = []
let body = await this.api_call('artist.getTopTrack', {art_id, nb: limit})
body.data.forEach(track => {
track.POSITION = body.data.indexOf(track)
tracks_array.push(_track)
})
return tracks_array
}
async get_artist_discography(art_id, index=0, limit=25){
return await this.api_call('album.getDiscography', {
art_id,
discography_mode:"all",
nb: limit,
nb_songs: 0,
start: index
})
}
async get_playlist(playlist_id){
return await this.api_call('playlist.getData', {playlist_id})
}
async get_playlist_page(playlist_id){
return await this.api_call('deezer.pagePlaylist', {
playlist_id,
lang: 'en',
header: True,
tab: 0
})
}
async get_playlist_tracks(playlist_id){
let tracks_array = []
let body = await this.api_call('playlist.getSongs', {playlist_id, nb: -1})
body.data.forEach(track => {
track.POSITION = body.data.indexOf(track)
tracks_array.push(_track)
})
return tracks_array
}
async create_playlist(title, status=PlaylistStatus.PUBLIC, description, songs=[]){
newSongs = []
songs.forEach(song => {
newSongs.push([song, 0])
});
return await this.api_call('playlist.create', {
title,
status,
description,
songs: newSongs
})
}
async edit_playlist(playlist_id, title, status, description, songs=[]){
newSongs = []
songs.forEach(song => {
newSongs.push([song, 0])
});
return await this.api_call('playlist.update', {
playlist_id,
title,
status,
description,
songs: newSongs
})
}
async add_songs_to_playlist(playlist_id, songs, offset=-1){
newSongs = []
songs.forEach(song => {
newSongs.push([song, 0])
});
return await this.api_call('playlist.addSongs', {
playlist_id,
songs: newSongs,
offset
})
}
async add_song_to_playlist(playlist_id, sng_id, offset=-1){
return await this.add_songs_to_playlist(playlist_id, [sng_id], offset)
}
async remove_songs_from_playlist(playlist_id, songs){
newSongs = []
songs.forEach(song => {
newSongs.push([song, 0])
});
return await this.api_call('playlist.deleteSongs', {
playlist_id,
songs: newSongs
})
}
async remove_song_from_playlist(playlist_id, sng_id){
return await this.remove_songs_from_playlist(playlist_id, [sng_id])
}
async delete_playlist(playlist_id){
return await this.api_call('playlist.delete', {playlist_id})
}
async add_song_to_favorites(sng_id){
return await this.gw_api_call('favorite_song.add', {sng_id})
}
async remove_song_from_favorites(sng_id){
return await this.gw_api_call('favorite_song.remove', {sng_id})
}
async add_album_to_favorites(alb_id){
return await this.gw_api_call('album.addFavorite', {alb_id})
}
async remove_album_from_favorites(alb_id){
return await this.gw_api_call('album.deleteFavorite', {alb_id})
}
async add_artist_to_favorites(art_id){
return await this.gw_api_call('artist.addFavorite', {art_id})
}
async remove_artist_from_favorites(art_id){
return await this.gw_api_call('artist.deleteFavorite', {art_id})
}
async add_playlist_to_favorites(playlist_id){
return await this.gw_api_call('playlist.addFavorite', {PARENT_PLAYLIST_ID: playlist_id})
}
async remove_playlist_from_favorites(playlist_id){
return await this.gw_api_call('playlist.deleteFavorite', {PLAYLIST_ID: playlist_id})
}
async get_page(page){
let params = {
gateway_input: JSON.stringify({
PAGE: page,
VERSION: '2.3',
SUPPORT: {
grid: [
'channel',
'album'
],
horizontal-grid: [
'album'
],
},
LANG: 'en'
})
}
return await this.api_call('page.get', params=params)
}
async search(query, index=0, limit=10, suggest=true, artist_suggest=true, top_tracks=true){
return await this.api_call('deezer.pageSearch', {
query,
start: index,
nb: limit,
suggest,
artist_suggest,
top_tracks
})
}
async search_music(query, type, index=0, limit=10){
return await this.api_call('search.music', {
query,
filter: "ALL",
output: type,
start: index,
nb: limit
})
}
// Extra calls
async get_artist_discography_tabs(art_id, limit=100){
let index = 0
let releases = []
let result = {all: []}
let ids = []
// Get all releases
do {
response = await this.get_artist_discography(art_id, index=index, limit=limit)
releases.concat(response.data)
index += limit
} while (index < response.total)
releases.forEach(release => {
if (ids.indexOf(release.ALB_ID) == -1){
ids.push(release.ALB_ID)
obj = map_artist_album(release)
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)
}
} 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)
}
}
})
return result
}
async get_track_with_fallback(sng_id){
let body
if (int(sng_id) > 0){
try{ body = await this.get_track_page(sng_id) }
catch (e) {}
}
if (body){
if (body.LYRICS) body.DATA.LYRICS = body.LYRICS
body = body.DATA
} else {
body = await this.get_track(sng_id)
}
return body
}
async get_user_playlists(user_id, limit=25){
let user_profile_page = await this.get_user_profile_page(user_id, 'playlists', limit=limit)
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
}
async get_user_albums(user_id, limit=25){
let data = await this.get_user_profile_page(user_id, 'albums', limit=limit).TAB.albums.data
let result = []
data.forEach(album => {
result.push(map_user_album(album))
})
return result
}
async get_user_artists(user_id, limit=25){
let data = this.get_user_profile_page(user_id, 'artists', limit=limit).TAB.artists.data
let result = []
data.forEach(artist => {
result.push(map_user_artist(artist))
})
return result
}
async get_user_tracks(user_id, limit=25){
let data = this.get_user_profile_page(user_id, 'loved', limit=limit).TAB.loved.data
let result = []
data.forEach(track => {
result.push(map_user_track(track))
})
return result
}
}
// Base class for Deezer exceptions
export class GWAPIError extends Error {
constructor(message) {
super(message);
this.name = "APIError";
}
}

127
deezer/index.js Normal file
View file

@ -0,0 +1,127 @@
const got = require('got')
const {CookieJar, Cookie} = require('tough-cookie')
const { API } = require('./api.js')
const { GW } = require('./gw.js')
// Number associtation for formats
export const TrackFormats = {
FLAC : 9
MP3_320 : 3
MP3_128 : 1
MP4_RA3 : 15
MP4_RA2 : 14
MP4_RA1 : 13
DEFAULT : 8
LOCAL : 0
}
export class Deezer{
constructor(accept_language=""){
this.http_headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36",
"Accept-Language": accept_language
}
this.cookie_jar = new CookieJar()
this.logged_in = false
this.current_user = {}
this.childs = []
this.selected_account = 0
this.api = new API(this.cookie_jar, this.http_headers)
this.gw = new GW(this.cookie_jar, this.http_headers)
}
get_accept_language(){
return this.http_headers['Accept-Language']
}
set_accept_language(lang){
this.http_headers['Accept-Language'] = lang
}
async login(email, password, re_captcha_token, child=0){
// Check if user already logged in
let user_data = await this.gw.get_user_data()
if (user_data.USER.USER_ID == 0){
this.logged_in = false
return false
}
// Get the checkFormLogin
let check_form_login = user_data.checkFormLogin
let login = await got.post("https://www.deezer.com/ajax/action.php", {
headers: this.http_headers,
cookieJar: this.cookie_jar,
form:{
type: 'login',
mail: email,
password: password,
checkFormLogin: check_form_login,
reCaptchaToken: re_captcha_token
}
}).text()
// Check if user logged in
if (login.text.indexOf('success') == -1){
this.logged_in = false
return false
}
user_data = await this.gw.get_user_data()
await this._post_login(user_data)
this.change_account(child)
this.logged_in = true
return true
}
async login_via_arl(arl, child=0){
arl = arl.trim()
// TODO: Check how to do this
let cookie_obj = Cookie({
key: 'arl',
value: arl,
path: "/",
httpOnly: true
})
this.cookie_jar.setCookie(cookie_obj, '.deezer.com')
let user_data = await this.gw.get_user_data()
// Check if user logged in
if (user_data.USER.USER_ID == 0){
this.logged_in = false
return false
}
await this._post_login(user_data)
this.change_account(child)
this.logged_in = true
return true
}
async _post_login(user_data){
this.childs = []
let family = user_data.USER.MULTI_ACCOUNT.ENABLED
if (family){
let childs = await this.gw.get_child_accounts()
childs.forEach(child => {
this.childs.push({
'id': child.USER_ID,
'name': child.BLOG_NAME,
'picture': child.USER_PICTURE || ""
})
})
} else {
this.childs.append({
'id': user_data.USER.USER_ID,
'name': user_data.USER.BLOG_NAME,
'picture': user_data.USER.USER_PICTURE || ""
})
}
}
change_account(child_n){
if (this.childs.length-1 < child_n) child_n = 0
this.current_user = this.childs[child_n]
this.selected_account = child_n
return [this.current_user, this.selected_account]
}
}

212
deezer/utils.js Normal file
View file

@ -0,0 +1,212 @@
const RELEASE_TYPE = ["single", "album", "compile", "ep", "bundle"]
// maps gw-light api user/tracks to standard api
export function map_user_track(track){
return {
id: track.SNG_ID,
title: track.SNG_TITLE,
link: 'https://www.deezer.com/track/'+track.SNG_ID,
duration: track.DURATION,
rank: track.RANK_SNG,
explicit_lyrics: int(track.EXPLICIT_LYRICS) > 0,
explicit_content_lyrics: track.EXPLICIT_TRACK_CONTENT.EXPLICIT_COVER_STATUS,
explicit_content_cover: track.EXPLICIT_TRACK_CONTENT.EXPLICIT_LYRICS_STATUS,
time_add: track.DATE_ADD,
album: {
id: track.ALB_ID,
title: track.ALB_TITLE,
cover: 'https://api.deezer.com/album/'+track.ALB_ID+'/image',
cover_small: 'https://e-cdns-images.dzcdn.net/images/cover/'+track.ALB_PICTURE+'/56x56-000000-80-0-0.jpg',
cover_medium: 'https://e-cdns-images.dzcdn.net/images/cover/'+track.ALB_PICTURE+'/250x250-000000-80-0-0.jpg',
cover_big: 'https://e-cdns-images.dzcdn.net/images/cover/'+track.ALB_PICTURE+'/500x500-000000-80-0-0.jpg',
cover_xl: 'https://e-cdns-images.dzcdn.net/images/cover/'+track.ALB_PICTURE+'/1000x1000-000000-80-0-0.jpg',
tracklist: 'https://api.deezer.com/album/'+track.ALB_ID+'/tracks',
type: 'album'
},
artist: {
id: track.ART_ID,
name: track.ART_NAME,
picture: 'https://api.deezer.com/artist/'+track.ART_ID+'/image',
picture_small: 'https://e-cdns-images.dzcdn.net/images/artist/'+track.ART_PICTURE+'/56x56-000000-80-0-0.jpg',
picture_medium: 'https://e-cdns-images.dzcdn.net/images/artist/'+track.ART_PICTURE+'/250x250-000000-80-0-0.jpg',
picture_big: 'https://e-cdns-images.dzcdn.net/images/artist/'+track.ART_PICTURE+'/500x500-000000-80-0-0.jpg',
picture_xl: 'https://e-cdns-images.dzcdn.net/images/artist/'+track.ART_PICTURE+'/1000x1000-000000-80-0-0.jpg',
tracklist: 'https://api.deezer.com/artist/'+track.ART_ID+'/top?limit=50',
type: 'artist'
},
type: 'track'
}
}
// maps gw-light api user/artists to standard api
export function map_user_artist(artist){
return {
id: artist.ART_ID,
name: artist.ART_NAME,
link: 'https://www.deezer.com/artist/'+artist.ART_ID,
picture: 'https://api.deezer.com/artist/'+artist.ART_ID+'/image',
picture_small: 'https://e-cdns-images.dzcdn.net/images/artist/'+artist.ART_PICTURE+'/56x56-000000-80-0-0.jpg',
picture_medium: 'https://e-cdns-images.dzcdn.net/images/artist/'+artist.ART_PICTURE+'/250x250-000000-80-0-0.jpg',
picture_big: 'https://e-cdns-images.dzcdn.net/images/artist/'+artist.ART_PICTURE+'/500x500-000000-80-0-0.jpg',
picture_xl: 'https://e-cdns-images.dzcdn.net/images/artist/'+artist.ART_PICTURE+'/1000x1000-000000-80-0-0.jpg',
nb_fan: artist.NB_FAN,
tracklist: 'https://api.deezer.com/artist/'+artist.ART_ID+'/top?limit=50',
type: 'artist'
}
}
// maps gw-light api user/albums to standard api
export function map_user_album(album){
return {
id: album.ALB_ID,
title: album.ALB_TITLE,
link: 'https://www.deezer.com/album/'+album.ALB_ID,
cover: 'https://api.deezer.com/album/'+album.ALB_ID+'/image',
cover_small: 'https://e-cdns-images.dzcdn.net/images/cover/'+album.ALB_PICTURE+'/56x56-000000-80-0-0.jpg',
cover_medium: 'https://e-cdns-images.dzcdn.net/images/cover/'+album.ALB_PICTURE+'/250x250-000000-80-0-0.jpg',
cover_big: 'https://e-cdns-images.dzcdn.net/images/cover/'+album.ALB_PICTURE+'/500x500-000000-80-0-0.jpg',
cover_xl: 'https://e-cdns-images.dzcdn.net/images/cover/'+album.ALB_PICTURE+'/1000x1000-000000-80-0-0.jpg',
tracklist: 'https://api.deezer.com/album/'+album.ALB_ID+'/tracks',
explicit_lyrics: album.EXPLICIT_ALBUM_CONTENT.EXPLICIT_LYRICS_STATUS > 0,
artist: {
id: album.ART_ID,
name: album.ART_NAME,
picture: 'https://api.deezer.com/artist/'+album.ART_ID+'image',
tracklist: 'https://api.deezer.com/artist/'+album.ART_ID+'/top?limit=50'
},
type: 'album'
}
}
// maps gw-light api user/playlists to standard api
export function map_user_playlist(playlist, default_user_name=""){
return {
id: playlist.PLAYLIST_ID,
title: playlist.TITLE,
description: playlist.DESCRIPTION || "",
nb_tracks: playlist.NB_SONG,
link: 'https://www.deezer.com/playlist/'+playlist.PLAYLIST_ID,
picture: 'https://api.deezer.com/playlist/'+playlist.PLAYLIST_ID+'/image',
picture_small: 'https://e-cdns-images.dzcdn.net/images/'+playlist.PICTURE_TYPE+'/'+playlist.PLAYLIST_PICTURE+'/56x56-000000-80-0-0.jpg',
picture_medium: 'https://e-cdns-images.dzcdn.net/images/'+playlist.PICTURE_TYPE+'/'+playlist.PLAYLIST_PICTURE+'/250x250-000000-80-0-0.jpg',
picture_big: 'https://e-cdns-images.dzcdn.net/images/'+playlist.PICTURE_TYPE+'/'+playlist.PLAYLIST_PICTURE+'/500x500-000000-80-0-0.jpg',
picture_xl: 'https://e-cdns-images.dzcdn.net/images/'+playlist.PICTURE_TYPE+'/'+playlist.PLAYLIST_PICTURE+'/1000x1000-000000-80-0-0.jpg',
tracklist: 'https://api.deezer.com/playlist/'+playlist.PLAYLIST_ID+'/tracks',
creation_date: playlist.DATE_ADD,
creator: {
id: playlist.PARENT_USER_ID,
name: playlist.PARENT_USERNAME || default_user_name
},
type: 'playlist'
}
}
// maps gw-light api albums to standard api
export function map_album(album){
return {
id: album.ALB_ID,
title: album.ALB_TITLE,
upc: "", // TODO: Needs to be checked
link: `https://www.deezer.com/album/${album.ALB_ID}`,
share: "", // TODO: Needs to be checked
cover: `https://api.deezer.com/album/${album.ALB_ID}/image`,
cover_small: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/56x56-000000-80-0-0.jpg`,
cover_medium: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/250x250-000000-80-0-0.jpg`,
cover_big: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/500x500-000000-80-0-0.jpg`,
cover_xl: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/1000x1000-000000-80-0-0.jpg`,
md5_image: album.ALB_PICTURE,
genre_id: album.GENRE_ID,
genres: [], // TODO: Needs to be checked
label: "", // TODO: Needs to be checked
nb_tracks: album.NUMBER_TRACK,
duration: 0, // TODO: Needs to be checked
fans: album.RANK,
rating: 0, // TODO: Needs to be checked
release_date: album.PHYSICAL_RELEASE_DATE,
record_type: RELEASE_TYPE[int(album.TYPE)] || "unknown",
available: true, // TODO: Needs to be checked
alternative: null, // TODO: Needs to be checked
tracklist: `https://api.deezer.com/album/${album.ALB_ID}/tracks`,
explicit_lyrics: int(album.EXPLICIT_LYRICS) > 0,
explicit_content_lyrics: 2, // TODO: Needs to be checked
explicit_content_cover: 2, // TODO: Needs to be checked
contributors: [], // TODO: Needs to be checked
artist: null, // TODO: Needs to be checked
tracks: [], // TODO: Needs to be checked
type: album.__TYPE__,
// Extras
nb_disk: album.NUMBER_DISK
}
}
// maps gw-light api artist/albums to standard api
export function map_artist_album(album){
return {
id: album.ALB_ID,
title: album.ALB_TITLE,
link: `https://www.deezer.com/album/${album.ALB_ID}`,
cover: `https://api.deezer.com/album/${album.ALB_ID}/image`,
cover_small: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/56x56-000000-80-0-0.jpg`,
cover_medium: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/250x250-000000-80-0-0.jpg`,
cover_big: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/500x500-000000-80-0-0.jpg`,
cover_xl: `https://cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/1000x1000-000000-80-0-0.jpg`,
genre_id: album.GENRE_ID,
fans: album.RANK,
release_date: album.PHYSICAL_RELEASE_DATE,
record_type: RELEASE_TYPE[int(album.TYPE)] || "unknown",
tracklist: `https://api.deezer.com/album/${album.ALB_ID}/tracks`,
explicit_lyrics: int(album.EXPLICIT_LYRICS) > 0,
type: album.__TYPE__,
// Extras
nb_tracks: album.NUMBER_TRACK,
nb_disk: album.NUMBER_DISK
}
}
// maps gw-light api playlists to standard api
export function map_playlist(playlist){
return {
id: playlist.PLAYLIST_ID,
title: playlist.TITLE,
description: playlist.DESCRIPTION,
duration: playlist.DURATION,
public: playlist.STATUS == 1,
is_loved_track: playlist.TYPE == 4,
collaborative: playlist.STATUS == 2,
nb_tracks: playlist.NB_SONG,
fans: playlist.NB_FAN,
link: "https://www.deezer.com/playlist/"+playlist.PLAYLIST_ID,
share: "https://www.deezer.com/playlist/"+playlist.PLAYLIST_ID,
picture: "https://api.deezer.com/playlist/"+playlist.PLAYLIST_ID+"/image",
picture_small: "https://cdns-images.dzcdn.net/images/"+playlist.PICTURE_TYPE+"/"+playlist.PLAYLIST_PICTURE+"/56x56-000000-80-0-0.jpg",
picture_medium: "https://cdns-images.dzcdn.net/images/"+playlist.PICTURE_TYPE+"/"+playlist.PLAYLIST_PICTURE+"/250x250-000000-80-0-0.jpg",
picture_big: "https://cdns-images.dzcdn.net/images/"+playlist.PICTURE_TYPE+"/"+playlist.PLAYLIST_PICTURE+"/500x500-000000-80-0-0.jpg",
picture_xl: "https://cdns-images.dzcdn.net/images/"+playlist.PICTURE_TYPE+"/"+playlist.PLAYLIST_PICTURE+"/1000x1000-000000-80-0-0.jpg",
checksum: playlist.CHECKSUM,
tracklist: "https://api.deezer.com/playlist/"+playlist.PLAYLIST_ID+"/tracks",
creation_date: playlist.DATE_ADD,
creator: {
id: playlist.PARENT_USER_ID,
name: playlist.PARENT_USERNAME,
tracklist: "https://api.deezer.com/user/"+playlist.PARENT_USER_ID+"/flow",
type: "user"
},
type: "playlist"
}
}
// Cleanup terms that can hurt search results
export function clean_search_query(term){
term = term.replaceAll(/ feat[\.]? /g, " ")
term = term.replaceAll(/ ft[\.]? /g, " ")
term = term.replaceAll(/\(feat[\.]? /g, " ")
term = term.replaceAll(/\(ft[\.]? /g, " ")
term = term.replace(' & ', " ").replace('', "-").replace('—', "-")
return term
}

574
package-lock.json generated Normal file
View file

@ -0,0 +1,574 @@
{
"name": "deezer-js",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"got": "^11.8.2",
"tough-cookie": "^4.0.0"
}
},
"node_modules/@sindresorhus/is": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz",
"integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/is?sponsor=1"
}
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
"dependencies": {
"defer-to-connect": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@types/cacheable-request": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
"integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
"dependencies": {
"@types/http-cache-semantics": "*",
"@types/keyv": "*",
"@types/node": "*",
"@types/responselike": "*"
}
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
},
"node_modules/@types/keyv": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
"integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "14.14.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.33.tgz",
"integrity": "sha512-oJqcTrgPUF29oUP8AsUqbXGJNuPutsetaa9kTQAQce5Lx5dTYWV02ScBiT/k1BX/Z7pKeqedmvp39Wu4zR7N7g=="
},
"node_modules/@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/cacheable-lookup": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
"engines": {
"node": ">=10.6.0"
}
},
"node_modules/cacheable-request": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
"dependencies": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^4.1.0",
"responselike": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/clone-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
"dependencies": {
"mimic-response": "^1.0.0"
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/decompress-response/node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/defer-to-connect": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
"engines": {
"node": ">=10"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dependencies": {
"pump": "^3.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/got": {
"version": "11.8.2",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz",
"integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==",
"dependencies": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
"@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.1",
"decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0",
"p-cancelable": "^2.0.0",
"responselike": "^2.0.0"
},
"engines": {
"node": ">=10.19.0"
},
"funding": {
"url": "https://github.com/sindresorhus/got?sponsor=1"
}
},
"node_modules/http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
"dependencies": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
},
"engines": {
"node": ">=10.19.0"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"node_modules/keyv": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
"integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
"dependencies": {
"json-buffer": "3.0.1"
}
},
"node_modules/lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
"engines": {
"node": ">=8"
}
},
"node_modules/mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/p-cancelable": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz",
"integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
},
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"engines": {
"node": ">=6"
}
},
"node_modules/quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/resolve-alpn": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz",
"integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA=="
},
"node_modules/responselike": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
"dependencies": {
"lowercase-keys": "^2.0.0"
}
},
"node_modules/tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.1.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
},
"dependencies": {
"@sindresorhus/is": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz",
"integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ=="
},
"@szmarczak/http-timer": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
"requires": {
"defer-to-connect": "^2.0.0"
}
},
"@types/cacheable-request": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
"integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
"requires": {
"@types/http-cache-semantics": "*",
"@types/keyv": "*",
"@types/node": "*",
"@types/responselike": "*"
}
},
"@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
},
"@types/keyv": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
"integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
"requires": {
"@types/node": "*"
}
},
"@types/node": {
"version": "14.14.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.33.tgz",
"integrity": "sha512-oJqcTrgPUF29oUP8AsUqbXGJNuPutsetaa9kTQAQce5Lx5dTYWV02ScBiT/k1BX/Z7pKeqedmvp39Wu4zR7N7g=="
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
"requires": {
"@types/node": "*"
}
},
"cacheable-lookup": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="
},
"cacheable-request": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
"requires": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^4.1.0",
"responselike": "^2.0.0"
}
},
"clone-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
"requires": {
"mimic-response": "^1.0.0"
}
},
"decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"requires": {
"mimic-response": "^3.1.0"
},
"dependencies": {
"mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
}
}
},
"defer-to-connect": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"requires": {
"pump": "^3.0.0"
}
},
"got": {
"version": "11.8.2",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz",
"integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==",
"requires": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
"@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.1",
"decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0",
"p-cancelable": "^2.0.0",
"responselike": "^2.0.0"
}
},
"http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
},
"http2-wrapper": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
"requires": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
}
},
"json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"keyv": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
"integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
"requires": {
"json-buffer": "3.0.1"
}
},
"lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
},
"mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"p-cancelable": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz",
"integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ=="
},
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
},
"resolve-alpn": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz",
"integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA=="
},
"responselike": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
"requires": {
"lowercase-keys": "^2.0.0"
}
},
"tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
"requires": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.1.2"
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}

19
package.json Normal file
View file

@ -0,0 +1,19 @@
{
"name": "deezer-js",
"version": "0.0.1",
"description": "A wrapper for all Deezer's APIs",
"main": "deezer/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://git.rip/RemixDev/deezer-js"
},
"author": "RemixDev",
"license": "GPL-3.0-or-later",
"dependencies": {
"got": "^11.8.2",
"tough-cookie": "^4.0.0"
}
}