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