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