Small REST API cleanup
[DeezloaderRemix.git] / app / app.js
1 /*
2 *   _____                _                 _             _____                _
3 *  |  __ \              | |               | |           |  __ \              (_)
4 *  | |  | | ___  ___ ___| | ___   __ _  __| | ___ _ __  | |__) |___ _ __ ___  ___  __
5 *  | |  | |/ _ \/ _ \_  / |/ _ \ / _` |/ _` |/ _ \ '__| |  _  // _ \ '_ ` _ \| \ \/ /
6 *  | |__| |  __/  __// /| | (_) | (_| | (_| |  __/ |    | | \ \  __/ | | | | | |>  <
7 *  |_____/ \___|\___/___|_|\___/ \__,_|\__,_|\___|_|    |_|  \_\___|_| |_| |_|_/_/\_\
8 *
9 **/
10
11 // Server stuff
12 const express = require('express')
13 const app = express()
14 const server = require('http').createServer(app)
15 const io = require('socket.io').listen(server, {log: false, wsEngine: 'ws'})
16 var cookieParser = require('cookie-parser')
17 var i18n = require('./i18n');
18 // REST API
19 const socketclientio = require('socket.io-client')
20 const bodyParser = require('body-parser');      //for receiving req.body JSON
21
22 // Music tagging stuff
23 const metaflac = require('metaflac-js2')
24 const ID3Writer = require('./lib/browser-id3-writer')
25 const deezerApi = require('deezer-api')
26 const getBlowfishKey = require('deezer-api/utils.js').getBlowfishKey
27 const decryptChunk = require('deezer-api/utils.js').decryptChunk
28 const spotifyApi = require('spotify-web-api-node')
29
30 // App stuff
31 const fs = require('fs-extra')
32 const async = require('async')
33 const https = require('https')
34 const request = require('request-promise')
35 const os = require('os')
36 const path = require('path')
37 const logger = require('./utils/logger.js')
38 const queue = require('queue')
39 const localpaths = require('./utils/localpaths.js')
40 const package = require('./package.json')
41 const stq = require('sequential-task-queue')
42
43 // Main Constants
44 // Files
45 const configFileLocation = localpaths.user+"config.json"
46 // Folders
47 var coverArtFolder = 'deezloader-imgs' + path.sep
48 var defaultDownloadFolder = path.sep + 'Deezloader Music' + path.sep
49
50 if(process.platform == "android"){
51         coverArtFolder = localpaths.user + coverArtFolder
52         fs.ensureFileSync(coverArtFolder + '.nomedia');
53         defaultDownloadFolder = localpaths.home + path.sep + 'Music' + defaultDownloadFolder
54 }else{
55         coverArtFolder = os.tmpdir() + path.sep + coverArtFolder
56         defaultDownloadFolder = localpaths.home + defaultDownloadFolder
57 }
58
59 // Default settings
60 const defaultSettings = require('./default.json').userDefined
61
62 // Setup the folders START
63 var mainFolder = defaultDownloadFolder
64
65 // First run, create config file
66 if(!fs.existsSync(configFileLocation)){
67         logger.info("Can't find config.json, creating one now!")
68         fs.outputFileSync(configFileLocation, fs.readFileSync(__dirname+path.sep+"default.json",'utf8'))
69 }
70
71 if(!fs.existsSync(localpaths.user+"authCredentials.json")){
72         logger.info("Can't find authCredentials.json, creating one now!")
73         fs.outputFileSync(localpaths.user+"authCredentials.json", JSON.stringify({clientId: "", clientSecret: ""}, null, 2))
74 }
75
76 // Spotify Files
77 var authCredentials = require(localpaths.user+'authCredentials.json')
78 if (authCredentials.clientId == "" || authCredentials.clientSecret == ""){
79         spotifySupport = false
80 }else{
81         spotifySupport = true
82         var Spotify = new spotifyApi(authCredentials)
83 }
84
85 // See if all settings are there after update
86 var configFile = require(configFileLocation);
87 for (let x in defaultSettings){
88         if (typeof configFile.userDefined[x] != typeof defaultSettings[x]){
89                 configFile.userDefined[x] = defaultSettings[x]
90         }
91 }
92 // Set default download folder if not userDefined
93 if (configFile.userDefined.downloadLocation != "") {
94         mainFolder = configFile.userDefined.downloadLocation
95 }
96
97 initFolders();
98
99 // Route and create server
100 app.use('/', express.static(__dirname + '/public/'))
101 app.set('views', __dirname + '/views');
102 app.use(cookieParser());
103 app.use(bodyParser.json());
104 app.use(bodyParser.urlencoded({ extended: true }));
105 app.use(i18n.express);
106 server.listen(configFile.serverPort)
107 logger.info('Server is running @ localhost:' + configFile.serverPort)
108
109 app.get('/', function(req, res) {
110   res.render('index.ejs');
111 });
112
113 var dqueue = new stq.SequentialTaskQueue()
114 var downloadQueue = {}
115 var trackQueue = queue({
116         autostart: true
117 })
118 trackQueue.concurrency = configFile.userDefined.queueConcurrency
119
120 // START sockets clusterfuck
121 io.sockets.on('connection', function (s) {
122         const req = s.request
123         i18n.init(req)
124
125         request({
126                 url: "https://www.deezer.com/",
127                 rejectUnauthorized: false,
128                 headers: {Cookie: `dz_lang=en; Domain=deezer.com; Path=/; Secure; hostOnly=false;`}
129         })
130         .then(body=>{
131                 logger.info("Checking for country")
132                 let re = /(<\s*title[^>]*>(.+?)<\s*\/\s*title)>/gi;
133                 let match = re.exec(body);
134                 if (match && match[2]) {
135                         let title = match[2]
136                         if (title === "Deezer will soon be available in your country."){
137                                 s.emit("deezerNotAvailable")
138                         }
139                 }
140         })
141         .catch(err=>{
142                 logger.error(`CountryCheck failed: ${err}`)
143         })
144
145         logger.info("Connection received!")
146
147         // Connection dependet variables
148         s.Deezer = new deezerApi()
149         s.spotifyUser = null
150
151         s.emit("getDefaultSettings", defaultSettings, defaultDownloadFolder)
152         s.emit("populateDownloadQueue", downloadQueue)
153         s.emit("checkAutologin")
154
155         if(process.platform != "android"){
156                 const captcha = require('./utils/captcha');
157                 captcha.callbackResponse = function (data) {
158                         s.emit("getCaptcha", data)
159                 };
160         }
161
162         s.on("getLang", (lang)=>{
163                 req.setLocale(lang)
164                 logger.info("Connection language set to: "+lang)
165         })
166
167         // Check for updates
168         request({
169                 url: "https://notabug.org/RemixDevs/DeezloaderRemix/raw/master/update.json",
170                 rejectUnauthorized: false,
171                 json: true
172         })
173         .then(body=>{
174                 logger.info("Checking for updates")
175                 let [currentVersion_MAJOR, currentVersion_MINOR, currentVersion_PATCH] = package.version.split(".").map(x=>parseInt(x))
176                 let [lastVersion_MAJOR, lastVersion_MINOR, lastVersion_PATCH] = body.version.split(".").map(x=>parseInt(x))
177                 if (
178                         lastVersion_MAJOR>currentVersion_MAJOR ||
179                         lastVersion_MAJOR==currentVersion_MAJOR && lastVersion_MINOR>currentVersion_MINOR ||
180                         lastVersion_MAJOR==currentVersion_MAJOR && lastVersion_MINOR==currentVersion_MINOR && lastVersion_PATCH>currentVersion_PATCH
181                 ){
182                         logger.info("Update Available")
183                         s.emit("messageUpdate", {title: `Version ${lastVersion_MAJOR}.${lastVersion_MINOR}.${lastVersion_PATCH} is available!`, msg: body.changelog, lastVersion: body.version})
184                 }else{
185                         logger.info("Running the latest version!")
186                 }
187         })
188         .catch(error=>{
189                 logger.error(`UpdateCheck failed: ${error}`)
190         })
191
192         // Function for logging in
193         s.on("login", async function (username, password, captchaResponse) {
194                 try{
195                         logger.info("Logging in");
196                         await s.Deezer.login(username, password, captchaResponse)
197                         s.emit("login", {user: s.Deezer.user})
198                         logger.info("Logged in successfully")
199                         // Save session login so next time login is not needed
200                         // This is the same method used by the official website
201                         s.emit('getCookies', s.Deezer.getCookies())
202                 }catch(err){
203                         s.emit("login", {error: err.message})
204                         logger.error(`Login failed: ${err.message}`)
205                 }
206         })
207
208         // Function for login with userToken
209         s.on("loginViaUserToken", async function (userToken) {
210                 try{
211                         logger.info("Logging in");
212                         await s.Deezer.loginViaArl(userToken)
213                         s.emit("login", {user: s.Deezer.user})
214                         logger.info("Logged in successfully")
215                         // Save session login so next time login is not needed
216                         // This is the same method used by the official website
217                         s.emit('getCookies', s.Deezer.getCookies())
218                 }catch(err){
219                         s.emit("login", {error: err.message})
220                         logger.error(`Login failed: ${err.message}`)
221                 }
222         });
223
224         // Function for autologin
225         s.on("autologin", async function(jar, email){
226                 try{
227                         logger.info("Logging in");
228                         await s.Deezer.loginViaCookies(jar, email)
229                         s.emit('login', {user: s.Deezer.user})
230                         logger.info("Logged in successfully")
231                 }catch(err){
232                         s.emit('login', {error: err.message})
233                         logger.error(`Autologin failed: ${err.message}`)
234                 }
235         })
236
237         // Function for when there is no autologin
238         s.on("init", async function(){
239                 s.emit('login', {user: s.Deezer.user})
240                 logger.info("Not logged in")
241         })
242
243         // Function for logout
244         s.on("logout", function(){
245                 logger.info("Logged out")
246                 // Creating new object to clear the cookies
247                 s.Deezer = new deezerApi()
248                 return
249         })
250
251         // Returns list of charts available
252         s.on("getChartsCountryList", async function (data) {
253                 try{
254                         let charts = await s.Deezer.legacyGetChartsTopCountry()
255                         charts = charts.data || []
256                         charts.sort((a, b) => a.title.localeCompare(b.title));
257                         let countries = []
258                         for (let i = 0; i < charts.length; i++) {
259                                 let obj = {
260                                         country: charts[i].title.replace("Top ", ""),
261                                         picture_small: charts[i].picture_small,
262                                         picture_medium: charts[i].picture_medium,
263                                         picture_big: charts[i].picture_big,
264                                         playlistId: charts[i].id
265                                 }
266                                 countries.push(obj)
267                         }
268                         s.emit("getChartsCountryList", {countries: countries, selected: data.selected})
269                 }catch(err){
270                         logger.error(`getChartsCountryList failed: ${err.stack}`)
271                         return
272                 }
273         })
274
275         // Returns chart tracks from Playlist ID
276         async function getChartsTrackListById(playlistId){
277                 if (typeof playlistId === 'undefined') {
278                         s.emit("getChartsTrackListByCountry", {err: "Can't find that playlist"})
279                         return
280                 }
281                 try{
282                         let tracks = await s.Deezer.legacyGetPlaylistTracks(playlistId)
283                         s.emit("getChartsTrackListByCountry", {
284                                 playlistId: playlistId,
285                                 tracks: tracks.data
286                         })
287                 }catch(err){
288                         s.emit("getChartsTrackListByCountry", {err: err})
289                         logger.error(`getChartsTrackListById failed: ${err.stack}`)
290                         return
291                 }
292         }
293
294         // Returns chart tracks from country name
295         async function getChartsTrackListByCountry(country){
296                 if (typeof country === 'undefined') {
297                         s.emit("getChartsTrackListByCountry", {err: "No country passed"})
298                         return
299                 }
300                 try{
301                         let charts = await s.Deezer.legacyGetChartsTopCountry()
302                         charts = charts.data || []
303                         let countries = []
304                         for (let i = 0; i < charts.length; i++) {
305                                 countries.push(charts[i].title.replace("Top ", ""))
306                         }
307                         if (countries.indexOf(country) == -1) {
308                                 s.emit("getChartsTrackListByCountry", {err: "Country not found"})
309                                 return
310                         }
311                         let playlistId = charts[countries.indexOf(country)].id
312                         await getChartsTrackListById(playlistId)
313                 }catch(err){
314                         logger.error(`getChartsTrackListByCountry failed: ${err.stack}`)
315                         return
316                 }
317         }
318         s.on("getChartsTrackListByCountry", function (data) {getChartsTrackListByCountry(data.country)})
319
320         // Returns list of playlists
321         async function getMyPlaylistList(spotUser=null){
322                 if (spotUser && s.spotifyUser != spotUser) s.spotifyUser = spotUser
323                 try{
324                         logger.info("Loading Personal Playlists")
325                         let playlists = []
326                         if (s.Deezer.user.id != 0){
327                                 let data = await s.Deezer.legacyGetUserPlaylists(s.Deezer.user.id)
328                                 data = data.data || []
329                                 for (let i = 0; i < data.length; i++) {
330                                         let obj = {
331                                                 title: data[i].title,
332                                                 image: data[i].picture_small,
333                                                 songs: data[i].nb_tracks,
334                                                 link: data[i].link
335                                         }
336                                         playlists.push(obj)
337                                 }
338                         }
339                         if (s.spotifyUser && spotifySupport){
340                                 try{
341                                         let creds = await Spotify.clientCredentialsGrant()
342                                         Spotify.setAccessToken(creds.body['access_token'])
343                                         let first = true
344                                         let offset = 0
345                                         do{
346                                                 let data = await Spotify.getUserPlaylists(s.spotifyUser, {fields: "items(images,name,owner.id,tracks.total,uri),total", offset: offset*20})
347                                                 if (first){
348                                                         var total = data.body.total
349                                                         var numPages=Math.floor((total-1)/20)
350                                                         var playlistList = new Array(total)
351                                                         first = false
352                                                 }
353                                                 data.body.items.forEach((playlist, i) => {
354                                                         playlistList[(offset*20)+i] = {
355                                                                 title: playlist.name,
356                                                                 image: (playlist.images[0] ? playlist.images[0].url : ""),
357                                                                 songs: playlist.tracks.total,
358                                                                 link: playlist.uri,
359                                                                 spotify: true
360                                                         }
361                                                 })
362                                                 offset++
363                                         }while(offset<=numPages)
364                                         playlists = playlists.concat(playlistList)
365                                 }catch(err){
366                                         logger.error(`Spotify playlist failed loading: ${err}`)
367                                 }
368                         }
369                         logger.info(`Loaded ${playlists.length} Playlist${playlists.length>1 ? "s" : ""}`)
370                         s.emit("getMyPlaylistList", {playlists: playlists})
371                 }catch(err){
372                         logger.error(`getMyPlaylistList failed: ${err}`)
373                         return
374                 }
375         }
376         s.on("getMyPlaylistList", function (d) {getMyPlaylistList(d.spotifyUser)})
377
378         // Returns search results from a query
379         async function search(type, text){
380                 type = type || "track"
381                 if (["track", "playlist", "album", "artist"].indexOf(type) == -1) type = "track"
382
383                 // Remove "feat." "ft." and "&" (causes only problems)
384                 text = text
385                         .replace(/ feat[\.]? /g, " ")
386                         .replace(/ ft[\.]? /g, " ")
387                         .replace(/\(feat[\.]? /g, " ")
388                         .replace(/\(ft[\.]? /g, " ")
389                         .replace(/\&/g, "")
390                         .replace(/–/g, "-")
391                         .replace(/—/g, "-")
392
393                 try {
394                         let searchObject = await s.Deezer.legacySearch(encodeURIComponent(text), type)
395                         return {type: type, items: searchObject.data}
396                 } catch (err) {
397                         logger.error(`search failed: ${err.stack}`)
398                         return {type: type, items: []}
399                 }
400         }
401         s.on("search", async function (data) {
402                 s.emit("search", await search(data.type, data.text))
403         })
404
405         // Returns list of tracks from an album/playlist or the list of albums from an artist
406         s.on("getTrackList", async function (data) {
407                 if (!data.type || (["playlist", "album", "artist", "spotifyplaylist"].indexOf(data.type) == -1) || !data.id) {
408                         s.emit("getTrackList", {err: -1, response: {}, id: data.id, reqType: data.type})
409                         return
410                 }
411                 if (data.type == 'artist') {
412                         try{
413                                 let response = await s.Deezer.legacyGetArtist(data.id)
414                                 let tracks = await s.Deezer.legacyGetArtistAlbums(data.id)
415                                 response.data = tracks.data
416                                 s.emit("getTrackList", {response: response, id: data.id, reqType: data.type})
417                         }catch(err){
418                                 s.emit("getTrackList", {err: "wrong artist id", response: {}, id: data.id, reqType: data.type})
419                                 logger.error(`getTrackList failed: ${err.stack}`)
420                                 return
421                         }
422                 }else if(data.type == "spotifyplaylist" && spotifySupport){
423                         try{
424                                 let creds = await Spotify.clientCredentialsGrant()
425                                 Spotify.setAccessToken(creds.body.access_token)
426                                 let first = true
427                                 let offset = 0
428                                 var response = {}
429                                 let resp0 = await Spotify.getPlaylist(data.id, {fields: "images,name,owner"})
430                                 response.title = resp0.body.name
431                                 response.image = resp0.body.images[0].url
432                                 response.owner = resp0.body.owner.display_name
433                                 do{
434                                         let resp = await Spotify.getPlaylistTracks(data.id, {fields: "items(track(artists,name,duration_ms,preview_url,explicit,uri)),total", offset: offset*100})
435                                         if (first){
436                                                 var numPages=Math.floor((resp.body.total-1)/100)
437                                                 response.data = new Array(resp.body.total)
438                                                 first = false
439                                         }
440                                         resp.body.items.forEach((t, index) => {
441                                                 response.data[index+offset*100]={
442                                                         explicit_lyrics: t.track.explicit,
443                                                         preview: t.track.preview_url,
444                                                         title: t.track.name,
445                                                         artist: {
446                                                                 name: t.track.artists[0].name
447                                                         },
448                                                         duration: Math.floor(t.track.duration_ms/1000),
449                                                         link: t.track.uri
450                                                 }
451                                         })
452                                         offset++
453                                 }while(offset<=numPages)
454                                 s.emit("getTrackList", {response: response, id: data.id, reqType: data.type})
455                         }catch(err){
456                                 logger.error(`getTrackList failed: ${err.stack}`)
457                         }
458                 }else{
459                         let reqType = data.type.charAt(0).toUpperCase() + data.type.slice(1)
460                         try{
461                                 let response = await s.Deezer["legacyGet" + reqType](data.id)
462                                 let tracks = await s.Deezer["legacyGet" + reqType + "Tracks"](data.id)
463                                 response.data = tracks.data
464                                 s.emit("getTrackList", {response: response, id: data.id, reqType: data.type})
465                         }catch(err){
466                                 s.emit("getTrackList", {err: "wrong id "+reqType, response: {}, id: data.id, reqType: data.type})
467                                 logger.error(`getTrackList failed: ${err.stack}`)
468                                 return
469                         }
470                 }
471         })
472
473         // Sends settings saved by the user to the frontend
474         s.on("getUserSettings", function () {
475                 let settings = configFile.userDefined
476                 if (!settings.downloadLocation) {
477                         settings.downloadLocation = mainFolder
478                 }
479                 s.emit('getUserSettings', {settings: settings, spotify: spotifySupport ? authCredentials : {clientId:"", clientSecret:""}})
480         });
481
482         // Saves locally the settings comming from the frontend
483         s.on("saveSettings", function (settings, spotifyUser) {
484                 if (settings.userDefined.downloadLocation == defaultDownloadFolder) {
485                         settings.userDefined.downloadLocation = ""
486                 } else {
487                         settings.userDefined.downloadLocation = path.resolve(settings.userDefined.downloadLocation + path.sep) + path.sep
488                         mainFolder = settings.userDefined.downloadLocation
489                 }
490
491                 if (settings.userDefined.queueConcurrency < 1) settings.userDefined.queueConcurrency = 1
492
493                 if (settings.userDefined.queueConcurrency != trackQueue.concurrency){
494                         trackQueue.concurrency = settings.userDefined.queueConcurrency
495                 }
496
497                 if (spotifyUser != s.spotifyUser){
498                         s.spotifyUser = spotifyUser
499                         getMyPlaylistList(spotifyUser)
500                 }
501
502                 configFile.userDefined = settings.userDefined;
503                 fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
504                         if (err) return
505                         logger.info("Settings updated")
506                         initFolders()
507                 });
508         });
509
510         // Save spotify features settings
511         s.on("saveSpotifyFeatures", function (settings){
512                 if (spotifySupport){
513                         if (authCredentials.clientId != settings.clientId || authCredentials.clientSecret != settings.clientSecret){
514                                 fs.outputFile(localpaths.user+"authCredentials.json", JSON.stringify(settings, null, 2), function (err) {
515                                         if (err) return
516                                         logger.info("Spotify Features settings updated")
517                                         initFolders()
518                                 })
519                         }
520                 }else{
521                         if (settings.clientId != "" || settings.clientSecret != ""){
522                                 fs.outputFile(localpaths.user+"authCredentials.json", JSON.stringify(settings, null, 2), function (err) {
523                                         if (err) return
524                                         logger.info("Spotify Features settings updated")
525                                         initFolders()
526                                 })
527                                 s.emit("message", {title: req.__("You need to restart the app to apply the changes"), msg: req.__("Changing the spotify settings id and secret requires an app restart")})
528                         }
529                 }
530         })
531
532         s.on("analyzetrack", async (id)=>{
533                 s.emit("analyzetrack", await s.Deezer.legacyGetTrack(id))
534         })
535
536         s.on("analyzealbum", async (id)=>{
537                 s.emit("analyzealbum", await s.Deezer.legacyGetAlbum(id))
538         })
539
540         /*
541          * Downloading section of the app
542         */
543
544         // Gets data from the frontend and creates the track object
545         async function downloadTrack(data){
546                 logger.info(`Added to Queue ${data.id}`)
547                 try{
548                         var track = await s.Deezer.getTrack(data.id)
549                         data.settings.filename = data.settings.trackNameTemplate
550                         let _track = {
551                                 name: track.title,
552                                 artist: track.artist.name,
553                                 size: 1,
554                                 downloaded: 0,
555                                 failed: 0,
556                                 queueId: `id${Math.random().toString(36).substring(2)}`,
557                                 id: `${track.id}:${data.bitrate}`,
558                                 bitrate: data.bitrate+"",
559                                 type: 'track',
560                                 settings: data.settings || {},
561                                 obj: track,
562                                 cover: `${s.Deezer.albumPicturesHost}${track.album.picture}/250x250-000000-80-0-0.jpg`,
563                         }
564                         addToQueue(_track)
565                 }catch(err){
566                         logger.error(`downloadTrack failed: ${err.stack ? err.stack : err}`)
567                         message = ""
568                         if (err.message){
569                                 switch (err.message){
570                                         case "DataException: no data":
571                                                 message = "Not Found"
572                                         break
573                                         default:
574                                                 message = err.message
575                                         break
576                                 }
577                         }
578                         s.emit("toast", `Track ${data.id} download failed: ${message}`)
579                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
580                         return
581                 }
582         }
583         s.on("downloadtrack", async data=>{await downloadTrack(data)})
584
585         // Gets data from the frontend and creates the album object
586         async function downloadAlbum(data){
587                 logger.info(`Added to Queue ${data.id}`)
588                 try{
589                         var album = await s.Deezer.legacyGetAlbum(data.id)
590                         if (data.settings.tags.discTotal || data.settings.createCDFolder){
591                                 var discTotal = await s.Deezer.getAlbum(data.id)
592                                 album.discTotal = discTotal.discTotal
593                         }
594                         if (album.nb_tracks == 1 && data.settings.downloadSinglesAsTracks){
595                                 var track = await s.Deezer.getTrack(album.tracks.data[0].id)
596                                 data.settings.filename = data.settings.trackNameTemplate
597                                 let _track = {
598                                         name: track.title,
599                                         artist: track.artist.name,
600                                         size: 1,
601                                         downloaded: 0,
602                                         failed: 0,
603                                         queueId: `id${Math.random().toString(36).substring(2)}`,
604                                         id: `${track.id}:${data.bitrate}`,
605                                         urlId: data.id,
606                                         bitrate: data.bitrate+"",
607                                         type: 'track',
608                                         settings: data.settings || {},
609                                         obj: track,
610                                         cover: `${s.Deezer.albumPicturesHost}${track.album.picture}/250x250-000000-80-0-0.jpg`,
611                                 }
612                                 addToQueue(_track)
613                         }else{
614                                 album.tracks = await s.Deezer.getAlbumTracks(data.id)
615                                 data.settings.filename = data.settings.albumTrackNameTemplate
616                                 let _album = {
617                                         name: album.title,
618                                         artist: album.artist.name,
619                                         size: album.tracks.length,
620                                         downloaded: 0,
621                                         failed: 0,
622                                         queueId: `id${Math.random().toString(36).substring(2)}`,
623                                         id: `${album.id}:${data.bitrate}`,
624                                         bitrate: data.bitrate+"",
625                                         type: 'album',
626                                         settings: data.settings || {},
627                                         obj: album,
628                                         cover: album.cover_medium,
629                                 }
630                                 addToQueue(_album)
631                         }
632                         return
633                 }catch(err){
634                         logger.error(`downloadAlbum failed: ${err.stack ? err.stack : err}`)
635                         message = ""
636                         if (err.message){
637                                 switch (err.message){
638                                         case "DataException: no data":
639                                                 message = "Not Found"
640                                         break
641                                         default:
642                                                 message = err.message
643                                         break
644                                 }
645                         }
646                         s.emit("toast", `Album ${data.id} download failed: ${message}`)
647                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
648                         return
649                 }
650         }
651         s.on("downloadalbum", async data=>{await downloadAlbum(data)});
652
653         // Gets data from the frontend and creates for each album an album object
654         async function downloadArtist(data){
655                 logger.info(`Added to Queue ${data.id}`)
656                 try{
657                         var albums = await s.Deezer.legacyGetArtistAlbums(data.id);
658                         (function sendAllAlbums(i) {
659                                 setTimeout(function () {
660                                         data.id = albums.data[albums.data.length-1-i].id
661                                         downloadAlbum(JSON.parse(JSON.stringify(data)))
662                                         if (--i+1) sendAllAlbums(i)
663                                 }, 100)
664                         })(albums.data.length-1)
665                 }catch(err){
666                         logger.error(`downloadArtist failed: ${err.stack ? err.stack : err}`)
667                         message = ""
668                         if (err.message){
669                                 switch (err.message){
670                                         case "DataException: no data":
671                                                 message = "Not Found"
672                                         break
673                                         default:
674                                                 message = err.message
675                                         break
676                                 }
677                         }
678                         s.emit("toast", `Artist ${data.id} download failed: ${message}`)
679                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
680                         return
681                 }
682         }
683         s.on("downloadartist", async data=>{ await downloadArtist(data)});
684
685         // Gets data from the frontend and creates the playlist object
686         async function downloadPlaylist(data){
687                 logger.info(`Added to Queue ${data.id}`)
688                 try{
689                         var playlist = await s.Deezer.legacyGetPlaylist(data.id)
690                         data.settings.filename = data.settings.playlistTrackNameTemplate
691                         playlist.tracks = await s.Deezer.getPlaylistTracks(data.id)
692                         let _playlist = {
693                                 name: playlist.title,
694                                 artist: playlist.creator.name,
695                                 artistId: playlist.creator.id,
696                                 size: playlist.tracks.length,
697                                 downloaded: 0,
698                                 failed: 0,
699                                 queueId: `id${Math.random().toString(36).substring(2)}`,
700                                 id: `${playlist.id}:${data.bitrate}`,
701                                 bitrate: data.bitrate+"",
702                                 type: "playlist",
703                                 settings: data.settings || {},
704                                 obj: playlist,
705                                 cover: playlist.picture_medium,
706                         }
707                         addToQueue(_playlist)
708                 }catch(err){
709                         logger.error(`downloadPlaylist failed: ${err.stack ? err.stack : err}`)
710                         message = ""
711                         if (err.message){
712                                 switch (err.message){
713                                         case "DataException: no data":
714                                                 message = "Not Found"
715                                         break
716                                         default:
717                                                 message = err.message
718                                         break
719                                 }
720                         }
721                         s.emit("toast", `Playlist ${data.id} download failed: ${message}`)
722                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
723                         return
724                 }
725         }
726         s.on("downloadplaylist", data=>{downloadPlaylist(data)});
727
728         // Gets data from the frontend and creates the object fot the artist top tracks
729         async function downloadArtistTop(data){
730                 logger.info(`Added to Queue ${data.id}`)
731                 try{
732                         var artist = await s.Deezer.legacyGetArtist(data.id)
733                         data.settings.filename = data.settings.playlistTrackNameTemplate
734                         artist.tracks = await s.Deezer.getArtistTopTracks(data.id)
735                         let _playlist = {
736                                 name: artist.name + " Most played tracks",
737                                 artist: artist.name,
738                                 artistId: artist.id,
739                                 size: artist.tracks.length,
740                                 downloaded: 0,
741                                 failed: 0,
742                                 queueId: `id${Math.random().toString(36).substring(2)}`,
743                                 id: `${artist.id}:${data.bitrate}`,
744                                 bitrate: data.bitrate+"",
745                                 type: "playlist",
746                                 settings: data.settings || {},
747                                 obj: artist,
748                                 cover: artist.picture_medium,
749                         }
750                         addToQueue(_playlist)
751                 }catch(err){
752                         logger.error(`downloadArtistTop failed: ${err.stack ? err.stack : err}`)
753                         message = ""
754                         if (err.message){
755                                 switch (err.message){
756                                         case "DataException: no data":
757                                                 message = "Not Found"
758                                         break
759                                         default:
760                                                 message = err.message
761                                         break
762                                 }
763                         }
764                         s.emit("toast", `ArtistTop ${data.id} download failed: ${message}`)
765                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
766                         return
767                 }
768         }
769         s.on("downloadartisttop", data=>{downloadArtistTop(data)});
770
771         // Gets data from the frontend and creates the spotify playlist object
772         async function downloadSpotifyPlaylist(data){
773                 logger.info(`Added to Queue ${data.id}`)
774                 if (spotifySupport){
775                         try{
776                                 let creds = await Spotify.clientCredentialsGrant()
777                                 Spotify.setAccessToken(creds.body['access_token'])
778                                 var offset = 0
779                                 data.settings.filename = data.settings.playlistTrackNameTemplate
780                                 var resp = await Spotify.getPlaylist(data.id, {fields: "id,name,owner,images,tracks(total)"})
781                                 var _playlist = {
782                                         name: resp.body.name,
783                                         artist: (resp.body.owner.display_name ? resp.body.owner.display_name : resp.body.owner.id),
784                                         artistId: resp.body.owner.id,
785                                         size: resp.body.tracks.total,
786                                         downloaded: 0,
787                                         failed: 0,
788                                         queueId: `id${Math.random().toString(36).substring(2)}`,
789                                         settings: data.settings || {},
790                                         id: `${resp.body.id}:${data.bitrate}`,
791                                         bitrate: data.bitrate+"",
792                                         type: "spotifyplaylist",
793                                         obj: resp.body,
794                                         cover: resp.body.images[0].url,
795                                 }
796                                 var numPages=Math.floor((_playlist.size-1)/100)
797                                 var trackList = new Array(_playlist.size)
798                                 var creationDate = ""
799                                 do{
800                                         var resp = await Spotify.getPlaylistTracks(data.id, {fields: "items(track(artists,name,album,external_ids),added_at)", offset: offset*100})
801                                         resp.body.items.forEach((track, i) => {
802                                                 if (creationDate === "")
803                                                         creationDate = track.added_at
804                                                 if (Date.parse(track.added_at) < Date.parse(creationDate))
805                                                         creationDate = track.added_at
806                                                 trackList[i+(offset*100)] = track.track
807                                         })
808                                         offset++
809                                 }while(offset<=numPages)
810                                 _playlist.obj.tracks = trackList
811                                 _playlist.obj.creation_date = creationDate
812                                 addToQueue(_playlist)
813                         }catch(err){
814                                 logger.error(`downloadSpotifyPlaylist failed: ${err.stack ? err.stack : err}`)
815                                 if (err.message && err.message == "Bad Request"){
816                                         s.emit("message", {title: req.__("You setted it up wrong!"), msg: req.__("Somehow you managed to fuck it up. Good job.<br>Now go do the guide again.<br><br>If you need the link again <a href=\"https://notabug.org/RemixDevs/DeezloaderRemix/wiki/Spotify+Features\">Here it is</a>")})
817                                 }else{
818                                         s.emit("toast", `SpotifyPlaylist ${data.id} failed: ${err.message ? err.message : err}`)
819                                 }
820                                 s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
821                                 return
822                         }
823                 }else{
824                         s.emit("message", {title: req.__("Spotify Features is not enabled"), msg: req.__("spotifyFeaturesMessage")})
825                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
826                 }
827         }
828         s.on("downloadspotifyplaylist", data=>{downloadSpotifyPlaylist(data)})
829
830         // Gets data from the frontend and creates data for the deezer track object
831         async function downloadSpotifyTrack(data){
832                 logger.info(`Added to Queue ${data.id}`)
833                 if (spotifySupport){
834                         try{
835                                 let creds = await Spotify.clientCredentialsGrant()
836                                 Spotify.setAccessToken(creds.body['access_token'])
837                                 var resp = await Spotify.getTrack(data.id, {fields: "external_ids,artists,album,name"})
838                                 deezerId = await convertSpotify2Deezer(resp.body)
839                                 if (deezerId != 0){
840                                         data.id = deezerId
841                                         downloadTrack(data)
842                                 }else{
843                                         s.emit("toast", req.__("Can't find the track on Deezer!"))
844                                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
845                                         logger.error(`Can't find the track on Deezer!`)
846                                 }
847                         }catch(err){
848                                 logger.error(`downloadSpotifyTrack failed: ${err.stack ? err.stack : err}`)
849                                 return
850                         }
851                 }else{
852                         s.emit("message", {title: req.__("Spotify Features is not enabled"), msg: req.__("spotifyFeaturesMessage")})
853                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
854                 }
855         }
856         s.on("downloadspotifytrack", data=>{downloadSpotifyTrack(data)})
857
858         // Gets data from the frontend and creates data for the deezer track object
859         async function downloadSpotifyAlbum(data){
860                 logger.info(`Added to Queue ${data.id}`)
861                 if (spotifySupport){
862                         try{
863                                 let creds = await Spotify.clientCredentialsGrant()
864                                 Spotify.setAccessToken(creds.body['access_token'])
865                                 var resp = await Spotify.getAlbum(data.id, {fields: "external_ids,artists,name"})
866                                 deezerId = await convertSpotifyAlbum2Deezer(resp.body)
867                                 if (deezerId != 0){
868                                         data.id = deezerId
869                                         downloadAlbum(data)
870                                 }else{
871                                         s.emit("toast", req.__("Can't find the album on Deezer!"))
872                                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
873                                         logger.error(`Can't find the album on Deezer!`)
874                                 }
875                         }catch(err){
876                                 logger.error(`downloadSpotifyAlbum failed: ${err.stack ? err.stack : err}`)
877                                 return
878                         }
879                 }else{
880                         s.emit("message", {title: req.__("Spotify Features is not enabled"), msg: req.__("spotifyFeaturesMessage")})
881                         s.emit("silentlyCancelDownload", `${data.id}:${data.bitrate}`)
882                 }
883         }
884         s.on("downloadspotifyalbum", data=>{downloadSpotifyAlbum(data)})
885
886         // Converts the spotify track to a deezer one
887         // It tries first with the isrc (best way of conversion)
888         // Fallbacks to the old way, using search
889         async function convertSpotify2Deezer(track){
890                 if (!track) return 0
891                 try{
892                         if (track.external_ids.isrc){
893                                 let resp = await s.Deezer.legacyGetTrackByISRC(track.external_ids.isrc)
894                                 if (resp.title)
895                                         return resp.id
896                                 else
897                                         logger.warn("ISRC track is not on Deezer, falling back to old method")
898                         }
899                 }catch(err){
900                         logger.warn("ISRC not found, falling back to old method")
901                 }
902                 return convertMetadata2Deezer(track.artists[0].name, track.name, track.album.name)
903         }
904
905         // Tries to get track id from pure luck
906         async function convertMetadata2Deezer(artist, track, album){
907                 let resp
908                 artist = artist.replace(/–/g,"-").replace(/’/g, "'")
909                 track = track.replace(/–/g,"-").replace(/’/g, "'")
910                 album = album.replace(/–/g,"-").replace(/’/g, "'")
911                 try{
912                         resp = await s.Deezer.legacySearch(`artist:"${artist}" track:"${track}" album:"${album}"`, "track", 1)
913                 }catch(err){logger.err(`ConvertFromMetadata: ${err.stack ? err.stack : err}`)}
914                 if (resp.data[0]) return resp.data[0].id
915                 try{
916                         resp = await s.Deezer.legacySearch(encodeURIComponent(`artist:"${artist}" track:"${track}"`), "track", 1)
917                 }catch(err){logger.err(`ConvertFromMetadata: ${err.stack ? err.stack : err}`)}
918                 if (resp.data[0]) return resp.data[0].id
919                 if (track.indexOf("(") < track.indexOf(")")){
920                         try{
921                                 resp = await s.Deezer.legacySearch(encodeURIComponent(`artist:"${artist}" track:"${track.split("(")[0]}"`), "track", 1)
922                         }catch(err){logger.err(`ConvertFromMetadata: ${err.stack ? err.stack : err}`)}
923                         if (resp.data[0]) return resp.data[0].id
924                 }else if (track.indexOf(" - ")>0){
925                         try{
926                                 resp = await s.Deezer.legacySearch(encodeURIComponent(`artist:"${artist}" track:"${track.split(" - ")[0]}"`), "track", 1)
927                         }catch(err){logger.err(`ConvertFromMetadata: ${err.stack ? err.stack : err}`)}
928                         if (resp.data[0]) return resp.data[0].id
929                 }else{
930                         return 0
931                 }
932                 return 0
933         }
934
935         // Converts the spotify album to a deezer one
936         // It tries first with the upc (best way of conversion)
937         // Fallbacks to the old way, using search
938         async function convertSpotifyAlbum2Deezer(album){
939                 if (!album) return 0
940                 try{
941                         if (album.external_ids.upc){
942                                 if (! isNaN(album.external_ids.upc)) album.external_ids.upc = parseInt(album.external_ids.upc)
943                                 let resp = await s.Deezer.legacyGetAlbumByUPC(album.external_ids.upc)
944                                 if (resp.title)
945                                         return resp.id
946                                 else
947                                         logger.warn("UPC album is not on Deezer, falling back to old method")
948                         }
949                 }catch(err){
950                         logger.warn("UPC not found, falling back to old method")
951                 }
952                 return convertAlbumMetadata2Deezer(album.artists[0].name, album.name)
953         }
954
955         // Tries to get album id from pure luck
956         async function convertAlbumMetadata2Deezer(artist, album){
957                 let resp
958                 artist = artist.replace(/–/g,"-").replace(/’/g, "'")
959                 album = album.replace(/–/g,"-").replace(/’/g, "'")
960                 try{
961                         resp = await s.Deezer.legacySearch(`artist:"${artist}" album:"${album}"`, "album", 1)
962                 }catch(err){logger.err(`ConvertAlbumFromMetadata: ${err.stack ? err.stack : err}`)}
963                 if (resp.data[0]) return resp.data[0].id
964                 return 0
965         }
966
967         // All the above functions call this function
968         // It adds the object to an array and adds the promise for the download to the object itself
969         function addToQueue(object) {
970                 downloadQueue[object.queueId] = object
971                 io.sockets.emit('addToQueue', object)
972                 downloadQueue[object.queueId].downloadQueuePromise = dqueue.push(addNextDownload, { args: object })
973         }
974
975         // Wrapper for queue download
976         function addNextDownload(obj, token){
977                 return new Promise(async (resolve, reject) => {
978                         await queueDownload(obj)
979                         resolve()
980                 }).then(() => new Promise((resolve, reject) => {
981                         if (token.cancelled)
982                                 reject()
983                         else
984                                 resolve()
985                 }))
986         }
987
988         // Cancels download
989         // TODO: Might check this one, could be a little buggy
990         function cancelDownload(queueId, cleanAll=false){
991                 if (!queueId) return
992                 let cancel = false
993                 let cancelSuccess
994                 if (downloadQueue[queueId]){
995                         cancel = true;
996                         if (downloadQueue[queueId].downloadQueuePromise) downloadQueue[queueId].downloadQueuePromise.cancel()
997                         if (downloadQueue[Object.keys(downloadQueue)[0]].queueId == queueId) {
998                                 trackQueue = queue({
999                                         autostart: true,
1000                                         concurrency: trackQueue.concurrency
1001                                 })
1002                         }
1003                         delete downloadQueue[queueId]
1004                 }
1005
1006                 if (cancel) {
1007                         io.sockets.emit("cancelDownload", {queueId: queueId, cleanAll: cleanAll});
1008                 }
1009         }
1010         s.on("cancelDownload", function (data) {cancelDownload(data.queueId)});
1011
1012         s.on("cancelAllDownloads", function(data){
1013                 data.queueList.forEach(x=>{
1014                         cancelDownload(x, true);
1015                 })
1016                 io.sockets.emit("cancelAllDownloads")
1017         })
1018
1019         //downloadQueue: the tracks in the queue to be downloaded
1020         //queueId: random number generated when user clicks download on something
1021         async function queueDownload(downloading) {
1022                 if (!downloading) return
1023
1024                 if (downloading.type != "spotifyplaylist"){
1025                         io.sockets.emit("downloadStarted", {queueId: downloading.queueId})
1026                 }
1027
1028                 downloading.errorLog = "";
1029                 downloading.searchedLog = "";
1030
1031                 logger.info(`Registered ${downloading.type}: ${downloading.id} | ${downloading.artist} - ${downloading.name}`);
1032                 switch(downloading.type){
1033                         /*
1034                         * TRACK DOWNLOAD
1035                         */
1036                         case "track":
1037                                 var downloadPromise = new Promise(async (resolve,reject)=>{
1038                                         downloading.settings.singleTrack = true;
1039                                         try{
1040                                                 await downloadTrackObject(downloading.obj, downloading.queueId, downloading.settings)
1041                                                 downloading.downloaded++
1042                                         }catch(err){
1043                                                 logger.error(`[${downloading.obj.artist.name} - ${downloading.obj.title}] ${err}`)
1044                                                 downloading.errorLog += `${downloading.obj.id} | ${downloading.obj.artist.name} - ${downloading.obj.title} | ${err}\r\n`
1045                                                 downloading.failed++
1046                                         }
1047                                         io.sockets.emit("updateQueue", {
1048                                                 name: downloading.name,
1049                                                 artist: downloading.artist,
1050                                                 size: downloading.size,
1051                                                 downloaded: downloading.downloaded,
1052                                                 failed: downloading.failed,
1053                                                 queueId: downloading.queueId,
1054                                                 id: downloading.id,
1055                                                 type: downloading.type,
1056                                                 errorLog: downloading.errorLog,
1057                                                 cover: downloading.cover,
1058                                         })
1059                                         io.sockets.emit("downloadProgress", {
1060                                                 queueId: downloading.queueId,
1061                                                 percentage: 100
1062                                         })
1063                                         resolve()
1064                                 })
1065                                 try{
1066                                         await downloadPromise
1067                                 }catch(err){
1068                                         if (err) logger.error(`queueDownload:track failed: ${err.stack ? err.stack : err}`)
1069                                         logger.info("Downloading Stopped")
1070                                 }
1071                         break
1072                         /*
1073                         * ALBUM DOWNLOAD
1074                         */
1075                         case "album":
1076                                 downloading.settings.albName = downloading.name;
1077                                 downloading.settings.artName = downloading.artist;
1078                                 downloading.playlistArr = Array(downloading.size);
1079                                 downloading.tracksData = Array(downloading.size);
1080                                 let ajson = {
1081                                         artist : downloading.obj.artist,
1082                                         nb_tracks : downloading.obj.nb_tracks,
1083                                         upc : downloading.obj.upc,
1084                                         record_type : downloading.obj.record_type,
1085                                         explicit_lyrics : downloading.obj.explicit_lyrics,
1086                                         label : downloading.obj.label,
1087                                         release_date : downloading.obj.release_date,
1088                                         genres : downloading.obj.genres,
1089                                         discTotal: downloading.obj.discTotal ? downloading.obj.discTotal : null
1090                                 }
1091                                 downloading.downloadPromise = new Promise((resolve,reject)=>{
1092                                         downloading.obj.tracks.every(function (t, index) {
1093                                                 downloading.tracksData[index] = {
1094                                                         artist: t.artist.name,
1095                                                         title: t.title,
1096                                                         progress: 0,
1097                                                 }
1098                                                 trackQueue.push(async cb=>{
1099                                                         if (!downloadQueue[downloading.queueId]) {
1100                                                                 reject()
1101                                                                 return false
1102                                                         }
1103                                                         t.ajson = ajson
1104                                                         t.position = index
1105                                                         logger.info(`Now downloading: ${t.artist.name} - ${t.title}`)
1106                                                         try{
1107                                                                 await downloadTrackObject(t, downloading.queueId, downloading.settings)
1108                                                                 downloading.downloaded++
1109                                                                 downloading.playlistArr[t.playlistData[0]] = t.playlistData[1].split(downloading.filePath)[1]
1110                                                                 if (t.searched) downloading.searchedLog += `${t.artist.name} - ${t.title}\r\n`
1111                                                         }catch(err){
1112                                                                 downloading.failed++
1113                                                                 downloading.errorLog += `${t.id} | ${t.artist.name} - ${t.title} | ${err}\r\n`
1114                                                                 logger.error(`[${t.artist.name} - ${t.title}] ${err}`)
1115                                                         }
1116                                                         /*io.sockets.emit("downloadProgress", {
1117                                                                 queueId: downloading.queueId,
1118                                                                 percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
1119                                                         });*/
1120                                                         io.sockets.emit("updateQueue", {
1121                                                                 name: downloading.name,
1122                                                                 artist: downloading.artist,
1123                                                                 size: downloading.size,
1124                                                                 downloaded: downloading.downloaded,
1125                                                                 failed: downloading.failed,
1126                                                                 queueId: downloading.queueId,
1127                                                                 id: downloading.id,
1128                                                                 type: downloading.type,
1129                                                                 errorLog: downloading.errorLog,
1130                                                                 tracksData: downloading.tracksData,
1131                                                                 cover: downloading.cover
1132                                                         })
1133                                                         if (downloading.downloaded + downloading.failed >= downloading.size) resolve()
1134                                                         cb()
1135                                                 })
1136                                                 return true
1137                                         })
1138                                 })
1139                                 try{
1140                                         await downloading.downloadPromise
1141                                         logger.info("Album finished downloading: "+downloading.name);
1142                                         io.sockets.emit("downloadProgress", {
1143                                                 queueId: downloading.queueId,
1144                                                 percentage: 100
1145                                         });
1146                                         if (downloading.settings.logErrors){
1147                                                 if (downloading.errorLog != ""){
1148                                                         if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1149                                                         fs.writeFileSync(downloading.filePath+"notFound.txt",downloading.errorLog)
1150                                                 }else{
1151                                                         if (fs.existsSync(downloading.filePath+"notFound.txt")) fs.unlinkSync(downloading.filePath+"notFound.txt");
1152                                                 }
1153                                         }
1154                                         if (downloading.settings.logSearched){
1155                                                 if (downloading.searchedLog != ""){
1156                                                         if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1157                                                         fs.writeFileSync(downloading.filePath+"alternativeSongs.txt",downloading.searchedLog)
1158                                                 }else{
1159                                                         if (fs.existsSync(downloading.filePath+"alternativeSongs.txt")) fs.unlinkSync(downloading.filePath+"alternativeSongs.txt");
1160                                                 }
1161                                         }
1162                                         if (downloading.settings.createM3UFile){
1163                                                 let path = ""
1164                                                 if (downloading.settings.changePlaylistName)
1165                                                         path = downloading.filePath + downloading.filePath.match(/([^\/]*)\/*$/)[1]+".m3u8"
1166                                                 else
1167                                                         path = downloading.filePath+"playlist.m3u8"
1168                                                 fs.writeFileSync(path, downloading.playlistArr.join("\r\n"));
1169                                         }
1170                                 }catch(err){
1171                                         if (err) logger.error(`queueDownload:album failed: ${err.stack ? err.stack : err}`)
1172                                         logger.info("Stopping the album queue");
1173                                 }
1174                         break
1175                         /*
1176                         * PLAYLIST DOWNLOAD
1177                         */
1178                         case "playlist":
1179                                 downloading.settings.plName = downloading.name;
1180                                 downloading.playlistArr = Array(downloading.size);
1181                                 downloading.tracksData = Array(downloading.size);
1182                                 date = {
1183                                         day: downloading.obj.creation_date.slice(8,10),
1184                                         month: downloading.obj.creation_date.slice(5,7),
1185                                         year: downloading.obj.creation_date.slice(0, 4),
1186                                         slicedYear: (downloading.settings.dateFormatYear == "2" ? downloading.obj.creation_date.slice(2, 4) : downloading.obj.creation_date.slice(0, 4))
1187                                 }
1188                                 switch (downloading.settings.dateFormat){
1189                                         case "1": dateString = `${date.day}-${date.month}-${date.slicedYear}`; break;
1190                                         case "2": dateString = `${date.month}-${date.day}-${date.slicedYear}`; break;
1191                                         case "3": dateString = `${date.slicedYear}-${date.day}-${date.month}`; break;
1192                                         case "0":default: dateString = `${date.slicedYear}-${date.month}-${date.day}`; break;
1193                                 }
1194                                 downloading.settings.playlist = {
1195                                         title: downloading.name,
1196                                         artist: {
1197                                                 name: downloading.artist,
1198                                                 id: downloading.artistId,
1199                                                 picture: ""
1200                                         },
1201                                         year: date.year,
1202                                         date: dateString,
1203                                         dateObj: date,
1204                                         recordType: "Playlist",
1205                                         label: "",
1206                                         barcode: "None",
1207                                         id: downloading.id.split(":")[0],
1208                                         explicit: false,
1209                                         compilation: true,
1210                                         discTotal: 1,
1211                                         cover: downloading.obj.picture_small.replace("56x56",`${downloading.settings.embeddedArtworkSize}x${downloading.settings.embeddedArtworkSize}`),
1212                                         fullSize: downloading.obj.tracks.length,
1213                                 }
1214                                 downloading.downloadPromise = new Promise((resolve,reject)=>{
1215                                         downloading.obj.tracks.every(function (t, index) {
1216                                                 downloading.tracksData[index] = {
1217                                                         artist: t.artist.name,
1218                                                         title: t.title,
1219                                                         progress: 0,
1220                                                 }
1221                                                 trackQueue.push(async cb=>{
1222                                                         if (!downloadQueue[downloading.queueId]) {
1223                                                                 reject()
1224                                                                 return false
1225                                                         }
1226                                                         try{
1227                                                                 await downloadTrackObject(t, downloading.queueId, downloading.settings)
1228                                                                 downloading.downloaded++
1229                                                                 downloading.playlistArr[t.playlistData[0]] = t.playlistData[1].split(downloading.filePath)[1]
1230                                                                 if (t.searched) downloading.searchedLog += `${t.artist.name} - ${t.title}\r\n`
1231                                                         }catch(err){
1232                                                                 downloading.failed++
1233                                                                 downloading.errorLog += `${t.id} | ${t.artist.name} - ${t.title} | ${err}\r\n`
1234                                                                 logger.error(`[${t.artist.name} - ${t.title}] ${err}`)
1235                                                         }
1236                                                         /*io.sockets.emit("downloadProgress", {
1237                                                                 queueId: downloading.queueId,
1238                                                                 percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
1239                                                         });*/
1240                                                         io.sockets.emit("updateQueue", {
1241                                                                 name: downloading.name,
1242                                                                 artist: downloading.artist,
1243                                                                 size: downloading.size,
1244                                                                 downloaded: downloading.downloaded,
1245                                                                 failed: downloading.failed,
1246                                                                 queueId: downloading.queueId,
1247                                                                 id: downloading.id,
1248                                                                 type: downloading.type,
1249                                                                 errorLog: downloading.errorLog,
1250                                                                 tracksData: downloading.tracksData,
1251                                                                 cover: downloading.cover
1252                                                         })
1253                                                         if (downloading.downloaded + downloading.failed >= downloading.size) resolve()
1254                                                         cb()
1255                                                 })
1256                                                 return true
1257                                         })
1258                                 })
1259                                 try{
1260                                         await downloading.downloadPromise
1261                                         logger.info("Playlist finished "+downloading.name);
1262                                         io.sockets.emit("downloadProgress", {
1263                                                 queueId: downloading.queueId,
1264                                                 percentage: 100
1265                                         });
1266                                         if (downloading.settings.logErrors){
1267                                                 if (downloading.errorLog != ""){
1268                                                         if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1269                                                         fs.writeFileSync(downloading.filePath+"notFound.txt",downloading.errorLog)
1270                                                 }else{
1271                                                         if (fs.existsSync(downloading.filePath+"notFound.txt")) fs.unlinkSync(downloading.filePath+"notFound.txt");
1272                                                 }
1273                                         }
1274                                         if (downloading.settings.logSearched){
1275                                                 if (downloading.searchedLog != ""){
1276                                                         if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1277                                                         fs.writeFileSync(downloading.filePath+"alternativeSongs.txt",downloading.searchedLog)
1278                                                 }else{
1279                                                         if (fs.existsSync(downloading.filePath+"alternativeSongs.txt")) fs.unlinkSync(downloading.filePath+"alternativeSongs.txt");
1280                                                 }
1281                                         }
1282                                         if (downloading.settings.createM3UFile){
1283                                                 let path = ""
1284                                                 if (downloading.settings.changePlaylistName)
1285                                                         path = downloading.filePath + antiDot(fixName(downloading.name))+".m3u8"
1286                                                 else
1287                                                         path = downloading.filePath+"playlist.m3u8"
1288                                                 fs.writeFileSync(path, downloading.playlistArr.join("\r\n"));
1289                                         }
1290                                         if (downloading.settings.saveArtwork){
1291                                                 if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1292                                                 let imgPath = downloading.filePath + antiDot(settingsRegexAlbum(downloading.settings.playlist, downloading.settings.coverImageTemplate))+(downloading.settings.PNGcovers ? ".png" : ".jpg");
1293                                                 if (downloading.obj.picture_small){
1294                                                         downloading.cover = downloading.obj.picture_small.replace("56x56",`${downloading.settings.localArtworkSize}x${downloading.settings.localArtworkSize}`)
1295                                                         request.get(downloading.cover, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
1296                                                                 if(error){
1297                                                                         logger.error(error.stack);
1298                                                                         return;
1299                                                                 }
1300                                                                 fs.outputFile(imgPath,body,'binary',function(err){
1301                                                                         if(err){
1302                                                                                 logger.error(err.stack);
1303                                                                                 return;
1304                                                                         }
1305                                                                         logger.info(`Cover downloaded for: ${downloading.settings.plName}`)
1306                                                                 })
1307                                                         });
1308                                                 }
1309                                         }
1310                                 }catch(err){
1311                                         if (err) logger.error(`queueDownload:playlist failed: ${err.stack ? err.stack : err}`)
1312                                         logger.info("Stopping the playlist queue")
1313                                 }
1314                         break
1315                         /*
1316                         * SPOTIFY PLAYLIST DOWNLOAD
1317                         */
1318                         case "spotifyplaylist":
1319                                 downloading.settings.plName = downloading.name
1320                                 downloading.playlistArr = Array(downloading.size)
1321                                 downloading.tracksData = Array(downloading.size)
1322                                 downloading.playlistContent = new Array(downloading.size)
1323                                 logger.info("Waiting for all tracks to be converted");
1324                                 const convert = async () =>{
1325                                         await asyncForEach(downloading.obj.tracks, async (t,i)=>{
1326                                                 if (!downloadQueue[downloading.queueId]) return false
1327                                                 try{
1328                                                         downloading.playlistContent[i] = await convertSpotify2Deezer(t)
1329                                                 }catch(err){
1330                                                         logger.error(`queueDownload:spotifyplaylist failed during conversion: ${err.stack ? err.stack : err}`)
1331                                                 }
1332                                         })
1333                                 }
1334                                 await convert()
1335                                 if (!downloadQueue[downloading.queueId]) {
1336                                         logger.info("Stopping the playlist queue")
1337                                         break
1338                                 }
1339                                 downloading.trackList = await s.Deezer.getTracks(downloading.playlistContent)
1340                                 logger.info("All tracks converted, starting download")
1341                                 io.sockets.emit("downloadStarted", {queueId: downloading.queueId})
1342                                 date = {
1343                                         day: downloading.obj.creation_date.slice(8,10),
1344                                         month: downloading.obj.creation_date.slice(5,7),
1345                                         year: downloading.obj.creation_date.slice(0, 4),
1346                                         slicedYear: (downloading.settings.dateFormatYear == "2" ? downloading.obj.creation_date.slice(2, 4) : downloading.obj.creation_date.slice(0, 4))
1347                                 }
1348                                 switch (downloading.settings.dateFormat){
1349                                         case "1": dateString = `${date.day}-${date.month}-${date.slicedYear}`; break;
1350                                         case "2": dateString = `${date.month}-${date.day}-${date.slicedYear}`; break;
1351                                         case "3": dateString = `${date.slicedYear}-${date.day}-${date.month}`; break;
1352                                         case "0":default: dateString = `${date.slicedYear}-${date.month}-${date.day}`; break;
1353                                 }
1354                                 downloading.settings.playlist = {
1355                                         title: downloading.name,
1356                                         artist: {
1357                                                 name: downloading.artist,
1358                                                 id: downloading.artistId,
1359                                                 picture: ""
1360                                         },
1361                                         year: date.year,
1362                                         date: dateString,
1363                                         dateObj: date,
1364                                         recordType: "Playlist",
1365                                         label: "",
1366                                         barcode: "None",
1367                                         id: downloading.id.split(":")[0],
1368                                         explicit: false,
1369                                         compilation: true,
1370                                         discTotal: 1,
1371                                         cover: downloading.obj.images[0].url.replace("56x56",`${downloading.settings.embeddedArtworkSize}x${downloading.settings.embeddedArtworkSize}`),
1372                                         fullSize: downloading.trackList.length,
1373                                 }
1374                                 downloading.downloadPromise = new Promise((resolve,reject)=>{
1375                                         downloading.trackList.every(function (t, index) {
1376                                                 downloading.tracksData[index] = {
1377                                                         artist: t.artist.name,
1378                                                         title: t.title,
1379                                                         progress: 0,
1380                                                 }
1381                                                 trackQueue.push(async cb=>{
1382                                                         if (!downloadQueue[downloading.queueId]) {
1383                                                                 reject()
1384                                                                 return false
1385                                                         }
1386                                                         t.position = index
1387                                                         if (t.id==0 && downloading.obj.tracks[t.position] != null){
1388                                                                 t.title = downloading.obj.tracks[t.position].name
1389                                                                 t.album = {id: 0, title: downloading.obj.tracks[t.position].album.name}
1390                                                                 t.artist = {id: 0, name: downloading.obj.tracks[t.position].artists[0].name}
1391                                                         }
1392                                                         try{
1393                                                                 await downloadTrackObject(t, downloading.queueId, downloading.settings)
1394                                                                 downloading.downloaded++
1395                                                                 downloading.playlistArr[t.playlistData[0]] = t.playlistData[1].split(downloading.filePath)[1]
1396                                                                 if (t.searched) downloading.searchedLog += `${t.artist.name} - ${t.title}\r\n`
1397                                                         }catch(err){
1398                                                                 downloading.failed++
1399                                                                 downloading.errorLog += `${t.id} | ${t.artist.name} - ${t.title} | ${err}\r\n`
1400                                                                 logger.error(`[${t.artist.name} - ${t.title}] ${err.stack ? err.stack : err}`)
1401                                                         }
1402                                                         /*io.sockets.emit("downloadProgress", {
1403                                                                 queueId: downloading.queueId,
1404                                                                 percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
1405                                                         });*/
1406                                                         io.sockets.emit("updateQueue", {
1407                                                                 name: downloading.name,
1408                                                                 artist: downloading.artist,
1409                                                                 size: downloading.size,
1410                                                                 downloaded: downloading.downloaded,
1411                                                                 failed: downloading.failed,
1412                                                                 queueId: downloading.queueId,
1413                                                                 id: downloading.id,
1414                                                                 type: downloading.type,
1415                                                                 errorLog: downloading.errorLog,
1416                                                                 tracksData: downloading.tracksData,
1417                                                                 cover: downloading.cover
1418                                                         })
1419                                                         if (downloading.downloaded + downloading.failed >= downloading.size) resolve()
1420                                                         cb()
1421                                                 })
1422                                                 return true
1423                                         })
1424                                 })
1425                                 try{
1426                                         await downloading.downloadPromise
1427                                         logger.info("Playlist finished "+downloading.name);
1428                                         io.sockets.emit("downloadProgress", {
1429                                                 queueId: downloading.queueId,
1430                                                 percentage: 100
1431                                         });
1432                                         if (downloading.settings.logErrors){
1433                                                 if (downloading.errorLog != ""){
1434                                                         if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1435                                                         fs.writeFileSync(downloading.filePath+"notFound.txt",downloading.errorLog)
1436                                                 }else{
1437                                                         if (fs.existsSync(downloading.filePath+"notFound.txt")) fs.unlinkSync(downloading.filePath+"notFound.txt");
1438                                                 }
1439                                         }
1440                                         if (downloading.settings.logSearched){
1441                                                 if (downloading.searchedLog != ""){
1442                                                         if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1443                                                         fs.writeFileSync(downloading.filePath+"alternativeSongs.txt",downloading.searchedLog)
1444                                                 }else{
1445                                                         if (fs.existsSync(downloading.filePath+"alternativeSongs.txt")) fs.unlinkSync(downloading.filePath+"alternativeSongs.txt");
1446                                                 }
1447                                         }
1448                                         if (downloading.settings.createM3UFile){
1449                                                 let path = ""
1450                                                 if (downloading.settings.changePlaylistName)
1451                                                         path = downloading.filePath + antiDot(fixName(downloading.name))+".m3u8"
1452                                                 else
1453                                                         path = downloading.filePath+"playlist.m3u8"
1454                                                 fs.writeFileSync(path, downloading.playlistArr.join("\r\n"));
1455                                         }
1456                                         if (downloading.settings.saveArtwork){
1457                                                 if (!fs.existsSync(downloading.filePath)) fs.mkdirpSync(downloading.filePath);
1458                                                 let imgPath = downloading.filePath + antiDot(settingsRegexAlbum(downloading.settings.playlist, downloading.settings.coverImageTemplate))+(downloading.settings.PNGcovers ? ".png" : ".jpg");
1459                                                 if (downloading.obj.images){
1460                                                         downloading.cover = downloading.obj.images[0].url.replace("56x56",`${downloading.settings.localArtworkSize}x${downloading.settings.localArtworkSize}`)
1461                                                         request.get(downloading.cover, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
1462                                                                 if(error){
1463                                                                         logger.error(error.stack);
1464                                                                         return;
1465                                                                 }
1466                                                                 fs.outputFile(imgPath,body,'binary',function(err){
1467                                                                         if(err){
1468                                                                                 logger.error(err.stack);
1469                                                                                 return;
1470                                                                         }
1471                                                                         logger.info(`Cover downloaded for: ${downloading.settings.plName}`)
1472                                                                 })
1473                                                         });
1474                                                 }
1475                                         }
1476                                 }catch(err){
1477                                         if (err) logger.error(`queueDownload:spotifyplaylist failed: ${err.stack ? err.stack : err}`)
1478                                         logger.info("Stopping the playlist queue")
1479                                 }
1480                         break
1481                 }
1482                 if (downloading && downloadQueue[Object.keys(downloadQueue)[0]] && (Object.keys(downloadQueue)[0] == downloading.queueId)) delete downloadQueue[Object.keys(downloadQueue)[0]]
1483                 if (Object.keys(downloadQueue).length == 0) {
1484                         io.sockets.emit("emptyDownloadQueue", {})
1485                 }
1486         }
1487
1488         // This function takes the track object and does all the stuff to download it
1489         async function downloadTrackObject(track, queueId, settings) {
1490                 if (!downloadQueue[queueId]) {
1491                         logger.error(`[${track.artist.name} - ${track.title}] Failed to download: Not in queue`)
1492                         throw new Error("Not in queue")
1493                         return false
1494                 }
1495                 if (parseInt(track.id) == 0){
1496                         logger.error(`[${track.artist.name} - ${track.title}] Failed to download: Song not Found`)
1497                         throw new Error("Song not Found")
1498                         return false
1499                 }
1500
1501                 /* Album information is necessary for the following tags:
1502                  * album_artist
1503                  * album_artist_picture
1504                  * trackTotal
1505                  * recordType
1506                  * barcode
1507                  * explicit
1508                  * label
1509                  * genres
1510                  * date
1511                 */
1512                 if (parseInt(track.id)>0){
1513                         var ajson
1514                         if (!track.ajson){
1515                                 try{
1516                                         logger.info(`[${track.artist.name} - ${track.title}] Getting album info`)
1517                                         ajson = await s.Deezer.legacyGetAlbum(track.album.id)
1518                                 }catch(err){
1519                                         logger.warn(`[${track.artist.name} - ${track.title}] Album not found, trying to reach deeper`)
1520                                         try{
1521                                                 ajson = await s.Deezer.getAlbum(track.album.id)
1522                                                 ajson.fromNewAPI = true
1523                                         } catch(err){
1524                                                 if(track.fallbackId){
1525                                                         logger.warn(`[${track.artist.name} - ${track.title}] Failed to download track, falling on alternative`)
1526                                                         track = await s.Deezer.getTrack(track.fallbackId)
1527                                                         return downloadTrackObject(track, queueId, settings)
1528                                                 }else if(!track.searched && settings.fallbackSearch){
1529                                                         logger.warn(`[${track.artist.name} - ${track.title}] Failed to download track, searching for alternative`)
1530                                                         var _trackID = await convertMetadata2Deezer(track.artist.name, track.title, track.album.title)
1531                                                         if (_trackID != "0"){
1532                                                                 track = await s.Deezer.getTrack()
1533                                                                 track.searched = true
1534                                                                 return downloadTrackObject(track, queueId, settings)
1535                                                         }else{
1536                                                                 logger.error(`[${track.artist.name} - ${track.title}] Failed to download: Alternative not found`)
1537                                                                 return
1538                                                         }
1539                                                 }else{
1540                                                         logger.error(`[${track.artist.name} - ${track.title}] Failed to download: ${err}`)
1541                                                         return
1542                                                 }
1543                                         }
1544                                 }
1545                         }else{
1546                                 ajson = track.ajson
1547                         }
1548                         if (!ajson.fromNewAPI){
1549                                 // Aquiring discTotal (only if necessary)
1550                                 if (settings.tags.discTotal || settings.createCDFolder){
1551                                         if (settings.savePlaylistAsCompilation && settings.plName){
1552                                                 track.album.discTotal = 1
1553                                         }else{
1554                                                 if (!ajson.discTotal){
1555                                                         logger.info(`[${track.artist.name} - ${track.title}] Getting total disc number`);
1556                                                         var discTotal = await s.Deezer.getAlbum(ajson.id)
1557                                                         track.album.discTotal = discTotal.discTotal
1558                                                 }else{
1559                                                         track.album.discTotal = ajson.discTotal
1560                                                 }
1561                                         }
1562                                 }
1563                                 if (settings.savePlaylistAsCompilation && settings.plName){
1564                                         track.album = settings.playlist
1565                                         track.trackNumber = track.position+1
1566                                         if (track.album.dateObj) {
1567                                                 track.date = track.album.dateObj
1568                                         }else if(!track.date){
1569                                                 track.date = {
1570                                                         day: 0,
1571                                                         month: 0,
1572                                                         year: 0,
1573                                                         slicedYear: 0
1574                                                 }
1575                                         }
1576                                 }else{
1577                                         track.album.artist = {
1578                                                 id: ajson.artist.id,
1579                                                 name: ajson.artist.name,
1580                                                 picture: ajson.artist.picture_small.substring(46,ajson.artist.picture_small.length-24),
1581                                         }
1582                                         track.album.trackTotal = ajson.nb_tracks
1583                                         if (ajson.record_type){
1584                                                 track.album.recordType = ajson.record_type
1585                                         }else{
1586                                                 track.album.recordType = switchReleaseType(track.album.recordType)
1587                                         }
1588                                         track.album.barcode = ajson.upc
1589                                         if (ajson.explicit_lyrics)
1590                                                 track.album.explicit = ajson.explicit_lyrics;
1591                                         if(ajson.label)
1592                                                 track.album.label = ajson.label;
1593                                         if (ajson.release_date) {
1594                                                 track.date = {
1595                                                         day: ajson.release_date.slice(8,10),
1596                                                         month: ajson.release_date.slice(5,7),
1597                                                         year: ajson.release_date.slice(0, 4),
1598                                                         slicedYear: (settings.dateFormatYear == "2" ? ajson.release_date.slice(2, 4) : ajson.release_date.slice(0, 4))
1599                                                 }
1600                                         }else if(!track.date){
1601                                                 track.date = {
1602                                                         day: 0,
1603                                                         month: 0,
1604                                                         year: 0,
1605                                                         slicedYear: 0
1606                                                 }
1607                                         }
1608                                 }
1609                                 if(ajson.genres && ajson.genres.data[0] && ajson.genres.data[0].name){
1610                                         track.album.genre = []
1611                                         ajson.genres.data.forEach(function(genre){
1612                                                 if (track.album.genre.indexOf(genre.name) == -1)
1613                                                         track.album.genre.push(genre.name)
1614                                         })
1615                                 }
1616                         }else{
1617                                 // Missing barcode, genre
1618                                 track.album = ajson
1619                                 track.date = track.album.date
1620                                 track.date.slicedYear = (settings.dateFormatYear == "2" ? track.date.year.slice(2, 4) : track.date.year.slice(0, 4))
1621                                 if (settings.savePlaylistAsCompilation && settings.plName){
1622                                         track.album = settings.playlist
1623                                         track.trackNumber = track.position+1
1624                                         if (track.album.dateObj) {
1625                                                 track.date = track.album.dateObj
1626                                         }else if(!track.date){
1627                                                 track.date = {
1628                                                         day: 0,
1629                                                         month: 0,
1630                                                         year: 0,
1631                                                         slicedYear: 0
1632                                                 }
1633                                         }
1634                                 }else{
1635                                         track.trackTotal = track.album.trackTotal
1636                                         track.album.recordType = "Album"
1637                                 }
1638                                 // TODO: Make a loop for each artist
1639                         }
1640
1641                         if (!track.date.slicedYear){
1642                                 track.date.slicedYear = settings.dateFormatYear == "2" ? track.date.year.slice(2, 4) : track.date.year.slice(0, 4)
1643                         }
1644
1645                         // Auto detect aviable track format from settings
1646                         let bitrateNotFound = false
1647                         if (parseInt(downloadQueue[queueId].bitrate) <= 9){
1648                                 switch(downloadQueue[queueId].bitrate.toString()){
1649                                         case "9":
1650                                         track.selectedFormat = 9
1651                                         track.selectedFilesize = track.filesize.flac
1652                                         if (track.filesize.flac>0) break
1653                                         if (!settings.fallbackBitrate){bitrateNotFound = true; break;}
1654                                         case "3":
1655                                         track.selectedFormat = 3
1656                                         track.selectedFilesize = track.filesize.mp3_320
1657                                         if (track.filesize.mp3_320>0) break
1658                                         if (!settings.fallbackBitrate){bitrateNotFound = true; break;}
1659                                         case "1":
1660                                         track.selectedFormat = 1
1661                                         track.selectedFilesize = track.filesize.mp3_128
1662                                         if (track.filesize.mp3_128>0) break
1663                                         if (!settings.fallbackBitrate){bitrateNotFound = true; break;}
1664                                         default:
1665                                         track.selectedFormat = 8
1666                                         track.selectedFilesize = track.filesize.default
1667                                 }
1668                         }else{
1669                                 switch(downloadQueue[queueId].bitrate.toString()){
1670                                         case "15":
1671                                         track.selectedFormat = 15
1672                                         track.selectedFilesize = track.filesize.mp4_ra3
1673                                         if (track.filesize.mp4_ra3>0) break
1674                                         if (!settings.fallbackBitrate){bitrateNotFound = true; break;}
1675                                         case "14":
1676                                         track.selectedFormat = 14
1677                                         track.selectedFilesize = track.filesize.mp4_ra2
1678                                         if (track.filesize.mp4_ra2>0) break
1679                                         if (!settings.fallbackBitrate){bitrateNotFound = true; break;}
1680                                         case "13":
1681                                         track.selectedFormat = 13
1682                                         track.selectedFilesize = track.filesize.mp4_ra1
1683                                         if (track.filesize.mp4_ra1>0) break
1684                                         if (!settings.fallbackBitrate){bitrateNotFound = true; break;}
1685                                         default:
1686                                         throw new Error("Song is not available in 360 mode.")
1687                                 }
1688                         }
1689                         if (bitrateNotFound){
1690                                 if(track.fallbackId && track.fallbackId != "0"){
1691                                         logger.warn(`[${track.artist.name} - ${track.title}] Song not found at desired bitrate, falling on alternative`)
1692                                         var _track = await s.Deezer.getTrack(track.fallbackId)
1693                                         track.id = _track.id
1694                                         track.fallbackId = _track.fallbackId
1695                                         track.filesize = _track.filesize
1696                                         track.duration = _track.duration
1697                                         track.MD5 = _track.MD5
1698                                         track.mediaVersion = _track.mediaVersion
1699                                         return downloadTrackObject(track, queueId, settings)
1700                                 }else if(!track.searched && settings.fallbackSearch){
1701                                         logger.warn(`[${track.artist.name} - ${track.title}] Song not found at desired bitrate, searching for alternative`)
1702                                         _trackId = await convertMetadata2Deezer(track.artist.name, track.title, track.album.title)
1703                                         if (_trackId != "0"){
1704                                                 _track = await s.Deezer.getTrack(_trackId)
1705                                                 track.id = _track.id
1706                                                 track.fallbackId = _track.fallbackId
1707                                                 track.filesize = _track.filesize
1708                                                 track.duration = _track.duration
1709                                                 track.MD5 = _track.MD5
1710                                                 track.mediaVersion = _track.mediaVersion
1711                                                 track.searched = true
1712                                                 return downloadTrackObject(track, queueId, settings)
1713                                         }else{
1714                                                 logger.error(`[${track.artist.name} - ${track.title}] Song not found at desired bitrate and no alternative found`)
1715                                                 throw new Error("Song not found at desired bitrate and no alternative found")
1716                                                 return
1717                                         }
1718                                 }else{
1719                                         logger.error(`[${track.artist.name} - ${track.title}] Downloading error: Song not found at desired bitrate.`)
1720                                         throw new Error("Song not found at desired bitrate.")
1721                                         return
1722                                 }
1723                         }
1724                         track.album.bitrate = track.selectedFormat
1725
1726                         // Acquiring bpm (only if necessary)
1727                         if (settings.tags.bpm){
1728                                 logger.info(`[${track.artist.name} - ${track.title}] Getting BPM`);
1729                                 if (!track.legacyTrack) track.legacyTrack = await s.Deezer.legacyGetTrack(track.id)
1730                                 try{
1731                                         track.bpm = track.legacyTrack.bpm
1732                                 }catch(err){
1733                                         track.bpm = 0
1734                                 }
1735                         }else{
1736                                 track.bpm = 0
1737                         }
1738
1739                         // Acquiring ReplayGain value (only if necessary)
1740                         if (settings.tags.replayGain){
1741                                 logger.info(`[${track.artist.name} - ${track.title}] Getting track gain`);
1742                                 if (!track.legacyTrack) track.legacyTrack = await s.Deezer.legacyGetTrack(track.id)
1743                                 try{
1744                                         track.replayGain = ((track.legacyTrack.gain + 18.4) * -1).toFixed(2)+" dB"
1745                                 }catch(err){
1746                                         track.replayGain = false
1747                                 }
1748                         }else{
1749                                 track.replayGain = false
1750                         }
1751
1752                         // Acquiring discNumber value (only if necessary)
1753                         if (settings.tags.discNumber && !track.discNumber){
1754                                 if (settings.savePlaylistAsCompilation && settings.plName){
1755                                         track.discNumber = 1
1756                                 }else{
1757                                         logger.info(`[${track.artist.name} - ${track.title}] Getting disc number`);
1758                                         if (!track.legacyTrack) track.legacyTrack = await s.Deezer.legacyGetTrack(track.id)
1759                                         track.discNumber = track.legacyTrack.disk_number
1760                                 }
1761                         }
1762
1763                         // Acquiring Explicit tag (only if necessary)
1764                         if (!track.explicit && (settings.tags.explicit || settings.filename.includes("%explicit%"))){
1765                                 logger.info(`[${track.artist.name} - ${track.title}] Is track explicit? Checking now`);
1766                                 if (!track.legacyTrack) track.legacyTrack = await s.Deezer.legacyGetTrack(track.id)
1767                                 track.explicit = track.legacyTrack.explicit_lyrics;
1768                         }
1769
1770
1771                         var separator = settings.multitagSeparator
1772                         if (separator == "null") separator = String.fromCharCode(0)
1773
1774                         // Autoremoves (Album Version) from the title
1775                         if (settings.removeAlbumVersion){
1776                                 if(track.title.indexOf("Album Version")>-1){
1777                                         track.title = track.title.replace(/ ?\(Album Version\)/g,"")
1778                                 }
1779                         }
1780
1781                         // See if you already have the artist picture
1782                         if (!track.album.artist.picture && !(settings.savePlaylistAsCompilation && settings.plName)){
1783                                 if (track.artist.name == track.album.artist.name && !track.album.artist.picture){
1784                                         track.album.artist.picture = track.artist.picture
1785                                 }else{
1786                                         let found = false
1787                                         if (track.artists){
1788                                                 track.artists.forEach(x=>{
1789                                                         if(!found && x.name == track.album.artist.name){
1790                                                                 track.album.artist.picture = x.picture
1791                                                                 found = true
1792                                                         }
1793                                                 })
1794                                         }
1795                                         if(settings.saveArtworkArtist && !found){
1796                                                 artist = await s.Deezer.legacyGetArtist(track.album.artist.id)
1797                                                 track.album.artist.picture = artist.picture_small.substring(46,ajson.artist.picture_small.length-24)
1798                                         }
1799                                 }
1800                         }
1801                         if (!track.album.artist.picture) track.album.artist.picture = ""
1802                         track.album.artist.pictureUrl = `${s.Deezer.artistPicturesHost}${track.album.artist.picture}/${settings.localArtworkSize}x${settings.localArtworkSize}-000000-80-0-0${(settings.PNGcovers ? ".png" : ".jpg")}`
1803                         if (settings.savePlaylistAsCompilation && settings.plName){
1804                                 track.album.pictureUrl = settings.playlist.cover
1805                         }else{
1806                                 track.album.pictureUrl = `${s.Deezer.albumPicturesHost}${track.album.picture}/${settings.embeddedArtworkSize}x${settings.embeddedArtworkSize}-000000-80-0-0${(settings.PNGcovers ? ".png" : ".jpg")}`
1807                         }
1808
1809
1810                         if(track.contributor){
1811                                 if(track.contributor.composer){
1812                                         track.composerString = uniqueArray(track.contributor.composer)
1813                                         if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.composerString = track.composerString.join(separator)
1814                                 }
1815                                 if(track.contributor.musicpublisher){
1816                                         track.musicpublisherString = uniqueArray(track.contributor.musicpublisher)
1817                                         if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.musicpublisherString = track.musicpublisherString.join(separator)
1818                                 }
1819                                 if(track.contributor.producer){
1820                                         track.producerString = uniqueArray(track.contributor.producer)
1821                                         if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.producerString = track.producerString.join(separator)
1822                                 }
1823                                 if(track.contributor.engineer){
1824                                         track.engineerString = uniqueArray(track.contributor.engineer)
1825                                         if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.engineerString = track.engineerString.join(separator)
1826                                 }
1827                                 if(track.contributor.writer){
1828                                         track.writerString = uniqueArray(track.contributor.writer)
1829                                         if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.writerString = track.writerString.join(separator)
1830                                 }
1831                                 if(track.contributor.author){
1832                                         track.authorString = uniqueArray(track.contributor.author)
1833                                         if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.authorString = track.authorString.join(separator)
1834                                 }
1835                                 if(track.contributor.mixer){
1836                                         track.mixerString = uniqueArray(track.contributor.mixer)
1837                                         if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.mixerString = track.mixerString.join(separator)
1838                                 }
1839                         }
1840
1841                         if(track.artists || track.artistsString){
1842                                 if (!track.artistsString){
1843                                         track.artistsString = []
1844                                         artistArray = []
1845                                         track.artists.forEach(function(artist){
1846                                                 artistArray.push(artist.name)
1847                                         })
1848                                 }else{
1849                                         if (! Array.isArray(track.artistsString)){
1850                                                 track.artistsString = [track.artistsString,]
1851                                         }
1852                                         artistArray = track.artistsString
1853                                 }
1854                                 track.artistsString = uniqueArray(artistArray)
1855                                 let posMainArtist = track.artistsString.indexOf(track.album.artist.name)
1856                                 if (posMainArtist !== -1 && posMainArtist !== 0){
1857                                         let element = track.artistsString[posMainArtist]
1858                                         track.artistsString.splice(posMainArtist, 1)
1859                                         track.artistsString.splice(0, 0, element)
1860                                 }
1861                                 track.mainArtist = track.artistsString[0]
1862                                 if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null")) track.artistsString = track.artistsString.join(separator)
1863                         }
1864                         if (track.album.genre){
1865                                 if (!(track.selectedFormat == 9 && settings.multitagSeparator == "null"))
1866                                         track.album.genreString = track.album.genre.join(separator)
1867                                 else
1868                                         track.album.genreString = track.album.genre
1869                         }
1870
1871                         if (track.date){
1872                                 let date
1873                                 switch (settings.dateFormat){
1874                                         case "0": date = `${track.date.slicedYear}-${track.date.month}-${track.date.day}`; break;
1875                                         case "1": date = `${track.date.day}-${track.date.month}-${track.date.slicedYear}`; break;
1876                                         case "2": date = `${track.date.month}-${track.date.day}-${track.date.slicedYear}`; break;
1877                                         case "3": date = `${track.date.slicedYear}-${track.date.day}-${track.date.month}`; break;
1878                                         default: date = `${track.date.slicedYear}-${track.date.month}-${track.date.day}`; break;
1879                                 }
1880                                 track.dateString = date;
1881                                 track.id3dateString = `${track.date.day}${track.date.month}`;
1882                         }
1883                 }else{
1884                         track.date = {year: 0,day: 0,month: 0}
1885                         track.selectedFilesize = track.filesize
1886                         track.selectedFormat = 3
1887                         track.album.bitrate = 3
1888                 }
1889                 track.album.date = track.dateString
1890                 track.album.year = track.date.year
1891
1892                 // TODO: Move to a separate function
1893                 // Generating file name
1894                 let filename = ""
1895                 if (settings.saveFullArtists){
1896                         if (settings.multitagSeparator == "null"){
1897                                 if (Array.isArray(track.artistsString)){
1898                                         filename = antiDot(fixName(`${track.artistsString.join(", ")} - ${track.title}`));
1899                                 }else{
1900                                         filename = antiDot(fixName(`${track.artistsString.split(String.fromCharCode(0)).join(", ")} - ${track.title}`))
1901                                 }
1902                         }else{
1903                                 filename = antiDot(fixName(`${track.artistsString} - ${track.title}`));
1904                         }
1905                 }else{
1906                         filename = antiDot(fixName(`${track.artist.name} - ${track.title}`));
1907                 }
1908                 if (settings.filename) {
1909                         filename = antiDot(fixName(settingsRegex(track, settings.filename, settings.playlist)))
1910                 }
1911
1912                 filename = antiDot(fixName(filename))
1913
1914                 // TODO: Move to a separate function
1915                 // Generating file path
1916                 let filepath = mainFolder;
1917                 let artistPath;
1918                 let coverPath;
1919
1920                 if (settings.createPlaylistFolder && settings.plName && !settings.savePlaylistAsCompilation)
1921                         filepath += antiDot(fixName(settingsRegexPlaylist(settings.playlist, settings.playlistNameTemplate))) + path.sep;
1922
1923                 if (settings.plName && !settings.savePlaylistAsCompilation)
1924                         downloadQueue[queueId].filePath = filepath
1925
1926                 if (
1927                         settings.createArtistFolder && !settings.plName ||
1928                         (settings.createArtistFolder && settings.plName && settings.savePlaylistAsCompilation) ||
1929                         (settings.createArtistFolder && settings.plName && settings.createStructurePlaylist)
1930                 ){
1931                         filepath += antiDot(fixName(settingsRegexArtist(track.album.artist, settings.artistNameTemplate))) + path.sep;
1932                         artistPath = filepath;
1933                 }
1934                 if (settings.createAlbumFolder &&
1935                         (!settings.singleTrack || (settings.singleTrack && settings.createSingleFolder)) &&
1936                         (!settings.plName || (settings.plName && settings.savePlaylistAsCompilation) || (settings.plName && settings.createStructurePlaylist))
1937                 ){
1938                         filepath += antiDot(fixName(settingsRegexAlbum(track.album, settings.albumNameTemplate))) + path.sep;
1939                         coverPath = filepath;
1940                 }
1941
1942                 if(!(settings.plName && settings.createStructurePlaylist && !settings.savePlaylistAsCompilation)) downloadQueue[queueId].filePath = filepath
1943
1944                 if (
1945                         track.album.discTotal > 1 && (
1946                         (settings.createAlbumFolder && settings.createCDFolder) && (!settings.plName ||
1947                         (settings.plName && settings.savePlaylistAsCompilation) ||
1948                         (settings.plName && settings.createStructurePlaylist))
1949                 )){
1950                         filepath += `CD${track.discNumber + path.sep}`
1951                 }
1952
1953                 let writePath;
1954                 if(track.selectedFormat == 9){
1955                         writePath = filepath + filename + '.flac';
1956                 }else if (track.selectedFormat == 13 || track.selectedFormat == 14 || track.selectedFormat == 15){
1957                         writePath = filepath + filename + '.mp4';
1958                 }else{
1959                         writePath = filepath + filename + '.mp3';
1960                 }
1961
1962                 if ((settings.syncedlyrics || settings.tags.unsynchronisedLyrics) && track.lyricsId>0){
1963                         let lyr = await s.Deezer.getLyrics(track.id)
1964                         track.syncLyrics = lyr.syncLyrics
1965                         track.unsyncLyrics = lyr.unsyncLyrics
1966                 }
1967
1968                 if(track.syncLyrics && settings.syncedlyrics){
1969                         fs.outputFile(writePath.substring(0,writePath.lastIndexOf('.'))+".lrc",track.syncLyrics,function(){});
1970                 }
1971
1972                 track.playlistData = [0,""]
1973                 if (settings.createM3UFile && (settings.plName || settings.albName)) {
1974                         if (typeof track.position != 'undefined'){
1975                                 track.playlistData = [parseInt(track.position), writePath];
1976                         }else{
1977                                 track.playlistData = [track.trackNumber-1, writePath];
1978                         }
1979                 }
1980
1981                 if (fs.existsSync(writePath)) {
1982                         logger.info(`[${track.artist.name} - ${track.title}] Already downloaded`);
1983                         if (!downloadQueue[queueId].percentage) {
1984                                 downloadQueue[queueId].percentage = 0
1985                                 downloadQueue[queueId].lastPercentage = 0
1986                         }
1987                         downloadQueue[queueId].percentage += 100/downloadQueue[queueId].size
1988                         if (Math.round(downloadQueue[queueId].percentage) != downloadQueue[queueId].lastPercentage) {
1989                                 if (Math.round(downloadQueue[queueId].percentage) % 5 == 0) {
1990                                         downloadQueue[queueId].lastPercentage = Math.round(downloadQueue[queueId].percentage)
1991                                         io.sockets.emit("downloadProgress", {
1992                                                 queueId: queueId,
1993                                                 percentage: downloadQueue[queueId].lastPercentage
1994                                         })
1995                                 }
1996                         }
1997                         return;
1998                 }else{
1999                         logger.info(`[${track.artist.name} - ${track.title}] Downloading file to ${writePath}`);
2000                 }
2001
2002                 // Get cover image
2003                 if (track.album.pictureUrl) {
2004                         let imgPath;
2005                         imgPath = coverArtFolder + ((settings.savePlaylistAsCompilation && settings.plName) ? fixName(`${track.album.artist.name} - ${track.album.title}`) : track.album.barcode ? fixName(track.album.barcode) : fixName(`${track.album.artist.name} - ${track.album.title}`))+"_"+settings.embeddedArtworkSize+(settings.PNGcovers ? ".png" : ".jpg")
2006                         if(fs.existsSync(imgPath)){
2007                                 track.album.picturePath = (imgPath).replace(/\\/g, "/")
2008                                 logger.info(`[${track.artist.name} - ${track.title}] Cover already downloaded`)
2009                         }else{
2010                                 try{
2011                                         var body = await request.get(track.album.pictureUrl, {strictSSL: false,encoding: 'binary'})
2012                                         fs.outputFileSync(imgPath,body,'binary')
2013                                         track.album.picturePath = (imgPath).replace(/\\/g, "/")
2014                                         logger.info(`[${track.artist.name} - ${track.title}] Cover downloaded!`)
2015                                 }catch(error){
2016                                         logger.error(`[${track.artist.name} - ${track.title}] Cannot download Album Image: ${error}`)
2017                                         logger.error(`Album art link: ${track.album.pictureUrl}`)
2018                                         track.album.pictureUrl = undefined
2019                                         track.album.picturePath = undefined
2020                                 }
2021                         }
2022
2023                         if (settings.saveArtwork && coverPath){
2024                                 imgPath = coverPath + settingsRegexAlbum(track.album, settings.coverImageTemplate)+(settings.PNGcovers ? ".png" : ".jpg")
2025                                 if (!fs.existsSync(coverPath)) fs.mkdirpSync(coverPath);
2026                                 if(!fs.existsSync(imgPath)){
2027                                         try{
2028                                                 var body = await request.get(track.album.pictureUrl.replace(`${settings.embeddedArtworkSize}x${settings.embeddedArtworkSize}`,`${settings.localArtworkSize}x${settings.localArtworkSize}`), {strictSSL: false,encoding: 'binary'})
2029                                                 fs.outputFileSync(imgPath,body,'binary')
2030                                                 logger.info(`[${track.artist.name} - ${track.title}] Local Cover downloaded!`)
2031                                         }catch(error){
2032                                                 logger.error(`[${track.artist.name} - ${track.title}] Cannot download Local Cover: ${error}`)
2033                                         }
2034                                 }
2035                         }
2036                 }else{
2037                         track.album.pictureUrl = undefined
2038                         logger.info(`[${track.artist.name} - ${track.title}] No cover found`)
2039                 }
2040
2041                 // Get Artist Image
2042                 if (parseInt(track.id)>0 && track.album.artist.pictureUrl && settings.saveArtworkArtist) {
2043                         if(settings.createArtistFolder && artistPath){
2044                                 let imgPath = artistPath + antiDot(settingsRegexArtist(track.album.artist, settings.artistImageTemplate))+(settings.PNGcovers ? ".png" : ".jpg");
2045                                 if (!fs.existsSync(artistPath)) fs.mkdirpSync(artistPath);
2046                                 if(!fs.existsSync(imgPath)){
2047                                         try{
2048                                                 var body = await request.get(track.album.artist.pictureUrl, {strictSSL: false,encoding: 'binary'})
2049                                                 if (body.indexOf("unauthorized")>-1) throw new Error("Unauthorized")
2050                                                 fs.outputFileSync(imgPath,body,'binary')
2051                                                 logger.info(`[${track.artist.name} - ${track.title}] Saved Artist Image`)
2052                                         }catch(err){
2053                                                 logger.error(`[${track.artist.name} - ${track.title}] Cannot download Artist Image: ${err}`)
2054                                         }
2055                                 }
2056                         }
2057                 }
2058                 if (!track.MD5)
2059                         track.MD5 = await s.Deezer.getTrackMD5(track.id)
2060                 logger.info(`[${track.artist.name} - ${track.title}] Starting the download process`)
2061                 var downloadingPromise = new Promise((resolve, reject)=>{
2062                         if (!fs.existsSync(`${filepath}`)) fs.mkdirpSync(`${filepath}`)
2063                         let url = new URL(track.getDownloadUrl(track.selectedFormat));
2064                         let options = {
2065                                 host: url.hostname,
2066                                 path: url.pathname,
2067                                 headers: s.Deezer.httpHeaders,
2068                                 rejectUnauthorized: false,
2069                                 strictSSL: false
2070                         }
2071
2072                         let req = https.get(options, function (response) {
2073                                 if (200 === response.statusCode) {
2074                                         const fileStream = fs.createWriteStream(writePath);
2075                                         if (track.id > 0){
2076                                                 if (track.selectedFormat == 9){
2077                                                         var flacBuffer = Buffer.alloc(0);
2078                                                 }else if (track.selectedFormat <= 9){
2079                                                         fileStream.write(getID3(track, settings));
2080                                                 }
2081                                         }
2082
2083                                         let i = 0;
2084                                         let chunkLength = 0;
2085                                         let tagSize = 0;
2086                                         response.on('readable', () => {
2087                                                 const blowFishKey = getBlowfishKey(track.id);
2088                                                 let chunk;
2089                                                 while (chunk = response.read(2048)) {
2090                                                         chunkLength += 2048;
2091
2092                                                         // Progress bar advancement for single tracks
2093                                                         if(downloadQueue[queueId]){
2094                                                                 if (downloadQueue[queueId].type == "track"){
2095                                                                         if (!downloadQueue[queueId]){
2096                                                                                 reject("Not in Queue")
2097                                                                         }
2098                                                                         try{
2099                                                                                 if (!downloadQueue[queueId].percentage) {
2100                                                                                         downloadQueue[queueId].percentage = 0
2101                                                                                         downloadQueue[queueId].lastPercentage = 0
2102                                                                                 }
2103                                                                                 let complete = track.selectedFilesize
2104                                                                                 let percentage = (chunkLength / complete) * 100;
2105                                                                                 if ((percentage - downloadQueue[queueId].percentage > 1) || (chunkLength == complete)) {
2106                                                                                         downloadQueue[queueId].percentage = percentage
2107                                                                                         if (Math.round(downloadQueue[queueId].percentage) != downloadQueue[queueId].lastPercentage) {
2108                                                                                                 if (Math.round(downloadQueue[queueId].percentage) % 5 == 0) {
2109                                                                                                         downloadQueue[queueId].lastPercentage = Math.round(downloadQueue[queueId].percentage)
2110                                                                                                         io.sockets.emit("downloadProgress", {
2111                                                                                                                 queueId: queueId,
2112                                                                                                                 percentage: downloadQueue[queueId].lastPercentage
2113                                                                                                         })
2114                                                                                                         //logger.info("Updating download progress to: " + downloadQueue[queueId].lastPercentage)
2115                                                                                                 }
2116                                                                                         }
2117                                                                                 }
2118                                                                         }catch(err){}
2119                                                                 }else{
2120                                                                         if (!downloadQueue[queueId]){
2121                                                                                 reject("Not in Queue")
2122                                                                         }
2123                                                                         try{
2124                                                                                 if (!downloadQueue[queueId].percentage) {
2125                                                                                         downloadQueue[queueId].percentage = 0
2126                                                                                         downloadQueue[queueId].lastPercentage = 0
2127                                                                                 }
2128                                                                                 let complete = track.selectedFilesize
2129                                                                                 let percentage = (chunkLength / complete) * 100;
2130                                                                                 if ((percentage - downloadQueue[queueId].tracksData[track.position].progress > 1) || (chunkLength == complete)) {
2131                                                                                         downloadQueue[queueId].tracksData[track.position].progress = percentage
2132                                                                                 }
2133                                                                                 let chunkProgres = ((chunk.length / complete)) / downloadQueue[queueId].size * 100
2134                                                                                 downloadQueue[queueId].percentage += chunkProgres
2135                                                                                 if (Math.round(downloadQueue[queueId].percentage) != downloadQueue[queueId].lastPercentage) {
2136                                                                                         if (Math.round(downloadQueue[queueId].percentage) % 5 == 0) {
2137                                                                                                 downloadQueue[queueId].lastPercentage = Math.round(downloadQueue[queueId].percentage)
2138                                                                                                 io.sockets.emit("downloadProgress", {
2139                                                                                                         queueId: queueId,
2140                                                                                                         percentage: downloadQueue[queueId].lastPercentage
2141                                                                                                 })
2142                                                                                                 //logger.info("Updating download progress to: " + downloadQueue[queueId].lastPercentage)
2143                                                                                         }
2144                                                                                 }
2145                                                                         }catch(err){}
2146                                                                 }
2147                                                         }
2148
2149                                                         // Thanks to Generalo for the improved download function
2150                                                         if (track.selectedFormat == 9){
2151                                                                 if (i % 3 > 0 || chunk.length < 2048) {
2152                                                                         if (i < 1000){
2153                                                                                 flacBuffer += chunk.toString('binary');
2154                                                                         }else if (i == 1000){
2155                                                                                 let buf = Buffer.from(flacBuffer, 'binary');
2156                                                                                 if (track.id > 0) fileStream.write(getMetadata(buf, track, settings));
2157                                                                                 fileStream.write(chunk);
2158                                                                         }else{
2159                                                                                 fileStream.write(chunk, 'binary');
2160                                                                         }
2161                                                                 } else {
2162                                                                         let chunkDec = decryptChunk(chunk, blowFishKey);
2163                                                                         if (i < 1000){
2164                                                                                 flacBuffer += chunkDec.toString('binary');
2165                                                                         }else{
2166                                                                                 fileStream.write(chunkDec, 'binary');
2167                                                                         }
2168                                                                 }
2169                                                         }else{
2170                                                                 if (i % 3 > 0 || chunk.length < 2048) {
2171                                                                         tagSize = saveChunk(tagSize, chunkLength, chunk, fileStream);
2172                                                                 }else{
2173                                                                         let chunkDec = decryptChunk(chunk, blowFishKey);
2174                                                                         if (chunkLength == 2048 && chunkDec.substring(0, 3) == 'ID3') {
2175                                                                                 tagSize = (chunkDec[6].charCodeAt(0) << 21) + (chunkDec[7].charCodeAt(0) << 14) + (chunkDec[8].charCodeAt(0) << 7) + chunkDec[9].charCodeAt(0) + 10;
2176                                                                         }
2177                                                                         tagSize = saveChunk(tagSize, chunkLength, chunkDec, fileStream);
2178                                                                 }
2179                                                         }
2180                                                         i++;
2181                                                 }
2182                                         });
2183
2184                                         response.on('end', () => {
2185                                                 try{
2186                                                         if (track.selectedFormat != 9 && settings.saveID3v1 && track.id > 0 && track.selectedFormat <= 9){
2187                                                                 fileStream.write(getID3v1(track, settings))
2188                                                         }
2189                                                         if (track.selectedFormat == 9 && i < 1000){
2190                                                                 let buf = Buffer.from(flacBuffer, 'binary');
2191                                                                 fileStream.write(getMetadata(buf, track, settings));
2192                                                         }
2193                                                         fileStream.end();
2194                                                         resolve()
2195                                                 }catch(err){
2196                                                         logger.error(`[${track.artist.name} - ${track.title}] Decryption error: ${err}`)
2197                                                         reject(err)
2198                                                         return false
2199                                                 }
2200                                         });
2201                                 } else {
2202                                         reject("Track is no longer provided by deezer")
2203                                         return false
2204                                 }
2205                         })
2206                 })
2207
2208                 try{
2209                         await downloadingPromise
2210                 }catch(err){
2211                         if (err==="Track is no longer provided by deezer"){
2212                                 if (track.selectedFormat == 9){
2213                                         track.filesize.flac = 0
2214                                         logger.warn(`[${track.artist.name} - ${track.title}] Track is no longer provided by deezer in FLAC, searching for lower bitrate`)
2215                                         return downloadTrackObject(track, queueId, settings)
2216                                 }
2217                                 if(track.fallbackId && track.fallbackId != "0"){
2218                                         logger.warn(`[${track.artist.name} - ${track.title}] Track is no longer provided by deezer, falling on alternative`)
2219                                         var _track = await s.Deezer.getTrack(track.fallbackId)
2220                                         track.id = _track.id
2221                                         track.fallbackId = _track.fallbackId
2222                                         track.filesize = _track.filesize
2223                                         track.duration = _track.duration
2224                                         track.MD5 = _track.MD5
2225                                         track.mediaVersion = _track.mediaVersion
2226                                         return downloadTrackObject(track, queueId, settings)
2227                                 }else if(!track.searched && settings.fallbackSearch){
2228                                         logger.warn(`[${track.artist.name} - ${track.title}] Track is no longer provided by deezer, searching for alternative`)
2229                                         _trackId = await convertMetadata2Deezer(track.artist.name, track.title, track.album.title)
2230                                         if (_trackId != "0"){
2231                                                 _track = await s.Deezer.getTrack(_trackId)
2232                                                 track.id = _track.id
2233                                                 track.fallbackId = _track.fallbackId
2234                                                 track.filesize = _track.filesize
2235                                                 track.duration = _track.duration
2236                                                 track.MD5 = _track.MD5
2237                                                 track.mediaVersion = _track.mediaVersion
2238                                                 track.searched = true
2239                                                 return downloadTrackObject(track, queueId, settings)
2240                                         }else{
2241                                                 logger.error(`[${track.artist.name} - ${track.title}] Track is no longer provided by deezer and no alternative found`)
2242                                                 throw new Error("Track is no longer provided by deezer and no alternative found")
2243                                                 return
2244                                         }
2245                                 }else{
2246                                         logger.error(`[${track.artist.name} - ${track.title}] Downloading error: Track is no longer provided by deezer`)
2247                                         throw new Error("Track is no longer provided by deezer")
2248                                         return
2249                                 }
2250                         }else{
2251                                 throw new Error(err)
2252                                 return
2253                         }
2254                 }
2255                 logger.info(`[${track.artist.name} - ${track.title}] Downloaded`)
2256         }
2257 })
2258
2259 //local client socket for use by rest API
2260 let clientsocket = socketclientio.connect('http://localhost:' + configFile.serverPort)
2261
2262 // REST API
2263 app.all('/api/download/', function (req, res) {
2264         //accepts a deezer url or array of urls, and adds it to download
2265         //expecting {"url": "https://www.deezer.com/playlist/xxxxxxxxxx" }
2266         // or &url="https://www.deezer.com/playlist/xxxxxxxxxx"
2267         if (req.method != 'POST' && req.method != 'GET') {
2268                 res.status(400).send({"Error": `${req.url} only accepts GET and POST`});
2269         } else {
2270                 let receivedData
2271                 if (req.method == 'POST') {
2272                         receivedData = req.body
2273                 } else if (req.method == 'GET') {
2274                         receivedData = req.query
2275                 }
2276                 if (Object.keys(receivedData).length == 0) {
2277                         res.status(200).send({"Error": `Empty ${req.method} received`});        //returning 200 for "TEST" functions of other software
2278                 } else {
2279                         response = ""
2280                         if (Array.isArray(receivedData.url)) {
2281                                 for (let x in receivedData.url) {
2282                                         response += `${receivedData.url[x]}: ${clientaddToQueue(receivedData.url[x])}\n`
2283                                 }
2284                         } else {
2285                                 response += clientaddToQueue(receivedData.url)
2286                         }
2287                 
2288                         res.writeHead(200, { 'Content-Type': 'application/json' });
2289                         res.end(JSON.stringify({'Message': response}));
2290                 }
2291         }
2292 });
2293
2294 app.all('/api/search/', function (req, res) {
2295         //accepts a mode (as a key) and search string, returns Deezer JSON
2296         //expecting {"album": "discovery - daft punk"}
2297         // or &album=discovery - daft punk
2298         if (req.method != 'POST' && req.method != 'GET') {
2299                 res.status(400).send({"Error": `${req.url} only accepts GET and POST`});
2300         } else {
2301                 let receivedData
2302                 if (req.method == 'POST') {
2303                         receivedData = req.body
2304                 } else if (req.method == 'GET') {
2305                         receivedData = req.query
2306                 }
2307                 if (Object.keys(receivedData).length == 0) {
2308                         res.status(200).send({"Error": `Empty ${req.method} received`});        //returning 200 for "TEST" functions of other software
2309                 } else {
2310                         let mode = Object.keys(receivedData)[0] //"album", playlist, album, artist
2311                         let searchString = receivedData[mode]
2312                         clientsocket.emit("search", {type: mode, text: searchString})
2313
2314                         clientsocket.on("search", function (data) {
2315                                 if (!(res.headersSent)) {       //no clue why I need this check but without, 2nd+ request breaks
2316                                         res.writeHead(200, { 'Content-Type': 'application/json' });
2317                                 }
2318                                 res.end(JSON.stringify(data));
2319                         })
2320                 }
2321         }
2322 });
2323
2324 app.all('/api/tracks/', function (req, res) {
2325         //accepts a type (as a key) and an ID, returns tracklist,       format: {"album": "302127"}
2326         //expecting "playlist" or "album" or "artist" or "spotifyplaylist"
2327         // or &album=302127
2328         if (req.method != 'POST' && req.method != 'GET') {
2329                 res.status(400).send({"Error": `${req.url} only accepts GET and POST`});
2330         } else {
2331                 let receivedData
2332                 if (req.method == 'POST') {
2333                         receivedData = req.body
2334                 } else if (req.method == 'GET') {
2335                         receivedData = req.query
2336                 }
2337                 if (Object.keys(receivedData).length == 0) {
2338                         res.status(400).send({"Error": `Empty ${req.method} received`});
2339                 } else {
2340                         let type = Object.keys(receivedData)[0] //"album", playlist, album, artist
2341                         let id = receivedData[type]
2342                         clientsocket.emit('getTrackList', {id: id, type: type})
2343
2344                         clientsocket.on("getTrackList", function (data) {
2345                                 //data.err                      -> undefined/err
2346                                 //data.id                         -> passed id
2347                                 //data.response -> API response
2348                                 if (data.err){
2349                                         if (!(res.headersSent)) {       //no clue why I need this check but without, 2nd+ request breaks
2350