const { Single, Collection } = require('./types/DownloadObjects.js') const { LyricsStatus } = require('deezer-js').gw const { map_user_playlist } = require('deezer-js').utils const { each } = require('async') async function generateTrackItem(dz, id, bitrate, trackAPI, albumAPI){ // Check if is an isrc: url if (String(id).startsWith("isrc")){ try { trackAPI = await dz.api.get_track(id) } catch (e){ console.trace(e) throw new GenerationError(`https://deezer.com/track/${id}`, e.message) } if (trackAPI.id && trackAPI.title){ id = trackAPI.id } else { throw new ISRCnotOnDeezer(`https://deezer.com/track/${id}`) } } if (!(/^-?\d+$/.test(id))) throw new InvalidID(`https://deezer.com/track/${id}`) // Get essential track info let trackAPI_gw try { trackAPI_gw = await dz.gw.get_track_with_fallback(id) } catch (e){ console.trace(e) throw new GenerationError(`https://deezer.com/track/${id}`, e.message) } let title = trackAPI_gw.SNG_TITLE.trim() if (trackAPI_gw.VERSION && !title.includes(trackAPI_gw.VERSION.trim())){ title += ` ${trackAPI_gw.VERSION.trim()}` } const explicit = Boolean(parseInt(trackAPI_gw.EXPLICIT_LYRICS || "0")) return new Single({ type: 'track', id: id, bitrate: bitrate, title: title, artist: trackAPI_gw.ART_NAME, cover: `https://e-cdns-images.dzcdn.net/images/cover/${trackAPI_gw.ALB_PICTURE}/75x75-000000-80-0-0.jpg`, explicit: explicit, single: { trackAPI_gw: trackAPI_gw, trackAPI: trackAPI, albumAPI: albumAPI } }) } async function generateAlbumItem(dz, id, bitrate, rootArtist){ // Get essential album info let albumAPI if (String(id).startsWith('upc')){ let upcs = [id.slice(4)] upcs.push(parseInt(upcs[0])) // Try UPC without leading zeros as well let lastError await each(upcs, async (upc)=>{ try { albumAPI = await dz.api.get_album(`upc:${upc}`) } catch (e) { lastError = e albumAPI = null } }) if (!albumAPI){ console.trace(lastError) throw new GenerationError(`https://deezer.com/album/${id}`, lastError.message) } id = albumAPI.id } else { try{ albumAPI = await dz.api.get_album(id) } catch (e){ console.trace(e) throw new GenerationError(`https://deezer.com/album/${id}`, e.message) } } if (!(/^\d+$/.test(id))) throw new InvalidID(`https://deezer.com/album/${id}`) // Get extra info about album // This saves extra api calls when downloading let albumAPI_gw = await dz.gw.get_album(id) albumAPI.nb_disk = albumAPI_gw.NUMBER_DISK albumAPI.copyright = albumAPI_gw.COPYRIGHT albumAPI.release_date = albumAPI_gw.PHYSICAL_RELEASE_DATE albumAPI.root_artist = rootArtist // If the album is a single download as a track if (albumAPI.nb_tracks == 1){ if (albumAPI.tracks.data.length) return generateTrackItem(dz, albumAPI.tracks.data[0].id, bitrate, null, albumAPI) throw new GenerationError(`https://deezer.com/album/${id}`, "Single has no tracks.") } let tracksArray = await dz.gw.get_album_tracks(id) let cover if (albumAPI.cover_small){ cover = albumAPI.cover_small.slice(0, -24) + '/75x75-000000-80-0-0.jpg' }else{ cover = `https://e-cdns-images.dzcdn.net/images/cover/${albumAPI_gw.ALB_PICTURE}/75x75-000000-80-0-0.jpg` } const totalSize = tracksArray.length albumAPI.nb_tracks = totalSize let collection = [] tracksArray.forEach((trackAPI, pos) => { trackAPI.POSITION = pos+1 collection.push(trackAPI) }) let explicit = [LyricsStatus.EXPLICIT, LyricsStatus.PARTIALLY_EXPLICIT].includes(albumAPI_gw.EXPLICIT_ALBUM_CONTENT.EXPLICIT_LYRICS_STATUS || LyricsStatus.UNKNOWN) return new Collection({ type: 'album', id: id, bitrate: bitrate, title: albumAPI.title, artist: albumAPI.artist.name, cover: cover, explicit: explicit, size: totalSize, collection: { tracks_gw: collection, albumAPI: albumAPI } }) } async function generatePlaylistItem(dz, id, bitrate, playlistAPI, playlistTracksAPI){ if (!playlistAPI){ if (!(/^\d+$/.test(id))) throw new InvalidID(`https://deezer.com/playlist/${id}`) // Get essential playlist info try{ playlistAPI = await dz.api.get_playlist(id) }catch (e){ console.trace(e) playlistAPI = null } // Fallback to gw api if the playlist is private if (!playlistAPI){ try{ let userPlaylist = await dz.gw.get_playlist_page(id) playlistAPI = map_user_playlist(userPlaylist['DATA']) }catch (e){ console.trace(e) throw new GenerationError(`https://deezer.com/playlist/${id}`, e.message) } } // Check if private playlist and owner if (!playlistAPI.public && playlistAPI.creator.id != dz.current_user.id){ throw new NotYourPrivatePlaylist(`https://deezer.com/playlist/${id}`) } } if (!playlistTracksAPI){ playlistTracksAPI = await dz.gw.get_playlist_tracks(id) } playlistAPI.various_artist = await dz.api.get_artist(5080) // Useful for save as compilation const totalSize = playlistTracksAPI.length playlistAPI.nb_tracks = totalSize let collection = [] playlistTracksAPI.forEach((trackAPI, pos) => { if (trackAPI.EXPLICIT_TRACK_CONTENT && [LyricsStatus.EXPLICIT, LyricsStatus.PARTIALLY_EXPLICIT].includes(trackAPI.EXPLICIT_TRACK_CONTENT.EXPLICIT_LYRICS_STATUS)) playlistAPI.explicit = true trackAPI.POSITION = pos+1 collection.push(trackAPI) }); if (!playlistAPI.explicit) playlistAPI.explicit = false return new Collection({ type: 'playlist', id: id, bitrate: bitrate, title: playlistAPI.title, artist: playlistAPI.creator.name, cover: playlistAPI.picture_small.slice(0, -24) + '/75x75-000000-80-0-0.jpg', explicit: playlistAPI.explicit, size: totalSize, collection: { tracks_gw: collection, playlistAPI: playlistAPI } }) } async function generateArtistItem(dz, id, bitrate, listener){ if (!(/^\d+$/.test(id))) throw new InvalidID(`https://deezer.com/artist/${id}`) // Get essential artist info let artistAPI try{ artistAPI = await dz.api.get_artist(id) }catch (e){ console.trace(e) throw new GenerationError(`https://deezer.com/artist/${id}`, e.message) } const rootArtist = { id: artistAPI.id, name: artistAPI.name, picture_small: artistAPI.picture_small } if (listener) { listener.send("startAddingArtist", rootArtist) } const artistDiscographyAPI = await dz.gw.get_artist_discography_tabs(id, 100) const allReleases = artistDiscographyAPI.all || [] let albumList = [] await each(allReleases, async (album) =>{ try{ let albumData = await generateAlbumItem(dz, album.id, bitrate, rootArtist) albumList.push(albumData) }catch (e){ console.warn(album.id, "No Data", e) } }) if (listener) { listener.send("finishAddingArtist", rootArtist) } return albumList } async function generateArtistDiscographyItem(dz, id, bitrate, listener){ if (!(/^\d+$/.test(id))) throw new InvalidID(`https://deezer.com/artist/${id}/discography`) // Get essential artist info let artistAPI try{ artistAPI = await dz.api.get_artist(id) }catch (e){ console.trace(e) throw new GenerationError(`https://deezer.com/artist/${id}/discography`, e.message) } const rootArtist = { id: artistAPI.id, name: artistAPI.name, picture_small: artistAPI.picture_small } if (listener) { listener.send("startAddingArtist", rootArtist) } let artistDiscographyAPI = await dz.gw.get_artist_discography_tabs(id, 100) delete artistDiscographyAPI.all let albumList = [] await each(artistDiscographyAPI, async(type) => { await each(type, async (album) =>{ try{ let albumData = await generateAlbumItem(dz, album.id, bitrate, rootArtist) albumList.push(albumData) }catch (e){ console.warn(album.id, "No Data", e) } }); }); if (listener) { listener.send("finishAddingArtist", rootArtist) } return albumList } async function generateArtistTopItem(dz, id, bitrate){ if (!(/^\d+$/.test(id))) throw new InvalidID(`https://deezer.com/artist/${id}/top_track`) // Get essential artist info let artistAPI try{ artistAPI = dz.api.get_artist(id) }catch (e){ console.trace(e) throw new GenerationError(`https://deezer.com/artist/${id}/top_track`, e.message) } // Emulate the creation of a playlist // Can't use generatePlaylistItem directly as this is not a real playlist const playlistAPI = { id: artistAPI.id+"_top_track", title: artistAPI.name+" - Top Tracks", description: "Top Tracks for "+artistAPI.name, duration: 0, public: true, is_loved_track: false, collaborative: false, nb_tracks: 0, fans: artistAPI.nb_fan, link: "https://www.deezer.com/artist/"+artistAPI.id+"/top_track", share: null, picture: artistAPI.picture, picture_small: artistAPI.picture_small, picture_medium: artistAPI.picture_medium, picture_big: artistAPI.picture_big, picture_xl: artistAPI.picture_xl, checksum: null, tracklist: "https://api.deezer.com/artist/"+artistAPI.id+"/top", creation_date: "XXXX-00-00", creator: { id: "art_"+artistAPI.id, name: artistAPI.name, type: "user" }, type: "playlist" } let artistTopTracksAPI_gw = await dz.gw.get_artist_toptracks(id) return generatePlaylistItem(dz, playlistAPI.id, bitrate, playlistAPI, artistTopTracksAPI_gw) } class GenerationError extends Error { constructor(link, message) { super(message) this.link = link this.name = "GenerationError" } } class ISRCnotOnDeezer extends GenerationError { constructor(link) { super(link, "Track ISRC is not available on deezer") this.name = "ISRCnotOnDeezer" this.errid = "ISRCnotOnDeezer" } } class NotYourPrivatePlaylist extends GenerationError { constructor(link) { super(link, "You can't download others private playlists.") this.name = "NotYourPrivatePlaylist" this.errid = "notYourPrivatePlaylist" } } class TrackNotOnDeezer extends GenerationError { constructor(link) { super(link, "Track not found on deezer!") this.name = "TrackNotOnDeezer" this.errid = "trackNotOnDeezer" } } class AlbumNotOnDeezer extends GenerationError { constructor(link) { super(link, "Album not found on deezer!") this.name = "AlbumNotOnDeezer" this.errid = "albumNotOnDeezer" } } class InvalidID extends GenerationError { constructor(link) { super(link, "Link ID is invalid!") this.name = "InvalidID" this.errid = "invalidID" } } class LinkNotSupported extends GenerationError { constructor(link) { super(link, "Link is not supported.") this.name = "LinkNotSupported" this.errid = "unsupportedURL" } } class LinkNotRecognized extends GenerationError { constructor(link) { super(link, "Link is not recognized.") this.name = "LinkNotRecognized" this.errid = "invalidURL" } } module.exports = { generateTrackItem, generateAlbumItem, generatePlaylistItem, generateArtistItem, generateArtistDiscographyItem, generateArtistTopItem, GenerationError, ISRCnotOnDeezer, NotYourPrivatePlaylist, TrackNotOnDeezer, AlbumNotOnDeezer, InvalidID, LinkNotSupported, LinkNotRecognized }