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