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