Fixing Adding to Queue for Tracks
[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('./lib/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-native')
25 const os = require('os')
26 const path = require('path')
27 const logger = require('./utils/logger.js')
28 const queue = require('queue')
29 const localpaths = require('./utils/localpaths.js')
30 const package = require('./package.json')
31
32 // First run, create config file
33 if(!fs.existsSync(localpaths.user+"config.json")){
34         fs.outputFileSync(localpaths.user+"config.json",fs.readFileSync(__dirname+path.sep+"default.json",'utf8'))
35 }
36
37 // Main Constants
38 // Files
39 const configFileLocation = localpaths.user+"config.json"
40 // Folders
41 const coverArtFolder = os.tmpdir() + path.sep + 'deezloader-imgs' + path.sep
42 const defaultDownloadFolder = localpaths.user + 'Deezloader Music' + path.sep
43 // Default settings
44 const defaultSettings = require('./default.json').userDefined
45 // Spotify Files
46 const spotifySupport = fs.existsSync(localpaths.user+"authCredentials.js")
47 if (spotifySupport){
48         var authCredentials = require(localpaths.user+'authCredentials.js')
49         var Spotify = new spotifyApi(authCredentials)
50 }
51
52 // Setup the folders START
53 var mainFolder = defaultDownloadFolder
54
55 // See if all settings are there after update
56 var configFile = require(localpaths.user+path.sep+"config.json");
57 for (let x in defaultSettings){
58         if (typeof configFile.userDefined[x] != typeof defaultSettings[x]){
59                 configFile.userDefined[x] = defaultSettings[x]
60         }
61 }
62 // Set default download folder if not userDefined
63 if (configFile.userDefined.downloadLocation != "") {
64         mainFolder = configFile.userDefined.downloadLocation
65 }
66
67 initFolders();
68
69 // Route and create server
70 app.use('/', express.static(__dirname + '/public/'))
71 server.listen(configFile.serverPort)
72 logger.info('Server is running @ localhost:' + configFile.serverPort)
73
74 // START sockets clusterfuck
75 io.sockets.on('connection', function (s) {
76         logger.info("Connection recived!")
77
78         // Check for updates
79         request({
80                 url: "https://notabug.org/RemixDevs/DeezloaderRemix/raw/master/update.json",
81                 json: true
82         })
83         .then(body=>{
84                 logger.info("Checking for updates")
85                 let [currentVersion_MAJOR, currentVersion_MINOR, currentVersion_PATCH] = package.version.split(".")
86                 let [lastVersion_MAJOR, lastVersion_MINOR, lastVersion_PATCH] = body.version.split(".")
87                 if (
88                         parseInt(lastVersion_MAJOR) > parseInt(currentVersion_MAJOR) ||
89                         parseInt(lastVersion_MINOR) > parseInt(currentVersion_MINOR) ||
90                         parseInt(lastVersion_PATCH) > parseInt(currentVersion_PATCH))
91                 {
92                         logger.info("Update Available")
93                         s.emit("message", {title: `Version ${lastVersion_MAJOR}.${lastVersion_MINOR}.${lastVersion_PATCH} is available!`, msg: body.changelog})
94                 }
95         })
96         .catch(error=>{
97                 logger.error(error)
98         })
99
100         // Connection dependet variables
101         s.Deezer = new deezerApi()
102         s.downloadQueue = {}
103         s.currentItem = null
104         s.lastQueueId = null
105         s.trackQueue = queue({
106                 autostart: true
107         })
108         s.trackQueue.concurrency = configFile.userDefined.queueConcurrency
109
110         // Function for logging in
111         s.on("login", async function (username, password, autologin) {
112                 try{
113                         logger.info("Logging in");
114                         await s.Deezer.login(username, password)
115                         s.emit("login", {user: s.Deezer.user})
116                         logger.info("Logged in successfully")
117                         if (autologin){
118                                 // Save session login so next time login is not needed
119                                 // This is the same method used by the official website
120                                 s.emit('getCookies', s.Deezer.getCookies())
121                         }
122                 }catch(err){
123                         s.emit("login", {error: err.message})
124                         logger.error(`Login failed: ${err.message}`)
125                 }
126         });
127
128         // Function for autologin
129         s.on("autologin", async function(jar, email){
130                 try{
131       await s.Deezer.loginViaCookies(jar, email)
132                         s.emit('login', {user: s.Deezer.user})
133     }catch(err){
134       s.emit('login', {error: err.message})
135                         logger.error(`Autologin failed: ${err.message}`)
136       return
137     }
138         })
139
140         // Function for logout
141         s.on("logout", function(){
142                 logger.info("Logged out")
143                 // Creating new object to clear the cookies
144                 s.Deezer = new deezerApi()
145                 return
146         })
147
148         // Returns list of charts available
149         s.on("getChartsCountryList", async function (data) {
150                 try{
151                         let charts = await s.Deezer.legacyGetChartsTopCountry()
152                         charts = charts.data || []
153                         let countries = []
154                         for (let i = 0; i < charts.length; i++) {
155                                 let obj = {
156                                         country: charts[i].title.replace("Top ", ""),
157                                         picture_small: charts[i].picture_small,
158                                         picture_medium: charts[i].picture_medium,
159                                         picture_big: charts[i].picture_big,
160                                         playlistId: charts[i].id
161                                 }
162                                 countries.push(obj)
163                         }
164                         s.emit("getChartsCountryList", {countries: countries, selected: data.selected})
165                 }catch(err){
166                         logger.error(`getChartsCountryList failed: ${err.stack}`)
167                         return
168                 }
169         })
170
171         // Returns chart tracks from Playlist ID
172         async function getChartsTrackListById(playlistId){
173                 if (typeof playlistId === 'undefined') {
174                         s.emit("getChartsTrackListByCountry", {err: "Can't find that playlist"})
175                         return
176                 }
177                 try{
178                         let tracks = await s.Deezer.legacyGetPlaylistTracks(playlistId)
179                         s.emit("getChartsTrackListByCountry", {
180                                 playlistId: playlistId,
181                                 tracks: tracks.data
182                         })
183                 }catch(err){
184                         s.emit("getChartsTrackListByCountry", {err: err})
185                         logger.error(`getChartsTrackListById failed: ${err.stack}`)
186                         return
187                 }
188         }
189
190         // Returns chart tracks from country name
191         async function getChartsTrackListByCountry(country){
192                 if (typeof country === 'undefined') {
193                         s.emit("getChartsTrackListByCountry", {err: "No country passed"})
194                         return
195                 }
196                 try{
197                         let charts = await s.Deezer.legacyGetChartsTopCountry()
198                         charts = charts.data || []
199                         let countries = []
200                         for (let i = 0; i < charts.length; i++) {
201                                 countries.push(charts[i].title.replace("Top ", ""))
202                         }
203                         if (countries.indexOf(country) == -1) {
204                                 s.emit("getChartsTrackListByCountry", {err: "Country not found"});
205                                 return
206                         }
207                         let playlistId = charts[countries.indexOf(country)].id;
208                         await getChartsTrackListById(playlistId)
209                 }catch(err){
210                         logger.error(`getChartsTrackListByCountry failed: ${err.stack}`)
211                         return
212                 }
213         }
214         s.on("getChartsTrackListByCountry", function (data) {getChartsTrackListByCountry(data.country)})
215
216         // Returns list of playlists
217         async function getMyPlaylistList(){
218                 try{
219                         logger.info("Loading Personal Playlists")
220                         let data = await s.Deezer.legacyGetUserPlaylists(s.Deezer.user.id)
221                         data = data.data || []
222                         let playlists = []
223                         for (let i = 0; i < data.length; i++) {
224                                 let obj = {
225                                         title: data[i].title,
226                                         image: data[i].picture_small,
227                                         songs: data[i].nb_tracks,
228                                         link: data[i].link
229                                 }
230                                 playlists.push(obj)
231                         }
232                         if (configFile.userDefined.spotifyUser && spotifySupport){
233                                 let creds = await Spotify.clientCredentialsGrant()
234                                 Spotify.setAccessToken(creds.body['access_token'])
235                                 let first = true
236                                 let offset = 0
237                                 do{
238                                         let data = await Spotify.getUserPlaylists(configFile.userDefined.spotifyUser, {fields: "items(images,name,owner.id,tracks.total,uri),total", offset: offset*20})
239                                         if (first){
240                                                 var total = data.body.total
241                                                 var numPages=Math.floor((total-1)/20)
242                                                 var playlistList = new Array(total)
243                                                 first = false
244                                         }
245                                         data.body.items.forEach((playlist, i) => {
246                                                 playlistList[(offset*20)+i] = {
247                                                         title: playlist.name,
248                                                         image: (playlist.images[0] ? playlist.images[0].url : ""),
249                                                         songs: playlist.tracks.total,
250                                                         link: playlist.uri,
251                                                         spotify: true
252                                                 }
253                                         })
254                                         offset++
255                                 }while(offset<=numPages)
256                                 playlists = playlists.concat(playlistList)
257                         }
258                         logger.info(`Loaded ${playlists.length} Playlist${playlists.length>1 ? "s" : ""}`)
259                         s.emit("getMyPlaylistList", {playlists: playlists})
260                 }catch(err){
261                         logger.error(`getMyPlaylistList failed: ${err.stack}`)
262                         return
263                 }
264         }
265         s.on("getMyPlaylistList", function (d) {getMyPlaylistList()})
266
267         // Returns search results from a query
268         s.on("search", async function (data) {
269                 data.type = data.type || "track"
270                 if (["track", "playlist", "album", "artist"].indexOf(data.type) == -1) data.type = "track"
271
272                 // Remove "feat."  "ft." and "&" (causes only problems)
273                 data.text = data.text
274                         .replace(/ feat[\.]? /g, " ")
275                         .replace(/ ft[\.]? /g, " ")
276                         .replace(/\(feat[\.]? /g, " ")
277                         .replace(/\(ft[\.]? /g, " ")
278                         .replace(/\&/g, "")
279                         .replace(/–/g, "-")
280                         .replace(/—/g, "-")
281
282                 try {
283                         let searchObject = await s.Deezer.legacySearch(encodeURIComponent(data.text), data.type)
284                         s.emit("search", {type: data.type, items: searchObject.data})
285                 } catch (err) {
286                         s.emit("search", {type: data.type, items: []})
287                         logger.error(`search failed: ${err.stack}`)
288                         return
289                 }
290         })
291
292         // Returns list of tracks from an album/playlist or the list of albums from an artist
293         s.on("getTrackList", async function (data) {
294                 if (!data.type || (["playlist", "album", "artist", "spotifyplaylist"].indexOf(data.type) == -1) || !data.id) {
295                         s.emit("getTrackList", {err: -1, response: {}, id: data.id, reqType: data.type})
296                         return
297                 }
298                 if (data.type == 'artist') {
299                         try{
300                                 let response = await s.Deezer.legacyGetArtistAlbums(data.id)
301                                 s.emit("getTrackList", {response: response, id: data.id, reqType: data.type})
302                         }catch(err){
303                                 s.emit("getTrackList", {err: "wrong artist id", response: {}, id: data.id, reqType: data.type})
304                                 logger.error(`getTrackList failed: ${err.stack}`)
305                                 return
306                         }
307                 }else if(data.type == "spotifyplaylist" && spotifySupport){
308                         try{
309                                 let creds = await Spotify.clientCredentialsGrant()
310                                 Spotify.setAccessToken(creds.body.access_token)
311                                 let first = true
312                                 let offset = 0
313                                 do{
314                                         let resp = await Spotify.getPlaylistTracks(data.id, {fields: "items(track(artists,name,duration_ms,preview_url,explicit)),total", offset: offset*100})
315                                         if (first){
316                                                 var numPages=Math.floor((resp.body.total-1)/100)
317                                                 var response = new Array(resp.body.total)
318                                                 first = false
319                                         }
320                                         resp.body.items.forEach((t, index) => {
321                                                 response[index+offset*100]={
322                                                         explicit_lyrics: t.track.explicit,
323                                                         preview: t.track.preview_url,
324                                                         title: t.track.name,
325                                                         artist: {
326                                                                 name: t.track.artists[0].name
327                                                         },
328                                                         duration: Math.floor(t.track.duration_ms/1000)
329                                                 }
330                                         })
331                                         offset++
332                                 }while(offset<=numPages)
333                                 s.emit("getTrackList", {response: {'data': response}, id: data.id, reqType: data.type})
334                         }catch(err){
335                                 logger.error(`getTrackList failed: ${err.stack}`)
336                         }
337                 }else{
338                         let reqType = data.type.charAt(0).toUpperCase() + data.type.slice(1)
339                         try{
340                                 let response = await s.Deezer["legacyGet" + reqType + "Tracks"](data.id)
341                                 s.emit("getTrackList", {response: response, id: data.id, reqType: data.type})
342                         }catch(err){
343                                 s.emit("getTrackList", {err: "wrong id "+reqType, response: {}, id: data.id, reqType: data.type})
344                                 logger.error(`getTrackList failed: ${err.stack}`)
345                                 return
346                         }
347                 }
348         })
349
350         s.on("getUserSettings", function () {
351                 let settings = configFile.userDefined;
352                 if (!settings.downloadLocation) {
353                         settings.downloadLocation = mainFolder;
354                 }
355                 s.emit('getUserSettings', {settings: settings});
356         });
357
358         s.on("saveSettings", function (settings) {
359                 if (settings.userDefined.downloadLocation == defaultDownloadFolder) {
360                         settings.userDefined.downloadLocation = "";
361                 } else {
362                         settings.userDefined.downloadLocation = path.resolve(settings.userDefined.downloadLocation + path.sep) + path.sep;
363                         mainFolder = settings.userDefined.downloadLocation;
364                 }
365
366                 if (settings.userDefined.queueConcurrency < 1) settings.userDefined.queueConcurrency = 1;
367
368                 if (settings.userDefined.queueConcurrency != s.trackQueue.concurrency){
369                         s.trackQueue.concurrency = settings.userDefined.queueConcurrency;
370                 }
371
372                 if (settings.userDefined.chartsCountry != configFile.userDefined.chartsCountry){
373                         s.emit("setChartsCountry", {selected: settings.userDefined.chartsCountry});
374                         getChartsTrackListByCountry(settings.userDefined.chartsCountry);
375                 }
376
377                 if (settings.userDefined.spotifyUser != configFile.userDefined.spotifyUser){
378                         getMyPlaylistList(settings.userDefined.spotifyUser);
379                 }
380
381                 configFile.userDefined = settings.userDefined;
382                 fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
383                         if (err) return;
384                         logger.info("Settings updated");
385                         initFolders();
386                 });
387         });
388
389         /*
390         // TODO: Make download progress not depend from the API
391         s.Deezer.onDownloadProgress = function (track, progress) {
392                 if (!track.trackSocket) {
393                         return;
394                 }
395                 if(track.trackSocket.currentItem && track.trackSocket.currentItem.type == "track"){
396                         let complete;
397                         if (!track.trackSocket.currentItem.percentage) {
398                                 track.trackSocket.currentItem.percentage = 0;
399                         }
400                         if (parseInt(track.SNG_ID)<0){
401                                 complete = track.FILESIZE;
402                         }else if(track.format == 9){
403                                 complete = track.FILESIZE_FLAC;
404                         }else{
405                                 if (track.FILESIZE_MP3_320) {
406                                         complete = track.FILESIZE_MP3_320;
407                                 } else if (track.FILESIZE_MP3_256) {
408                                         complete = track.FILESIZE_MP3_256;
409                                 } else {
410                                         complete = track.FILESIZE_MP3_128 || 0;
411                                 }
412                         }
413                         let percentage = (progress / complete) * 100;
414                         if ((percentage - track.trackSocket.currentItem.percentage > 1) || (progress == complete)) {
415                                 track.trackSocket.currentItem.percentage = percentage;
416                                 track.trackSocket.emit("downloadProgress", {
417                                         queueId: track.trackSocket.currentItem.queueId,
418                                         percentage: track.trackSocket.currentItem.percentage
419                                 });
420                         }
421                 }
422         };
423         */
424
425         function addToQueue(object) {
426                 s.downloadQueue[object.queueId] = object
427                 s.emit('addToQueue', object)
428                 queueDownload(getNextDownload())
429         }
430
431         function getNextDownload() {
432                 if (s.currentItem != null || Object.keys(s.downloadQueue).length == 0) {
433                         if (Object.keys(s.downloadQueue).length == 0 && s.currentItem == null) {
434                                 s.emit("emptyDownloadQueue", {})
435                         }
436                         return null
437                 }
438                 s.currentItem = s.downloadQueue[Object.keys(s.downloadQueue)[0]]
439                 return s.currentItem
440         }
441
442         async function downloadTrack(data){
443                 try{
444                         var track = await s.Deezer.getTrack(data.id)
445                         let _track = {
446                                 name: track.title,
447                                 artist: track.mainArtist.name,
448                                 size: 1,
449                                 downloaded: 0,
450                                 failed: 0,
451                                 queueId: `id${Math.random().toString(36).substring(2)}`,
452                                 id: `${track.id}:${data.settings.maxBitrate}`,
453                                 type: 'track',
454                                 settings: data.settings || {},
455                                 obj: track,
456                         }
457                         addToQueue(JSON.parse(JSON.stringify(_track)));
458                 }catch(err){
459                         logger.error(err)
460                         return
461                 }
462         }
463         s.on("downloadtrack", data=>{downloadTrack(data)});
464
465         /*
466         function downloadPlaylist(data){
467                 s.Deezer.getPlaylist(data.id, function (playlist, err) {
468                         if (err) {
469                                 logger.error(err)
470                                 return;
471                         }
472                         let queueId = "id" + Math.random().toString(36).substring(2);
473                         let _playlist = {
474                                 name: playlist["title"],
475                                 size: playlist.nb_tracks,
476                                 downloaded: 0,
477                                 artist: playlist.creator.name,
478                                 failed: 0,
479                                 queueId: queueId,
480                                 id: playlist["id"],
481                                 type: "playlist",
482                                 cover: playlist["picture_small"],
483                         };
484                         _playlist.settings = data.settings || {};
485                         s.Deezer.getAdvancedPlaylistTracks(data.id, function (playlist, err) {
486                                 if (err){
487                                         logger.error(err)
488                                         return;
489                                 }
490                                 _playlist.size = playlist.data.length
491                                 _playlist.tracks = playlist.data
492                                 addToQueue(JSON.parse(JSON.stringify(_playlist)));
493                         })
494                 });
495         }
496         s.on("downloadplaylist", data=>{downloadPlaylist(data)});
497
498         function downloadAlbum(data){
499                 s.Deezer.getAlbum(data.id, function (album, err) {
500                         if (err) {
501                                 logger.error(err)
502                                 return;
503                         }
504                         let queueId = "id" + Math.random().toString(36).substring(2);
505                         let _album = {
506                                 name: album["title"],
507                                 label: album["label"],
508                                 artist: album["artist"].name,
509                                 size: album.tracks.data.length,
510                                 downloaded: 0,
511                                 failed: 0,
512                                 queueId: queueId,
513                                 id: album["id"],
514                                 type: "album",
515                         };
516                         data.settings.albumInfo = slimDownAlbumInfo(album)
517                         _album.settings = data.settings || {};
518                         s.Deezer.getAdvancedAlbumTracks(data.id, function (album, err) {
519                                 if (err){
520                                         logger.error(err)
521                                         return;
522                                 }
523                                 _album.size = album.data.length
524                                 _album.tracks = album.data
525                                 addToQueue(JSON.parse(JSON.stringify(_album)));
526                         })
527                 });
528         }
529         s.on("downloadalbum", data=>{DownloadAlbum(data)});
530
531         function downloadArtist(data){
532                 s.Deezer.getArtistAlbums(data.id, function (albums, err) {
533                         if (err) {
534                                 logger.error(err)
535                                 return;
536                         }
537                         (function sendAllAlbums(i) {
538                                 setTimeout(function () {
539                       data.id = albums.data[albums.data.length-1-i].id;
540                                         downloadAlbum(JSON.parse(JSON.stringify(data)));
541                       if (--i+1) sendAllAlbums(i);
542                         }, 100)
543                         })(albums.data.length-1);
544                 });
545         }
546         s.on("downloadartist", data=>{downloadArtist(data)});
547
548         s.on("downloadspotifyplaylist", function (data) {
549                 if (spotifySupport){
550                         Spotify.clientCredentialsGrant().then(function(creds) {
551                                 Spotify.setAccessToken(creds.body['access_token']);
552                                 return Spotify.getPlaylist(data.id, {fields: "id,name,owner,images,tracks(total,items(track.artists,track.name,track.album))"})
553                         }).then(function(resp) {
554                                 let queueId = "id" + Math.random().toString(36).substring(2);
555                                 let _playlist = {
556                                         name: resp.body["name"],
557                                         artist: (resp.body["owner"]["display_name"] ? resp.body["owner"]["display_name"] : resp.body["owner"]["id"]),
558                                         size: resp.body["tracks"]["total"],
559                                         downloaded: 0,
560                                         failed: 0,
561                                         queueId: queueId,
562                                         id: resp.body["id"],
563                                         type: "spotifyplaylist",
564                                         cover: (resp.body["images"] ? resp.body["images"][0]["url"] : null),
565                                         tracks: resp.body["tracks"]["items"]
566                                 };
567                                 _playlist.settings = data.settings || {};
568                                 addToQueue(JSON.parse(JSON.stringify(_playlist)));
569                         }).catch(err=>{
570                                 logger.error(err)
571                                 return;
572                         })
573                 }else{
574                         s.emit("message", {title: "Spotify Support is not enabled", msg: "You should add authCredentials.js in your config files 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>"})
575                 }
576         });
577         */
578
579         //currentItem: the current item being downloaded at that moment such as a track or an album
580         //downloadQueue: the tracks in the queue to be downloaded
581         //lastQueueId: the most recent queueId
582         //queueId: random number generated when user clicks download on something
583         async function queueDownload(downloading) {
584                 if (!downloading) return;
585
586                 // New batch emits new message
587                 if (s.lastQueueId != downloading.queueId) {
588                         if (downloading.type != "spotifyplaylist"){
589                                 s.emit("downloadStarted", {queueId: downloading.queueId})
590                         }
591                         s.lastQueueId = downloading.queueId
592                 }
593
594                 let filePath;
595                 logger.info(`Registered ${downloading.type}: ${downloading.id} | ${downloading.artist} - ${downloading.name}`);
596                 switch(downloading.type){
597                         case "track":
598                                 try{
599                                         await downloadTrackObject(downloading.obj, downloading.queueId, downloading.settings)
600                                         downloading.downloaded++
601                                 }catch(err){
602                                         logger.error(err.stack)
603                                         downloading.failed++
604                                 }
605                                 s.emit("updateQueue", {
606                                         name: downloading.name,
607                                         artist: downloading.artist,
608                                         size: downloading.size,
609                                         downloaded: downloading.downloaded,
610                                         failed: downloading.failed,
611                                         queueId: downloading.queueId,
612                                         id: downloading.id,
613                                         type: downloading.type,
614                                 })
615                                 s.emit("downloadProgress", {
616                                         queueId: downloading.queueId,
617                                         percentage: 100
618                                 })
619                                 if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]]
620                                 s.currentItem = null
621                                 queueDownload(getNextDownload())
622                                 break
623                         /*
624                         case "album":
625                                 downloading.playlistContent = downloading.tracks.map((t,i) => {
626                                         if (t.FALLBACK){
627                                                 if (t.FALLBACK.SNG_ID)
628                                                         return {id: t.SNG_ID, fallback: t.FALLBACK.SNG_ID, name: (t.VERSION ? t.SNG_TITLE + " "+t.VERSION : t.SNG_TITLE), artist: t.ART_NAME, index: i+"", queueId: downloading.queueId}
629                                         }else{
630                                                 return {id: t.SNG_ID, name: (t.VERSION ? t.SNG_TITLE+" "+t.VERSION : t.SNG_TITLE), artist: t.ART_NAME, index: i+"", queueId: downloading.queueId}
631                                         }
632                                 })
633                                 downloading.settings.albName = downloading.name;
634                                 downloading.settings.artName = downloading.artist;
635                                 downloading.errorLog = "";
636                                 downloading.searchedLog = "";
637                                 downloading.playlistArr = Array(downloading.size);
638                                 filePath = mainFolder;
639                                 if (downloading.settings.createArtistFolder || downloading.settings.createAlbumFolder) {
640                                         if (downloading.settings.createArtistFolder) {
641                                                 filePath += antiDot(fixName(downloading.settings.artName)) + path.sep;
642                                         }
643                                         if (downloading.settings.createAlbumFolder) {
644                                                 filePath += antiDot(fixName(settingsRegexAlbum(downloading.settings.foldername,downloading.settings.artName,downloading.settings.albName,downloading.settings.albumInfo.release_date.slice(0, 4),downloading.settings.albumInfo.record_type,downloading.settings.albumInfo.explicit_lyrics,downloading.settings.albumInfo.label))) + path.sep;
645                                         }
646                                 } else if (downloading.settings.artName) {
647                                         filePath += antiDot(fixName(settingsRegexAlbum(downloading.settings.foldername,downloading.settings.artName,downloading.settings.albName,downloading.settings.albumInfo.release_date.slice(0, 4),downloading.settings.albumInfo.record_type,downloading.settings.albumInfo.explicit_lyrics,downloading.settings.albumInfo.label))) + path.sep;
648                                 }
649                                 downloading.finished = new Promise((resolve,reject)=>{
650                                         downloading.playlistContent.every(function (t) {
651                                                 s.trackQueue.push(cb=>{
652                                                         if (!s.downloadQueue[downloading.queueId]) {
653                                                                 reject();
654                                                                 return false;
655                                                         }
656                                                         logger.info(`Now downloading: ${t.artist} - ${t.name}`)
657                                                         downloadTrackObject(t, downloading.settings, null, function (err, track) {
658                                                                 if (!err) {
659                                                                         downloading.downloaded++;
660                                                                         downloading.playlistArr[track.playlistData[0]] = track.playlistData[1].split(filePath)[1];
661                                                                         if (track.searched) downloading.searchedLog += `${t.artist} - ${t.name}\r\n`
662                                                                 } else {
663                                                                         downloading.failed++;
664                                                                         downloading.errorLog += `${t.id} | ${t.artist} - ${t.name} | ${err}\r\n`;
665                                                                 }
666                                                                 s.emit("downloadProgress", {
667                                                                         queueId: downloading.queueId,
668                                                                         percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
669                                                                 });
670                                                                 s.emit("updateQueue", downloading);
671                                                                 if (downloading.downloaded + downloading.failed == downloading.size)
672                                                                         resolve();
673                                                                 cb();
674                                                         });
675                                                 });
676                                                 return true;
677                                         });
678                                 })
679                                 downloading.finished.then(()=>{
680                                         if (downloading.countPerAlbum) {
681                                                 if (Object.keys(s.downloadQueue).length > 1 && Object.keys(s.downloadQueue)[1] == downloading.queueId) {
682                                                         s.downloadQueue[downloading.queueId].download = downloading.downloaded;
683                                                 }
684                                                 s.emit("updateQueue", downloading);
685                                         }
686                                         logger.info("Album finished "+downloading.name);
687                                         s.emit("downloadProgress", {
688                                                 queueId: downloading.queueId,
689                                                 percentage: 100
690                                         });
691                                         if (downloading.settings.logErrors){
692                                                 if (downloading.errorLog != ""){
693                                                         if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
694                                                         fs.writeFileSync(filePath+"notFound.txt",downloading.errorLog)
695                                                 }else{
696                                                         if (fs.existsSync(filePath+"notFound.txt")) fs.unlinkSync(filePath+"notFound.txt");
697                                                 }
698                                         }
699                                         if (downloading.settings.logSearched){
700                                                 if (downloading.searchedLog != ""){
701                                                         if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
702                                                         fs.writeFileSync(filePath+"alternativeSongs.txt",downloading.searchedLog)
703                                                 }else{
704                                                         if (fs.existsSync(filePath+"alternativeSongs.txt")) fs.unlinkSync(filePath+"alternativeSongs.txt");
705                                                 }
706                                         }
707                                         if (downloading.settings.createM3UFile){
708                                                 fs.writeFileSync(filePath + "playlist.m3u", downloading.playlistArr.join("\r\n"));
709                                         }
710                                         if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]];
711                                         s.currentItem = null;
712                                         queueDownload(getNextDownload());
713                                 }).catch((err)=>{
714                                         if (err) return logger.error(err.stack);
715                                         logger.info("Stopping the album queue");
716                                         if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]];
717                                         s.currentItem = null;
718                                         queueDownload(getNextDownload());
719                                 });
720                                 break;
721                         case "playlist":
722                                 downloading.playlistContent = downloading.tracks.map((t,i) => {
723                                         if (t.FALLBACK){
724                                                 if (t.FALLBACK.SNG_ID)
725                                                         return {id: t.SNG_ID, fallback: t.FALLBACK.SNG_ID, name: (t.VERSION ? t.SNG_TITLE + " "+t.VERSION : t.SNG_TITLE), artist: t.ART_NAME, index: i+"", queueId: downloading.queueId}
726                                         }else{
727                                                 return {id: t.SNG_ID, name: (t.VERSION ? t.SNG_TITLE+" "+t.VERSION : t.SNG_TITLE), artist: t.ART_NAME, index: i+"", queueId: downloading.queueId}
728                                         }
729                                 })
730                                 downloading.settings.plName = downloading.name;
731                                 downloading.errorLog = ""
732                                 downloading.searchedLog = "";
733                                 downloading.playlistArr = Array(downloading.size);
734                                 downloading.settings.playlist = {
735                                         fullSize: downloading.playlistContent.length
736                                 };
737                                 filePath = mainFolder+antiDot(fixName(downloading.settings.plName)) + path.sep
738                                 downloading.finished = new Promise((resolve,reject)=>{
739                                         downloading.playlistContent.every(function (t) {
740                                                 s.trackQueue.push(cb=>{
741                                                         if (!s.downloadQueue[downloading.queueId]) {
742                                                                 reject();
743                                                                 return false;
744                                                         }
745                                                         logger.info(`Now downloading: ${t.artist} - ${t.name}`)
746                                                         downloadTrackObject(t, downloading.settings, null, function (err, track) {
747                                                                 if (!err) {
748                                                                         downloading.downloaded++;
749                                                                         downloading.playlistArr[track.playlistData[0]] = track.playlistData[1].split(filePath)[1];
750                                                                         if (track.searched) downloading.searchedLog += `${t.artist} - ${t.name}\r\n`
751                                                                 } else {
752                                                                         downloading.failed++;
753                                                                         downloading.errorLog += `${t.id} | ${t.artist} - ${t.name} | ${err}\r\n`;
754                                                                 }
755                                                                 s.emit("downloadProgress", {
756                                                                         queueId: downloading.queueId,
757                                                                         percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
758                                                                 });
759                                                                 s.emit("updateQueue", downloading);
760                                                                 if (downloading.downloaded + downloading.failed == downloading.size)
761                                                                         resolve();
762                                                                 cb();
763                                                         });
764                                                 });
765                                                 return true;
766                                         })
767                                 });
768                                 downloading.finished.then(()=>{
769                                         logger.info("Playlist finished "+downloading.name);
770                                         s.emit("downloadProgress", {
771                                                 queueId: downloading.queueId,
772                                                 percentage: 100
773                                         });
774                                         if (downloading.settings.logErrors){
775                                                 if (downloading.errorLog != ""){
776                                                         if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
777                                                         fs.writeFileSync(filePath+"notFound.txt",downloading.errorLog)
778                                                 }else{
779                                                         if (fs.existsSync(filePath+"notFound.txt")) fs.unlinkSync(filePath+"notFound.txt");
780                                                 }
781                                         }
782                                         if (downloading.settings.logSearched){
783                                                 if (downloading.searchedLog != ""){
784                                                         if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
785                                                         fs.writeFileSync(filePath+"alternativeSongs.txt",downloading.searchedLog)
786                                                 }else{
787                                                         if (fs.existsSync(filePath+"alternativeSongs.txt")) fs.unlinkSync(filePath+"alternativeSongs.txt");
788                                                 }
789                                         }
790                                         if (downloading.settings.createM3UFile){
791                                                 fs.writeFileSync(filePath + "playlist.m3u", downloading.playlistArr.join("\r\n"));
792                                         }
793                                         if (downloading.settings.saveArtwork){
794                                                 if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
795                                                 let imgPath = filePath + antiDot(fixName(settingsRegexCover(downloading.settings.coverImageTemplate,downloading.artist,downloading.name)))+(downloading.settings.PNGcovers ? ".png" : ".jpg");
796                                                 if (downloading.cover){
797                                                         downloading.cover = downloading.cover.replace("56x56",`${downloading.settings.artworkSize}x${downloading.settings.artworkSize}`)
798                                                         request.get(downloading.cover, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
799                                                                 if(error){
800                                                                         logger.error(error.stack);
801                                                                         return;
802                                                                 }
803                                                                 fs.outputFile(imgPath,body,'binary',function(err){
804                                                                         if(err){
805                                                                                 logger.error(err.stack);
806                                                                                 return;
807                                                                         }
808                                                                         logger.info(`Cover downloaded for: ${downloading.settings.plName}`)
809                                                                 })
810                                                         });
811                                                 }
812                                         }
813                                         if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]];
814                                         s.currentItem = null;
815                                         queueDownload(getNextDownload());
816                                 }).catch((err)=>{
817                                         if (err) return logger.error(err.stack);
818                                         logger.info("Stopping the playlist queue");
819                                         if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]];
820                                         s.currentItem = null;
821                                         queueDownload(getNextDownload());
822                                 });
823                                 break;
824                         case "spotifyplaylist":
825                         if (spotifySupport){
826                                 Spotify.clientCredentialsGrant().then(function(creds) {
827                                         downloading.settings.plName = downloading.name;
828                                         downloading.playlistArr = Array(downloading.size);
829                                         Spotify.setAccessToken(creds.body['access_token']);
830                                         numPages=Math.floor((downloading.size-1)/100);
831                                         let pages = []
832                                         downloading.playlistContent = new Array(downloading.size);
833                                         downloading.tracks.map((t,i)=>{
834                                                 downloading.playlistContent[i]=new Promise(function(resolve, reject) {
835                                                         s.Deezer.track2ID(t.track.artists[0].name, t.track.name, t.track.album.name, function (response,err){
836                                                                 resolve(response);
837                                                         });
838                                                 });
839                                         })
840                                         if (downloading.size>100){
841                                                 for (let offset = 1; offset<=numPages; offset++){
842                                                         pages.push(new Promise(function(resolvePage) {
843                                                                 Spotify.getPlaylistTracks(downloading.id, {fields: "items(track.artists,track.name,track.album)", offset: offset*100}).then(function(resp) {
844                                                                         resp.body['items'].forEach((t, index) => {
845                                                                                 downloading.playlistContent[(offset*100)+index] = new Promise(function(resolve, reject) {
846                                                                                         s.Deezer.track2ID(t.track.artists[0].name, t.track.name, t.track.album.name, function (response,err){
847                                                                                                 resolve(response);
848                                                                                         });
849                                                                                 });
850                                                                         });
851                                                                         resolvePage();
852                                                                 });
853                                                         }));
854                                                 }
855                                         }
856                                         logger.info("Waiting for all pages");
857                                         Promise.all(pages).then((val)=>{
858                                                 logger.info("Waiting for all tracks to be converted");
859                                                 return Promise.all(downloading.playlistContent)
860                                         }).then((values)=>{
861                                                 if (!s.downloadQueue[downloading.queueId]) {
862                                                         logger.info("Stopping the playlist queue");
863                                                         if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]];
864                                                         s.currentItem = null;
865                                                         queueDownload(getNextDownload());
866                                                         return;
867                                                 }
868                                                 logger.info("All tracks converted, starting download");
869                                                 s.emit("downloadStarted", {queueId: downloading.queueId});
870                                                 downloading.errorLog = "";
871                                                 downloading.searchedLog = "";
872                                                 downloading.settings.playlist = {
873                                                         fullSize: values.length
874                                                 };
875                                                 filePath = mainFolder+antiDot(fixName(downloading.settings.plName)) + path.sep
876                                                 downloading.finished = new Promise((resolve,reject)=>{
877                                                         values.every(function (t) {
878                                                                 t.index = values.indexOf(t)+""
879                                                                 t.queueId = downloading.queueId
880                                                                 s.trackQueue.push(cb=>{
881                                                                         if (!s.downloadQueue[downloading.queueId]) {
882                                                                                 reject();
883                                                                                 return false;
884                                                                         }
885                                                                         logger.info(`Now downloading: ${t.artist} - ${t.name}`)
886                                                                         downloadTrackObject(t, downloading.settings, null, function (err, track) {
887                                                                                 if (!err) {
888                                                                                         downloading.downloaded++;
889                                                                                         downloading.playlistArr[track.playlistData[0]] = track.playlistData[1].split(filePath)[1];
890                                                                                         if (track.searched) downloading.searchedLog += `${t.artist} - ${t.name}\r\n`
891                                                                                 } else {
892                                                                                         downloading.failed++;
893                                                                                         downloading.errorLog += `${t.id} | ${t.artist} - ${t.name} | ${err}\r\n`;
894                                                                                 }
895                                                                                 s.emit("downloadProgress", {
896                                                                                         queueId: downloading.queueId,
897                                                                                         percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
898                                                                                 });
899                                                                                 if (downloading.downloaded + downloading.failed == downloading.size)
900                                                                                         resolve();
901                                                                                 s.emit("updateQueue", downloading);
902                                                                                 cb();
903                                                                         });
904                                                                 });
905                                                                 return true;
906                                                         });
907                                                 });
908                                                 downloading.finished.then(()=>{
909                                                         logger.info("Playlist finished "+downloading.name);
910                                                         s.emit("downloadProgress", {
911                                                                 queueId: downloading.queueId,
912                                                                 percentage: 100
913                                                         });
914                                                         if (downloading.settings.logErrors){
915                                                                 if (downloading.errorLog != ""){
916                                                                         if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
917                                                                         fs.writeFileSync(filePath+"notFound.txt",downloading.errorLog)
918                                                                 }else{
919                                                                         if (fs.existsSync(filePath+"notFound.txt")) fs.unlinkSync(filePath+"notFound.txt");
920                                                                 }
921                                                         }
922                                                         if (downloading.settings.logSearched){
923                                                                 if (downloading.searchedLog != ""){
924                                                                         if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
925                                                                         fs.writeFileSync(filePath+"alternativeSongs.txt",downloading.searchedLog)
926                                                                 }else{
927                                                                         if (fs.existsSync(filePath+"alternativeSongs.txt")) fs.unlinkSync(filePath+"alternativeSongs.txt");
928                                                                 }
929                                                         }
930                                                         if (downloading.settings.createM3UFile){
931                                                                 fs.writeFileSync(filePath + "playlist.m3u", downloading.playlistArr.join("\r\n"));
932                                                         }
933                                                         if (downloading.settings.saveArtwork){
934                                                                 if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
935                                                                 let imgPath = filePath + antiDot(fixName(settingsRegexCover(downloading.settings.coverImageTemplate,downloading.artist,downloading.name)))+(downloading.settings.PNGcovers ? ".png" : ".jpg");
936                                                                 if (downloading.cover){
937                                                                         request.get(downloading.cover, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
938                                                                                 if(error){
939                                                                                         logger.error(error.stack);
940                                                                                         return;
941                                                                                 }
942                                                                                 fs.outputFile(imgPath,body,'binary',function(err){
943                                                                                         if(err){
944                                                                                                 logger.error(err.stack);
945                                                                                                 return;
946                                                                                         }
947                                                                                         logger.info(`Cover downloaded for: ${downloading.settings.plName}`)
948                                                                                 })
949                                                                         });
950                                                                 }
951                                                         }
952                                                         if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]];
953                                                         s.currentItem = null;
954                                                         queueDownload(getNextDownload());
955                                                 }).catch((err)=>{
956                                                         if (err) return logger.error(err.stack);
957                                                         logger.info("Stopping the playlist queue");
958                                                         if (downloading && s.downloadQueue[Object.keys(s.downloadQueue)[0]] && (Object.keys(s.downloadQueue)[0] == downloading.queueId)) delete s.downloadQueue[Object.keys(s.downloadQueue)[0]];
959                                                         s.currentItem = null;
960                                                         queueDownload(getNextDownload());
961                                                 });
962                                         }).catch((err)=>{
963                                                 logger.error('Something went wrong!'+err.stack);
964                                         });
965                                 }).catch((err)=>{
966                                         logger.error('Something went wrong!'+err.stack);
967                                 });
968                         }else{
969                                 s.emit("message", {title: "Spotify Support is not enabled", msg: "You should add authCredentials.js in your config files 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>"})
970                         }
971                         break;
972                 */
973                 }
974         }
975
976         function cancelDownload(queueId){
977                 if (!queueId) return
978                 let cancel = false
979                 let cancelSuccess
980                 if (s.downloadQueue[queueId]){
981                         cancel = true;
982                         delete s.downloadQueue[queueId];
983                 }
984                 if (s.currentItem && s.currentItem.queueId == queueId) {
985                         cancelSuccess = s.Deezer.cancelDecryptTrack(queueId);
986                         s.trackQueue = queue({
987                                 autostart: true,
988                                 concurrency: s.trackQueue.concurrency
989                         })
990                         cancel = cancel || cancelSuccess;
991                 }
992                 if (cancel) {
993                         s.emit("cancelDownload", {queueId: queueId});
994                 }
995         }
996         s.on("cancelDownload", function (data) {cancelDownload(data.queueId)});
997
998         s.on("cancelAllDownloads", function(data){
999                 data.queueList.forEach(x=>{
1000                         cancelDownload(x);
1001                 })
1002         })
1003
1004         s.on("downloadAlreadyInQueue", function (data) {
1005                 if (data.id) {
1006                         return;
1007                 }
1008                 let isInQueue = checkIfAlreadyInQueue(data.id);
1009                 if (isInQueue) {
1010                         s.emit("downloadAlreadyInQueue", {alreadyInQueue: true, id: data.id, queueId: isInQueue});
1011                 } else {
1012                         s.emit("downloadAlreadyInQueue", {alreadyInQueue: false, id: data.id});
1013                 }
1014         });
1015
1016         function checkIfAlreadyInQueue(id) {
1017                 let exists = false;
1018                 Object.keys(s.downloadQueue).forEach(x=>{
1019                         if (s.downloadQueue[x].id == id) {
1020                                 exists = s.downloadQueue[i].queueId;
1021                         }
1022                 });
1023                 if (s.currentItem && (s.currentItem.id == id)) {
1024                         exists = s.currentItem.queueId;
1025                 }
1026                 return exists;
1027         }
1028
1029         async function downloadTrackObject(track, queueId, settings) {
1030                 if (!s.downloadQueue[queueId]) {
1031                         logger.error(`Failed to download ${track.mainArtist.name} - ${track.title}: Not in queue`)
1032                         return
1033                 }
1034                 if (track.id == 0){
1035                         logger.error(`Failed to download ${track.mainArtist.name} - ${track.title}: Wrong ID`)
1036                         return
1037                 }
1038                 //track.trackSocket = socket
1039
1040                 /* Album information is necessary for the following tags:
1041                  * albumArtist
1042                  * artistImage
1043                  * trackTotal
1044                  * rtype
1045                  * barcode
1046                  * explicit_lyrics
1047                  * label
1048                  * genres
1049                  * release_date
1050                 */
1051                 var ajson
1052                 try{
1053                         ajson = await s.Deezer.legacyGetAlbum(track.album.id)
1054                 }catch(err){
1055                         logger.warn("Album not found, trying to reach deeper")
1056                         try{
1057                                 ajson = await s.Deezer.getAlbum(track.album.id)
1058                         } catch(err){
1059                                 if(track.fallbackId){
1060                                         logger.warn("Failed to download track, falling on alternative")
1061                                         track = await s.Deezer.getTrack(track.fallbackId)
1062                                         return downloadTrackObject(track, queueId, settings)
1063                                 }else{
1064                                         logger.error(`Failed to download ${track.mainArtist.name} - ${track.name}: ${err}`)
1065                                         return
1066                                 }
1067                         }
1068                 }
1069
1070                 // Aquiring discTotal (only if necessary)
1071                 if (ajson.totalDiskNumber){
1072                         track.discTotal = ajson.totalDiskNumber
1073                 }else{
1074                         if (((settings.tags.discTotal || settings.createCDFolder) && parseInt(track.id)>0)){
1075                                 logger.info("Getting total disc number");
1076                                 track.discTotal = await s.Deezer.legacyGetTrack(ajson.tracks.data[ajson.tracks.data.length-1].id).disk_number
1077                         }
1078                 }
1079
1080                 // Aquiring bpm (only if necessary)
1081                 if ((settings.tags.bpm && parseInt(track.id)>0)){
1082                         logger.info("Getting BPM");
1083                         try{
1084                                 track.bpm = await s.Deezer.legacyGetTrack(track.id).bpm
1085                         }catch(err){
1086                                 track.bpm = 0
1087                         }
1088                 }else{
1089                         track.bpm = 0
1090                 }
1091
1092                 if (settings.removeAlbumVersion){
1093                         if(track.title.indexOf("Album Version")>-1){
1094                                 track.title = track.title.replace(/\(Album Version\)/g,"")
1095                                 track.title.trim()
1096                         }
1097                 }
1098
1099                 let separator = settings.multitagSeparator
1100                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16))
1101                 track.albumArtist = {
1102                         id: ajson.artist.id,
1103                         name: ajson.artist.name,
1104                         picture: ajson.artist.picture_small.split("56x56-000000-80-0-0.jpg")[0],
1105                 }
1106                 track.albumArtist.pictureUrl = `${track.albumArtist.picture}${settings.artworkSize}x${settings.artworkSize}-000000-80-0-0${(settings.PNGcovers ? ".png" : ".jpg")}`
1107                 track.album.pictureUrl = `${s.Deezer.albumPicturesHost}${track.album.picture}\\${settings.artworkSize}x${settings.artworkSize}-000000-80-0-0${(settings.PNGcovers ? ".png" : ".jpg")}`
1108                 track.trackTotal = ajson.nb_tracks
1109                 if (!ajson.record_type){
1110                         track.recordType = swichReleaseType(track.recordType)
1111                 }else{
1112                         track.recordType = ajson.record_type
1113                 }
1114                 track.album.barcode = ajson.upc
1115
1116                 if (ajson.explicit_lyrics){
1117                         track.album.explicit = ajson.explicit_lyrics;
1118                 }
1119                 if(track.contributor){
1120                         if(track.contributor.composer){
1121                                 track.composerString = [];
1122                                 uniqueArray(track.contributor.composer, track.composerString, settings.removeDupedTags)
1123                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.composerString = track.composerString.join(separator);
1124                         }
1125                         if(track.contributor.musicpublisher){
1126                                 track.musicpublisherString = [];
1127                                 uniqueArray(track.contributor.musicpublisher, track.musicpublisherString, settings.removeDupedTags)
1128                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.musicpublisherString = track.musicpublisherString.join(separator);
1129                         }
1130                         if(track.contributor.producer){
1131                                 track.producerString = [];
1132                                 uniqueArray(track.contributor.producer, track.producerString, settings.removeDupedTags)
1133                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.producerString = track.producerString.join(separator);
1134                         }
1135                         if(track.contributor.engineer){
1136                                 track.engineerString = [];
1137                                 uniqueArray(track.contributor.engineer, track.engineerString, settings.removeDupedTags)
1138                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.engineerString = track.engineerString.join(separator);
1139                         }
1140                         if(track.contributor.writer){
1141                                 track.writerString = [];
1142                                 uniqueArray(track.contributor.writer, track.writerString, settings.removeDupedTags)
1143                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.writerString = track.writerString.join(separator);
1144                         }
1145                         if(track.contributor.author){
1146                                 track.authorString = [];
1147                                 uniqueArray(track.contributor.author, track.authorString, settings.removeDupedTags)
1148                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.authorString = track.authorString.join(separator);
1149                         }
1150                         if(track.contributor.mixer){
1151                                 track.mixerString = [];
1152                                 uniqueArray(track.contributor.mixer, track.mixerString, settings.removeDupedTags)
1153                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.mixerString = track.mixerString.join(separator);
1154                         }
1155                 }
1156
1157                 if(ajson.label){
1158                         track.publisher = ajson.label;
1159                 }
1160
1161                 if(track.artist && typeof track.artist == "object"){
1162                         track.artistsString = [];
1163                         artistArray = []
1164                         track.artist.forEach(function(artist){
1165                                 artistArray.push(artist.name);
1166                         });
1167                         uniqueArray(artistArray, track.artistsString, settings.removeDupedTags)
1168                         let posMainArtist = track.artistsString.indexOf(track.albumArtist.name)
1169                         if (posMainArtist !== -1 && posMainArtist !== 0 && settings.removeDupedTags){
1170                                 let element = track.artistsString[posMainArtist];
1171                         track.artistsString.splice(posMainArtist, 1);
1172                         track.artistsString.splice(0, 0, element);
1173                         }
1174                         if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.artistsString = track.artistsString.join(separator);
1175                 }
1176
1177                 if(ajson.genres && ajson.genres.data[0] && ajson.genres.data[0].name){
1178                         track.genreString = [];
1179                         genreArray = [];
1180                         ajson.genres.data.forEach(function(genre){
1181                                 genreArray.push(genre.name);
1182                         });
1183                         uniqueArray(genreArray, track.genreString, false)
1184                         if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) track.genreString = track.genreString.join(separator);
1185                 }
1186
1187                 if (ajson.release_date) {
1188                         track.date.year = ajson.release_date.slice(0, 4);
1189                         track.date = {
1190                                 day: ajson.release_date.slice(8,10),
1191                                 month: ajson.release_date.slice(5,7),
1192                                 slicedYear: (settings.dateFormatYear == "2" ? ajson.release_date.slice(2, 4) : ajson.release_date.slice(0, 4))
1193                         }
1194                 }
1195                 if (track.date){
1196                         let date
1197                         switch (settings.dateFormat){
1198                                 case "0": date = `${track.date.slicedYear}-${track.date.month}-${track.date.day}`; break;
1199                                 case "1": date = `${track.date.day}-${track.date.month}-${track.date.slicedYear}`; break;
1200                                 case "2": date = `${track.date.month}-${track.date.day}-${track.date.slicedYear}`; break;
1201                                 case "3": date = `${track.date.slicedYear}-${track.date.day}-${track.date.month}`; break;
1202                                 case "4": date = `${track.date.day}${track.date.month}`; break;
1203                                 default: date = `${track.date.day}${track.date.month}`; break;
1204                         }
1205                         track.dateString = date;
1206                 }
1207                 if(settings.plName && !(settings.createArtistFolder || settings.createAlbumFolder) && !settings.numplaylistbyalbum){
1208                         track.playlist.trackNumber = (position+1).toString();
1209                         track.playlist.trackTotal = settings.playlist.fullSize;
1210                         track.playlist.discNumber = "1";
1211                         track.playlist.discTotal = "1";
1212                 }
1213
1214                 // TODO: Move to a separate function
1215                 // Generating file name
1216                 if (settings.saveFullArtists && settings.multitagSeparator != null){
1217                         let filename = fixName(`${track.artistsString} - ${track.title}`);
1218                 }else{
1219                         let filename = fixName(`${track.mainArtist.name} - ${track.title}`);
1220                 }
1221                 if (settings.filename) {
1222                         filename = fixName(settingsRegex(track, settings.filename, settings.playlist, settings.saveFullArtists && settings.multitagSeparator != null, settings.paddingSize));
1223                 }
1224
1225                 // TODO: Move to a separate function
1226                 // Generating file path
1227                 let filepath = mainFolder;
1228                 let artistPath;
1229                 if (settings.createArtistFolder || settings.createAlbumFolder) {
1230                         if(settings.plName){
1231                                 filepath += antiDot(fixName(settings.plName)) + path.sep;
1232                         }
1233                         if (settings.createArtistFolder) {
1234                                 if(settings.artName){
1235                                         filepath += antiDot(fixName(settings.artName)) + path.sep;
1236                                 }else{
1237                                         filepath += antiDot(fixName(track.albumArtist.name)) + path.sep;
1238                                 }
1239                                 artistPath = filepath;
1240                         }
1241
1242                         if (settings.createAlbumFolder) {
1243                                 if(settings.artName){
1244                                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,settings.artName,settings.albName,track.date.year,track.recordType,track.album.explicit,track.publisher))) + path.sep;
1245                                 }else{
1246                                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,track.albumArtist.name,track.album.name,track.date.year,track.recordType,track.album.explicit,track.publisher))) + path.sep;
1247                                 }
1248                         }
1249                 } else if (settings.plName) {
1250                         filepath += antiDot(fixName(settings.plName)) + path.sep;
1251                 } else if (settings.artName) {
1252                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,settings.artName,settings.albName,track.date.year,track.recordType,track.album.explicit,track.publisher))) + path.sep;
1253                 }
1254                 let coverpath = filepath;
1255                 if (track.discTotal > 1 && (settings.artName || settings.createAlbumFolder) && settings.createCDFolder){
1256                         filepath += `CD${track.discNumber +  path.sep}`
1257                 }
1258                 let writePath;
1259                 // TODO: Use id instead, filename only at the end after tagging
1260                 if(track.format == 9){
1261                         writePath = filepath + filename + '.flac';
1262                 }else{
1263                         writePath = filepath + filename + '.mp3';
1264                 }
1265                 if(track.syncLyrics && settings.syncedlyrics){
1266                         fs.outputFile(writePath.substring(0,writePath.lastIndexOf('.'))+".lrc",track.syncLyrics,function(){});
1267                 }
1268                 let playlistData = [0,""]
1269                 if (settings.createM3UFile && (settings.plName || settings.albName)) {
1270                         if (t.index){
1271                                 playlistData = [parseInt(t.index), writePath];
1272                         }else{
1273                                 playlistData = [track.trackNumber-1, writePath];
1274                         }
1275                 }
1276                 if (fs.existsSync(writePath)) {
1277                         logger.info("Already downloaded: " + track.mainArtist.name + ' - ' + track.title);
1278                         return;
1279                 }else{
1280                         logger.info('Downloading file to ' + writePath);
1281                 }
1282                 // Get cover image
1283                 if (track.album.pictureUrl) {
1284                         let imgPath;
1285                         //If its not from an album but a playlist.
1286                         if(!(settings.albName || settings.createAlbumFolder)){
1287                                 imgPath = coverArtFolder + (track.album.barcode ? fixName(track.album.barcode) : fixName(`${track.albumArtist.name} - ${track.album.name}`))+(settings.PNGcovers ? ".png" : ".jpg");
1288                         }else{
1289                                 if (settings.saveArtwork)
1290                                         imgPath = coverpath + fixName(settingsRegexCover(settings.coverImageTemplate,settings.artName,settings.albName))+(settings.PNGcovers ? ".png" : ".jpg");
1291                                 else
1292                                         imgPath = coverArtFolder + fixName(track.album.barcode ? fixName(track.album.barcode) : fixName(`${track.albumArtist.name} - ${track.album.name}`))+(settings.PNGcovers ? ".png" : ".jpg");
1293                         }
1294                         if(fs.existsSync(imgPath)){
1295                                 track.album.picturePath = (imgPath).replace(/\\/g, "/")
1296                                 logger.info("Starting the download process CODE:1")
1297                         }else{
1298                                 try{
1299                                         var body = await request.get(track.album.pictureUrl, {strictSSL: false,encoding: 'binary'})
1300                                         fs.outputFileSync(imgPath,body,'binary')
1301                                         track.album.picturePath = (imgPath).replace(/\\/g, "/")
1302                                         logger.info("Starting the download process CODE:2")
1303                                 }catch(error){
1304                                         logger.error("Cannot download Album Image: "+error.stack)
1305                                         track.album.pictureUrl = undefined
1306                                         track.album.picturePath = undefined
1307                                 }
1308                         }
1309                 }else{
1310                         track.album.pictureUrl = undefined
1311                         logger.info("Starting the download process CODE:3")
1312                 }
1313
1314                 // Get Artist Image
1315                 if (track.albumArtist.picture && settings.saveArtworkArtist) {
1316                         let imgPath;
1317                         if(settings.createArtistFolder){
1318                                 imgPath = artistPath + antiDot(fixName(settingsRegexArtistCover(settings.artistImageTemplate,track.albumArtist.name)))+(settings.PNGcovers ? ".png" : ".jpg");
1319                                 if(!fs.existsSync(imgPath)){
1320                                         try{
1321                                                 var body = await request.get(track.albumArtist.pictureUrl, {strictSSL: false,encoding: 'binary'})
1322                                                 if (body.indexOf("unauthorized")>-1) throw new Error("Unauthorized")
1323                                                 fs.outputFileSync(imgPath,body,'binary')
1324                                                 logger.info("Saved Artist Image")
1325                                         }catch(err){
1326                                                 logger.error("Cannot download Artist Image: "+err.stack)
1327                                         }
1328                                 }
1329                         }
1330                 }
1331                 let tempPath
1332                 if(parseInt(track.id)>0)
1333                         tempPath = writePath+".temp"
1334                 else
1335                         tempPath = writePath
1336
1337                 // TODO: Add Code to download Track
1338                 logger.info("Downloading track")
1339
1340                 // TODO: Add code to decrypt Track
1341                 logger.info("Decrypting track")
1342
1343                 /*
1344                 if (parseInt(t.id)>0){
1345                         if(track.format == 9){
1346                                 let flacComments = [];
1347                                 if (settings.tags.title)
1348                                         flacComments.push('TITLE=' + metadata.title);
1349                                 if (settings.tags.album)
1350                                         flacComments.push('ALBUM=' + metadata.album);
1351                                 if (settings.tags.albumArtist)
1352                                         flacComments.push('ALBUMARTIST=' + metadata.albumArtist);
1353                                 if (settings.tags.trackNumber)
1354                                         flacComments.push('TRACKNUMBER=' + metadata.trackNumber);
1355                                 if (settings.tags.discNumber)
1356                                         flacComments.push('DISCNUMBER=' + metadata.discNumber);
1357                                 if (settings.tags.trackTotal)
1358                                         flacComments.push('TRACKTOTAL=' + metadata.trackTotal);
1359                                 if (settings.tags.explicit)
1360                                         flacComments.push('ITUNESADVISORY=' + metadata.explicit);
1361                                 if (settings.tags.isrc)
1362                                         flacComments.push('ISRC=' + metadata.ISRC);
1363                                 if (settings.tags.artist && metadata.artists)
1364                                         if (Array.isArray(metadata.artists)){
1365                                                 metadata.artists.forEach(x=>{
1366                                                         flacComments.push('ARTIST=' + x);
1367                                                 });
1368                                         }else{
1369                                                 flacComments.push('ARTIST=' + metadata.artists);
1370                                         }
1371                                 if (settings.tags.discTotal)
1372                                         flacComments.push('DISCTOTAL='+splitNumber(metadata.discTotal,true));
1373                                 if (settings.tags.length)
1374                                         flacComments.push('LENGTH=' + metadata.length);
1375                                 if (settings.tags.barcode && metadata.barcode)
1376                                         flacComments.push('BARCODE=' + metadata.barcode);
1377                                 if (metadata.unsynchronisedLyrics && settings.tags.unsynchronisedLyrics)
1378                                         flacComments.push('LYRICS='+metadata.unsynchronisedLyrics.lyrics);
1379                                 if (metadata.genre && settings.tags.genre)
1380                                         if (Array.isArray(metadata.genre)){
1381                                                 metadata.genre.forEach(x=>{
1382                                                         flacComments.push('GENRE=' + x);
1383                                                 });
1384                                         }else{
1385                                                 flacComments.push('GENRE=' + metadata.genre);
1386                                         }
1387                                 if (metadata.copyright && settings.tags.copyright)
1388                                         flacComments.push('COPYRIGHT=' + metadata.copyright);
1389                                 if (0 < parseInt(metadata.year)){
1390                                         if (settings.tags.year)
1391                                                 flacComments.push('YEAR=' + metadata.year);
1392                                         if (settings.tags.date)
1393                                         flacComments.push('DATE=' + metadata.date);
1394                                 }
1395                                 if (0 < parseInt(metadata.bpm) && settings.tags.bpm)
1396                                         flacComments.push('BPM=' + metadata.bpm);
1397                                 if(metadata.publisher && settings.tags.publisher)
1398                                         flacComments.push('PUBLISHER=' + metadata.publisher);
1399                                 if(metadata.composer && settings.tags.composer)
1400                                         if (Array.isArray(metadata.composer)){
1401                                                 metadata.composer.forEach(x=>{
1402                                                         flacComments.push('COMPOSER=' + x);
1403                                                 });
1404                                         }else{
1405                                                 flacComments.push('COMPOSER=' + metadata.composer);
1406                                         }
1407                                 if(metadata.musicpublisher && settings.tags.musicpublisher)
1408                                         if (Array.isArray(metadata.musicpublisher)){
1409                                                 metadata.musicpublisher.forEach(x=>{
1410                                                         flacComments.push('ORGANIZATION=' + x);
1411                                                 });
1412                                         }else{
1413                                                 flacComments.push('ORGANIZATION=' + metadata.musicpublisher);
1414                                         }
1415                                 if(metadata.mixer && settings.tags.mixer)
1416                                         if (Array.isArray(metadata.mixer)){
1417                                                 metadata.mixer.forEach(x=>{
1418                                                         flacComments.push('MIXER=' + x);
1419                                                 });
1420                                         }else{
1421                                                 flacComments.push('MIXER=' + metadata.mixer);
1422                                         }
1423                                 if(metadata.author && settings.tags.author)
1424                                         if (Array.isArray(metadata.author)){
1425                                                 metadata.author.forEach(x=>{
1426                                                         flacComments.push('AUTHOR=' + x);
1427                                                 });
1428                                         }else{
1429                                                 flacComments.push('AUTHOR=' + metadata.author);
1430                                         }
1431                                 if(metadata.writer && settings.tags.writer)
1432                                         if (Array.isArray(metadata.writer)){
1433                                                 metadata.writer.forEach(x=>{
1434                                                         flacComments.push('WRITER=' + x);
1435                                                 });
1436                                         }else{
1437                                                 flacComments.push('WRITER=' + metadata.writer);
1438                                         }
1439                                 if(metadata.engineer && settings.tags.engineer)
1440                                         if (Array.isArray(metadata.engineer)){
1441                                                 metadata.engineer.forEach(x=>{
1442                                                         flacComments.push('ENGINEER=' + x);
1443                                                 });
1444                                         }else{
1445                                                 flacComments.push('ENGINEER=' + metadata.engineer);
1446                                         }
1447                                 if(metadata.producer && settings.tags.producer)
1448                                         if (Array.isArray(metadata.producer)){
1449                                                 metadata.producer.forEach(x=>{
1450                                                         flacComments.push('PRODUCER=' + x);
1451                                                 });
1452                                         }else{
1453                                                 flacComments.push('PRODUCER=' + metadata.producer);
1454                                         }
1455                                 if(metadata.replayGain && settings.tags.replayGain)
1456                                         flacComments.push('REPLAYGAIN_TRACK_GAIN=' + metadata.replayGain);
1457
1458                                 const reader = fs.createReadStream(tempPath);
1459                                 const writer = fs.createWriteStream(writePath);
1460                                 let processor = new mflac.Processor({parseMetaDataBlocks: true});
1461                                 let vendor = 'reference libFLAC 1.2.1 20070917';
1462                                 let cover = null;
1463                                 if(metadata.imagePath && settings.tags.cover){
1464                                         cover = fs.readFileSync(metadata.imagePath);
1465                                 }
1466                                 let mdbVorbisPicture;
1467                                 let mdbVorbisComment;
1468                                 processor.on('preprocess', (mdb) => {
1469                                         // Remove existing VORBIS_COMMENT and PICTURE blocks, if any.
1470                                         if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type) {
1471                                                 mdb.remove();
1472                                         } else if (mflac.Processor.MDB_TYPE_PICTURE === mdb.type) {
1473                                                 mdb.remove();
1474                                         }
1475                                         if (mdb.isLast) {
1476                                                 if(cover){
1477                                                         mdbVorbisPicture = mflac.data.MetaDataBlockPicture.create(true, 3, `image/${(settings.PNGcovers ? "png" : "jpeg")}`, '', settings.artworkSize, settings.artworkSize, 24, 0, cover);
1478                                                 }
1479                                                 mdbVorbisComment = mflac.data.MetaDataBlockVorbisComment.create(!cover, vendor, flacComments);
1480                                                 mdb.isLast = false;
1481                                         }
1482                                 });
1483                                 processor.on('postprocess', (mdb) => {
1484                                         if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type && null !== mdb.vendor) {
1485                                                 vendor = mdb.vendor;
1486                                         }
1487                                         if (mdbVorbisPicture && mdbVorbisComment) {
1488                                                         processor.push(mdbVorbisComment.publish());
1489                                                         processor.push(mdbVorbisPicture.publish());
1490                                                 }else if(mdbVorbisComment){
1491                                                         processor.push(mdbVorbisComment.publish());
1492                                         }
1493                                 });
1494                                 reader.on('end', () => {
1495                                         fs.remove(tempPath);
1496                                 });
1497                                 await reader.pipe(processor).pipe(writer);
1498                         }else{
1499                                 const songBuffer = fs.readFileSync(tempPath);
1500                                 const writer = new ID3Writer(songBuffer);
1501                                 if (settings.tags.title)
1502                                         writer.setFrame('TIT2', metadata.title);
1503                                 if (settings.tags.artist)
1504                                         writer.setFrame('TPE1', [metadata.artists]);
1505                                 if (settings.tags.album)
1506                                         writer.setFrame('TALB', metadata.album)
1507                                 if (settings.tags.albumArtist && metadata.albumArtist)
1508                                         writer.setFrame('TPE2', metadata.albumArtist)
1509                                 if (settings.tags.trackNumber)
1510                                         writer.setFrame('TRCK', (settings.tags.trackTotal ? metadata.trackNumber+"/"+metadata.trackTotal : metadata.trackNumber))
1511                                 if (settings.tags.discNumber)
1512                                         writer.setFrame('TPOS', (settings.tags.discTotal ? metadata.discNumber+"/"+metadata.discTotal : metadata.discNumber))
1513                                 if (settings.tags.isrc)
1514                                         writer.setFrame('TSRC', metadata.ISRC);
1515
1516                                 if (settings.tags.length)
1517                                         writer.setFrame('TLEN', metadata.length);
1518                                 if (settings.tags.barcode && metadata.barcode)
1519                                         writer.setFrame('TXXX', {
1520                                                 description: 'BARCODE',
1521                                                 value: metadata.barcode
1522                                         });
1523                                 if(metadata.imagePath && settings.tags.cover){
1524                                         const coverBuffer = fs.readFileSync(metadata.imagePath);
1525                                         writer.setFrame('APIC', {
1526                                                 type: 3,
1527                                                 data: coverBuffer,
1528                                                 description: ''
1529                                         });
1530                                 }
1531                                 if(metadata.unsynchronisedLyrics && settings.tags.unsynchronisedLyrics)
1532                                         writer.setFrame('USLT', metadata.unsynchronisedLyrics);
1533                                 if(metadata.publisher && settings.tags.publisher)
1534                                         writer.setFrame('TPUB', metadata.publisher);
1535                                 if(metadata.genre && settings.tags.genre)
1536                                         writer.setFrame('TCON', [metadata.genre]);
1537                                 if(metadata.copyright && settings.tags.copyright)
1538                                         writer.setFrame('TCOP', metadata.copyright);
1539                                 if (0 < parseInt(metadata.year)) {
1540                                         if (settings.tags.date)
1541                                                 writer.setFrame('TDAT', metadata.date);
1542                                         if (settings.tags.year)
1543                                                 writer.setFrame('TYER', metadata.year);
1544                                 }
1545                                 if (0 < parseInt(metadata.bpm) && settings.tags.bpm)
1546                                         writer.setFrame('TBPM', metadata.bpm);
1547                                 if(metadata.composer && settings.tags.composer)
1548                                         writer.setFrame('TCOM', [metadata.composer]);
1549                                 if(metadata.replayGain && settings.tags.replayGain)
1550                                         writer.setFrame('TXXX', {
1551                                                 description: 'REPLAYGAIN_TRACK_GAIN',
1552                                                 value: metadata.replayGain
1553                                         });
1554                                 writer.addTag();
1555                                 const taggedSongBuffer = Buffer.from(writer.arrayBuffer);
1556                                 fs.writeFileSync(writePath, taggedSongBuffer);
1557                                 fs.remove(tempPath);
1558                         }
1559
1560                 }*/
1561                 logger.info("Downloaded: " + track.mainArtist.name + " - " + track.title)
1562         }
1563 });
1564
1565 // Helper functions
1566
1567 /**
1568  * Updates individual parameters in the settings file
1569  * @param config
1570  * @param value
1571  */
1572 function updateSettingsFile(config, value) {
1573         configFile.userDefined[config] = value;
1574
1575         fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
1576                 if (err) return;
1577                 logger.info("Settings updated");
1578
1579                 // FIXME: Endless Loop, due to call from initFolders()...crashes soon after startup
1580                 // initFolders();
1581         });
1582 }
1583
1584 function fixName (txt) {
1585   const regEx = /[\0\/\\:*?"<>|]/g;
1586   return txt.replace(regEx, '_');
1587 }
1588
1589 function antiDot(str){
1590         while(str[str.length-1] == "." || str[str.length-1] == " " || str[str.length-1] == "\n"){
1591                 str = str.substring(0,str.length-1);
1592         }
1593         if(str.length < 1){
1594                 str = "dot";
1595         }
1596         return fixName(str);
1597 }
1598
1599 /**
1600  * Initialize the temp folder for covers and main folder for downloads
1601  */
1602 function initFolders() {
1603         // Check if main folder exists
1604         if (!fs.existsSync(mainFolder)) {
1605                 mainFolder = defaultDownloadFolder;
1606                 updateSettingsFile('downloadLocation', defaultDownloadFolder);
1607         }
1608         //fs.removeSync(coverArtFolder);
1609         //fs.ensureFolderSync(coverArtFolder);
1610 }
1611
1612 /**
1613  * Creates the name of the tracks replacing wildcards to correct metadata
1614  * @param metadata
1615  * @param filename
1616  * @param playlist
1617  * @returns {XML|string|*}
1618  */
1619 function settingsRegex(metadata, filename, playlist, saveFullArtists, paddingSize) {
1620         filename = filename.replace(/%title%/g, metadata.title);
1621         filename = filename.replace(/%album%/g, metadata.album);
1622         filename = filename.replace(/%artist%/g, (saveFullArtists ? metadata.artists : metadata.artist));
1623         filename = filename.replace(/%year%/g, metadata.year);
1624         filename = filename.replace(/%label%/g, metadata.publisher);
1625         if(typeof metadata.trackNumber != 'undefined'){
1626                 if(configFile.userDefined.padtrck){
1627                          filename = filename.replace(/%number%/g, pad(metadata.trackNumber, (parseInt(paddingSize)>0 ? parseInt(paddingSize) : metadata.trackTotal)));
1628                 }else{
1629                         filename = filename.replace(/%number%/g, metadata.trackNumber);
1630                 }
1631         } else {
1632                 filename = filename.replace(/%number%/g, '');
1633         }
1634         filename = filename.replace(/%explicit%/g, (metadata.explicit==="1" ? (filename.indexOf(/[^%]explicit/g)>-1 ? "" : "(Explicit Version)") : ""));
1635         return filename.trim();
1636 }
1637
1638 /**
1639  * Creates the name of the albums folder replacing wildcards to correct metadata
1640  * @param metadata
1641  * @param foldername
1642  * @returns {XML|string|*}
1643  */
1644 function settingsRegexAlbum(foldername, artist, album, year, rtype, explicit, publisher) {
1645         foldername = foldername.replace(/%album%/g, album);
1646         foldername = foldername.replace(/%artist%/g, artist);
1647         foldername = foldername.replace(/%year%/g, year);
1648         if (rtype){
1649                 foldername = foldername.replace(/%type%/g, rtype[0].toUpperCase() + rtype.substring(1));
1650         }else{
1651                 foldername = foldername.replace(/%type%/g, "");
1652         }
1653         foldername = foldername.replace(/%label%/g, publisher);
1654         foldername = foldername.replace(/%explicit%/g, (explicit ? (foldername.indexOf(/[^%]explicit/g)>-1 ? "" : "(Explicit)") : ""));
1655         return foldername.trim();
1656 }
1657
1658 function settingsRegexCover(foldername, artist, name) {
1659         foldername = foldername.replace(/%name%/g, name);
1660         foldername = foldername.replace(/%artist%/g, artist);
1661         return foldername;
1662 }
1663
1664 function settingsRegexArtistCover(foldername, artist) {
1665         foldername = foldername.replace(/%artist%/g, artist);
1666         return foldername;
1667 }
1668
1669 /**
1670  * Pad number with 0s so max and str have the same nuber of characters
1671  * @param str
1672  * @param max
1673  * @returns {String|string|*}
1674  */
1675 function pad(str, max) {
1676         str = str.toString();
1677         max = max.toString();
1678         return str.length < max.length || str.length == 1 ? pad("0" + str, max) : str;
1679 }
1680
1681 /**
1682  * Splits the %number%
1683  * @param string str
1684  * @return string
1685  */
1686 function splitNumber(str,total){
1687         str = str.toString();
1688         let i = str.indexOf("/");
1689         if(total && i > 0){
1690                 return str.slice(i+1, str.length);
1691         }else if(i > 0){
1692                 return str.slice(0, i);
1693         }else{
1694                 return str;
1695         }
1696         return i > 0 ? str.slice(0, i) : str;
1697 }
1698
1699 function swichReleaseType(id){
1700         switch (id) {
1701                 case "0":
1702                         return "Album";
1703                 case "1":
1704                         return "Single";
1705                 case "3":
1706                         return "EP";
1707                 default:
1708                         return id;
1709         }
1710 }
1711
1712 function uniqueArray(origin, destination, removeDupes=true){
1713         Array.from(new Set(origin)).forEach(function(x){
1714                 if(destination.indexOf(x) == -1)
1715                         destination.push(x);
1716         });
1717         if (removeDupes){
1718                 destination.forEach((name,index)=>{
1719                         destination.forEach((name2,index2)=>{
1720                                 if(!(index===index2) && (name.indexOf(name2)!== -1)){
1721                                         destination.splice(index, 1);
1722                                 }
1723                         })
1724                 })
1725         }
1726 }
1727
1728 /*
1729 // TODO: Make the API do this
1730 function slimDownAlbumInfo(ajsonOld){
1731         let ajson = {};
1732         ajson.artist = {}
1733         ajson.artist.name = ajsonOld.artist.name
1734         ajson.artist.picture_small = ajsonOld.artist.picture_small
1735         ajson.nb_tracks = ajsonOld.nb_tracks
1736         ajson.upc = ajsonOld.upc
1737         ajson.record_type = ajsonOld.record_type
1738         ajson.label = ajsonOld.label
1739         ajson.genres = ajsonOld.genres
1740         ajson.explicit_lyrics = ajsonOld.explicit_lyrics
1741         ajson.release_date = ajsonOld.release_date
1742         ajson.tracks = {
1743                 data: ajsonOld.tracks.data.map(x=>{
1744                         return {id: x.id};
1745                 })
1746         }
1747         ajson.tracks.total = ajsonOld.tracks.total
1748         return ajson
1749 }
1750
1751 */
1752
1753 // Show crash error in console for debugging
1754 process.on('unhandledRejection', function (err) {
1755         logger.error(err.stack)
1756 })
1757 process.on('uncaughtException', function (err) {
1758         logger.error(err.stack)
1759 })
1760
1761 // Exporting vars
1762 module.exports.mainFolder = mainFolder
1763 module.exports.defaultSettings = defaultSettings
1764 module.exports.defaultDownloadFolder = defaultDownloadFolder