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