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