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