Testing new repo
[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                 let filename = fixName(`${metadata.artist} - ${metadata.title}`);
1232                 if (settings.filename) {
1233                         filename = fixName(settingsRegex(metadata, settings.filename, settings.playlist));
1234                 }
1235                 let filepath = mainFolder;
1236                 let artistPath;
1237                 if (settings.createArtistFolder || settings.createAlbumFolder) {
1238                         if(settings.plName){
1239                                 filepath += antiDot(fixName(settings.plName)) + path.sep;
1240                         }
1241                         if (settings.createArtistFolder) {
1242                                 if(settings.artName){
1243                                         filepath += antiDot(fixName(settings.artName)) + path.sep;
1244                                 }else{
1245                                         filepath += antiDot(fixName(metadata.albumArtist)) + path.sep;
1246                                 }
1247                                 artistPath = filepath;
1248                         }
1249
1250                         if (settings.createAlbumFolder) {
1251                                 if(settings.artName){
1252                                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,settings.artName,settings.albName,metadata.year,metadata.rtype,metadata.albumExplicit,metadata.publisher))) + path.sep;
1253                                 }else{
1254                                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,metadata.albumArtist,metadata.album,metadata.year,metadata.rtype,metadata.albumExplicit,metadata.publisher))) + path.sep;
1255                                 }
1256                         }
1257                 } else if (settings.plName) {
1258                         filepath += antiDot(fixName(settings.plName)) + path.sep;
1259                 } else if (settings.artName) {
1260                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,settings.artName,settings.albName,metadata.year,metadata.rtype,metadata.albumExplicit,metadata.publisher))) + path.sep;
1261                 }
1262                 let coverpath = filepath;
1263                 if (metadata.discTotal > 1 && (settings.artName || settings.createAlbumFolder) && settings.createCDFolder){
1264                         filepath += `CD${metadata.discNumber +  path.sep}`
1265                 }
1266                 let writePath;
1267                 if(track.format == 9){
1268                         writePath = filepath + filename + '.flac';
1269                 }else{
1270                         writePath = filepath + filename + '.mp3';
1271                 }
1272                 if(track["LYRICS_SYNC_JSON"] && settings.syncedlyrics){
1273                         let lyricsbuffer = "";
1274                         for(let i=0;i<track["LYRICS_SYNC_JSON"].length;i++){
1275                                 if(track["LYRICS_SYNC_JSON"][i].lrc_timestamp){
1276                                         lyricsbuffer += track["LYRICS_SYNC_JSON"][i].lrc_timestamp+track["LYRICS_SYNC_JSON"][i].line+"\r\n";
1277                                 }else if(i+1 < track["LYRICS_SYNC_JSON"].length){
1278                                         lyricsbuffer += track["LYRICS_SYNC_JSON"][i+1].lrc_timestamp+track["LYRICS_SYNC_JSON"][i].line+"\r\n";
1279                                 }
1280                         }
1281                         fs.outputFile(writePath.substring(0,writePath.lastIndexOf('.'))+".lrc",lyricsbuffer,function(){});
1282                 }
1283                 let playlistData = [0,""]
1284                 if (settings.createM3UFile && (settings.plName || settings.albName)) {
1285                         if (t.index){
1286                                 playlistData = [parseInt(t.index), writePath];
1287                         }else{
1288                                 playlistData = [metadata.trackNumber-1, writePath];
1289                         }
1290                 }
1291                 if (fs.existsSync(writePath)) {
1292                         logger.info("Already downloaded: " + metadata.artist + ' - ' + metadata.title);
1293                         callback(null, {playlistData: playlistData, searched: t.searched});
1294                         return;
1295                 }else{
1296                         logger.info('Downloading file to ' + writePath);
1297                 }
1298                 //Get image
1299                 temp = new Promise((resolve, reject)=>{
1300                         if (metadata.image) {
1301                                 let imgPath;
1302                                 //If its not from an album but a playlist.
1303                                 if(!(settings.albName || settings.createAlbumFolder)){
1304                                         imgPath = coverArtFolder + (metadata.barcode ? fixName(metadata.barcode) : fixName(`${metadata.albumArtist} - ${metadata.album}`))+(settings.PNGcovers ? ".png" : ".jpg");
1305                                 }else{
1306                                         if (settings.saveArtwork)
1307                                                 imgPath = coverpath + fixName(settingsRegexCover(settings.coverImageTemplate,settings.artName,settings.albName))+(settings.PNGcovers ? ".png" : ".jpg");
1308                                         else
1309                                                 imgPath = coverArtFolder + fixName(metadata.barcode ? fixName(metadata.barcode) : fixName(`${metadata.albumArtist} - ${metadata.album}`))+(settings.PNGcovers ? ".png" : ".jpg");
1310                                 }
1311                                 if(fs.existsSync(imgPath)){
1312                                         metadata.imagePath = (imgPath).replace(/\\/g, "/");
1313                                         logger.info("Starting the download process CODE:1");
1314                                         resolve();
1315                                 }else{
1316                                         request.get(metadata.image, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
1317                                                 if(error){
1318                                                         logger.error(error.stack);
1319                                                         metadata.image = undefined;
1320                                                         metadata.imagePath = undefined;
1321                                                         return;
1322                                                 }
1323                                                 fs.outputFile(imgPath,body,'binary',function(err){
1324                                                         if(err){
1325                                                                 logger.error(err.stack);
1326                                                         metadata.image = undefined;
1327                                                         metadata.imagePath = undefined;
1328                                                                 return;
1329                                                         }
1330                                                         metadata.imagePath = (imgPath).replace(/\\/g, "/");
1331                                                         logger.info("Starting the download process CODE:2");
1332                                                         resolve();
1333                                                 })
1334                                         });
1335                                 }
1336                         }else{
1337                                 metadata.image = undefined;
1338                                 logger.info("Starting the download process CODE:3");
1339                                 resolve();
1340                         }
1341                 })
1342                 temp.then(()=>{
1343                 temp = new Promise((resolve, reject)=>{
1344                         if (metadata.artistImage && settings.saveArtworkArtist) {
1345                                 let imgPath;
1346                                 if(settings.createArtistFolder){
1347                                         imgPath = artistPath + antiDot(fixName(settingsRegexArtistCover(settings.artistImageTemplate,metadata.albumArtist)))+(settings.PNGcovers ? ".png" : ".jpg");
1348                                         if(fs.existsSync(imgPath)){
1349                                                 resolve();
1350                                         }else{
1351                                                 request.get(metadata.artistImage, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
1352                                                         if(error){
1353                                                                 logger.error(error.stack);
1354                                                                 return;
1355                                                         }
1356                                                         if (body.indexOf("unauthorized")>-1) return resolve();
1357                                                         fs.outputFile(imgPath,body,'binary',function(err){
1358                                                                 if(err){
1359                                                                         logger.error(err.stack);
1360                                                                         return;
1361                                                                 }
1362                                                                 logger.info("Saved Artist Image");
1363                                                                 resolve();
1364                                                         })
1365                                                 });
1366                                         }
1367                                 }else{
1368                                         resolve();
1369                                 }
1370                         }else{
1371                                 resolve();
1372                         }
1373                 })
1374                 temp.then(()=>{
1375                 let tempPath
1376                 if(parseInt(t.id)>0)
1377                         tempPath = writePath+".temp"
1378                 else
1379                         tempPath = writePath;
1380                 logger.info("Downloading and decrypting");
1381                 Deezer.decryptTrack(tempPath, track, t.queueId, function (err) {
1382                         if (err && err.message == "aborted") {
1383                                 logger.info("Track got aborted");
1384                                 t.trackSocket = null
1385                                 callback(null, {playlistData: playlistData, searched: t.searched});
1386                                 return;
1387                         }
1388                         if (err) {
1389                                 if (t.fallback){
1390                                         logger.warn("Failed to download: " + metadata.artist + " - " + metadata.title+", falling on alternative");
1391                                         t.id = t.fallback
1392                                         t.fallback = 0
1393                                         settings.trackInfo = null;
1394                                         downloadTrack(t, settings, JSON.parse(JSON.stringify(metadata)), callback);
1395                                 }else if(!t.searched){
1396                                         logger.warn("Failed to download track, searching for alternative");
1397                                         Deezer.track2ID(t.artist, t.name, null, data=>{
1398                                                 t.searched = true;
1399                                                 t.id = data.id;
1400                                                 t.artist = data.artist;
1401                                                 t.name = data.name;
1402                                                 downloadTrack(t, settings, JSON.parse(JSON.stringify(metadata)), callback);
1403                                         });
1404                                 }else{
1405                                         logger.error(`Failed to download ${t.artist} - ${t.name}: ${err}`);
1406                                         callback(err)
1407                                 }
1408                                 return;
1409                         }
1410                         logger.info("Downloaded: " + metadata.artist + " - " + metadata.title);
1411                         if (parseInt(t.id)>0){
1412                                 if(track.format == 9){
1413                                         let flacComments = [];
1414                                         if (settings.tags.title)
1415                                                 flacComments.push('TITLE=' + metadata.title);
1416                                         if (settings.tags.album)
1417                                                 flacComments.push('ALBUM=' + metadata.album);
1418                                         if (settings.tags.albumArtist)
1419                                                 flacComments.push('ALBUMARTIST=' + metadata.albumArtist);
1420                                         if (settings.tags.trackNumber)
1421                                                 flacComments.push('TRACKNUMBER=' + metadata.trackNumber);
1422                                         if (settings.tags.discNumber)
1423                                                 flacComments.push('DISCNUMBER=' + metadata.discNumber);
1424                                         if (settings.tags.trackTotal)
1425                                                 flacComments.push('TRACKTOTAL=' + metadata.trackTotal);
1426                                         if (settings.tags.explicit)
1427                                                 flacComments.push('ITUNESADVISORY=' + metadata.explicit);
1428                                         if (settings.tags.isrc)
1429                                                 flacComments.push('ISRC=' + metadata.ISRC);
1430                                         if (settings.tags.artist && metadata.artists)
1431                                                 if (Array.isArray(metadata.artists)){
1432                                                         metadata.artists.forEach(x=>{
1433                                                                 flacComments.push('ARTIST=' + x);
1434                                                         });
1435                                                 }else{
1436                                                         flacComments.push('ARTIST=' + metadata.artists);
1437                                                 }
1438                                         if (settings.tags.discTotal)
1439                                                 flacComments.push('DISCTOTAL='+splitNumber(metadata.discTotal,true));
1440                                         if (settings.tags.length)
1441                                                 flacComments.push('LENGTH=' + metadata.length);
1442                                         if (settings.tags.barcode && metadata.barcode)
1443                                                 flacComments.push('BARCODE=' + metadata.barcode);
1444                                         if (metadata.unsynchronisedLyrics && settings.tags.unsynchronisedLyrics)
1445                                                 flacComments.push('LYRICS='+metadata.unsynchronisedLyrics.lyrics);
1446                                         if (metadata.genre && settings.tags.genre)
1447                                                 if (Array.isArray(metadata.genre)){
1448                                                         metadata.genre.forEach(x=>{
1449                                                                 flacComments.push('GENRE=' + x);
1450                                                         });
1451                                                 }else{
1452                                                         flacComments.push('GENRE=' + metadata.genre);
1453                                                 }
1454                                         if (metadata.copyright && settings.tags.copyright)
1455                                                 flacComments.push('COPYRIGHT=' + metadata.copyright);
1456                                         if (0 < parseInt(metadata.year)){
1457                                                 if (settings.tags.year)
1458                                                         flacComments.push('YEAR=' + metadata.year);
1459                                                 if (settings.tags.date)
1460                                                 flacComments.push('DATE=' + metadata.date);
1461                                         }
1462                                         if (0 < parseInt(metadata.bpm) && settings.tags.bpm)
1463                                                 flacComments.push('BPM=' + metadata.bpm);
1464                                         if(metadata.publisher && settings.tags.publisher)
1465                                                 flacComments.push('PUBLISHER=' + metadata.publisher);
1466                                         if(metadata.composer && settings.tags.composer)
1467                                                 if (Array.isArray(metadata.composer)){
1468                                                         metadata.composer.forEach(x=>{
1469                                                                 flacComments.push('COMPOSER=' + x);
1470                                                         });
1471                                                 }else{
1472                                                         flacComments.push('COMPOSER=' + metadata.composer);
1473                                                 }
1474                                         if(metadata.musicpublisher && settings.tags.musicpublisher)
1475                                                 if (Array.isArray(metadata.musicpublisher)){
1476                                                         metadata.musicpublisher.forEach(x=>{
1477                                                                 flacComments.push('ORGANIZATION=' + x);
1478                                                         });
1479                                                 }else{
1480                                                         flacComments.push('ORGANIZATION=' + metadata.musicpublisher);
1481                                                 }
1482                                         if(metadata.mixer && settings.tags.mixer)
1483                                                 if (Array.isArray(metadata.mixer)){
1484                                                         metadata.mixer.forEach(x=>{
1485                                                                 flacComments.push('MIXER=' + x);
1486                                                         });
1487                                                 }else{
1488                                                         flacComments.push('MIXER=' + metadata.mixer);
1489                                                 }
1490                                         if(metadata.author && settings.tags.author)
1491                                                 if (Array.isArray(metadata.author)){
1492                                                         metadata.author.forEach(x=>{
1493                                                                 flacComments.push('AUTHOR=' + x);
1494                                                         });
1495                                                 }else{
1496                                                         flacComments.push('AUTHOR=' + metadata.author);
1497                                                 }
1498                                         if(metadata.writer && settings.tags.writer)
1499                                                 if (Array.isArray(metadata.writer)){
1500                                                         metadata.writer.forEach(x=>{
1501                                                                 flacComments.push('WRITER=' + x);
1502                                                         });
1503                                                 }else{
1504                                                         flacComments.push('WRITER=' + metadata.writer);
1505                                                 }
1506                                         if(metadata.engineer && settings.tags.engineer)
1507                                                 if (Array.isArray(metadata.engineer)){
1508                                                         metadata.engineer.forEach(x=>{
1509                                                                 flacComments.push('ENGINEER=' + x);
1510                                                         });
1511                                                 }else{
1512                                                         flacComments.push('ENGINEER=' + metadata.engineer);
1513                                                 }
1514                                         if(metadata.producer && settings.tags.producer)
1515                                                 if (Array.isArray(metadata.producer)){
1516                                                         metadata.producer.forEach(x=>{
1517                                                                 flacComments.push('PRODUCER=' + x);
1518                                                         });
1519                                                 }else{
1520                                                         flacComments.push('PRODUCER=' + metadata.producer);
1521                                                 }
1522                                         if(metadata.replayGain && settings.tags.replayGain)
1523                                                 flacComments.push('REPLAYGAIN_TRACK_GAIN=' + metadata.replayGain);
1524
1525                                         const reader = fs.createReadStream(tempPath);
1526                                         const writer = fs.createWriteStream(writePath);
1527                                         let processor = new mflac.Processor({parseMetaDataBlocks: true});
1528                                         let vendor = 'reference libFLAC 1.2.1 20070917';
1529                                         let cover = null;
1530                                         if(metadata.imagePath && settings.tags.cover){
1531                                                 cover = fs.readFileSync(metadata.imagePath);
1532                                         }
1533                                         let mdbVorbisPicture;
1534                                         let mdbVorbisComment;
1535                                         processor.on('preprocess', (mdb) => {
1536                                                 // Remove existing VORBIS_COMMENT and PICTURE blocks, if any.
1537                                                 if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type) {
1538                                                         mdb.remove();
1539                                                 } else if (mflac.Processor.MDB_TYPE_PICTURE === mdb.type) {
1540                                                         mdb.remove();
1541                                                 }
1542                                                 if (mdb.isLast) {
1543                                                         if(cover){
1544                                                                 mdbVorbisPicture = mflac.data.MetaDataBlockPicture.create(true, 3, `image/${(settings.PNGcovers ? "png" : "jpeg")}`, '', settings.artworkSize, settings.artworkSize, 24, 0, cover);
1545                                                         }
1546                                                         mdbVorbisComment = mflac.data.MetaDataBlockVorbisComment.create(!cover, vendor, flacComments);
1547                                                         mdb.isLast = false;
1548                                                 }
1549                                         });
1550                                         processor.on('postprocess', (mdb) => {
1551                                                 if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type && null !== mdb.vendor) {
1552                                                         vendor = mdb.vendor;
1553                                                 }
1554                                                 if (mdbVorbisPicture && mdbVorbisComment) {
1555                                                                 processor.push(mdbVorbisComment.publish());
1556                                                                 processor.push(mdbVorbisPicture.publish());
1557                                                         }else if(mdbVorbisComment){
1558                                                                 processor.push(mdbVorbisComment.publish());
1559                                                 }
1560                                         });
1561                                         reader.on('end', () => {
1562                                                 fs.remove(tempPath);
1563                                         });
1564                                         reader.pipe(processor).pipe(writer);
1565                                 }else{
1566                                         const songBuffer = fs.readFileSync(tempPath);
1567                                         const writer = new ID3Writer(songBuffer);
1568                                         if (settings.tags.title)
1569                                                 writer.setFrame('TIT2', metadata.title);
1570                                         if (settings.tags.artist)
1571                                                 writer.setFrame('TPE1', [metadata.artists]);
1572                                         if (settings.tags.album)
1573                                                 writer.setFrame('TALB', metadata.album)
1574                                         if (settings.tags.albumArtist && metadata.albumArtist)
1575                                                 writer.setFrame('TPE2', metadata.albumArtist)
1576                                         if (settings.tags.trackNumber)
1577                                                 writer.setFrame('TRCK', (settings.tags.trackTotal ? metadata.trackNumber+"/"+metadata.trackTotal : metadata.trackNumber))
1578                                         if (settings.tags.discNumber)
1579                                                 writer.setFrame('TPOS', (settings.tags.discTotal ? metadata.discNumber+"/"+metadata.discTotal : metadata.discNumber))
1580                                         if (settings.tags.isrc)
1581                                                 writer.setFrame('TSRC', metadata.ISRC);
1582
1583                                         if (settings.tags.length)
1584                                                 writer.setFrame('TLEN', metadata.length);
1585                                         if (settings.tags.barcode && metadata.barcode)
1586                                                 writer.setFrame('TXXX', {
1587                                                         description: 'BARCODE',
1588                                                         value: metadata.barcode
1589                                                 });
1590                                         if(metadata.imagePath && settings.tags.cover){
1591                                                 const coverBuffer = fs.readFileSync(metadata.imagePath);
1592                                                 writer.setFrame('APIC', {
1593                                                         type: 3,
1594                                                         data: coverBuffer,
1595                                                         description: ''
1596                                                 });
1597                                         }
1598                                         if(metadata.unsynchronisedLyrics && settings.tags.unsynchronisedLyrics)
1599                                                 writer.setFrame('USLT', metadata.unsynchronisedLyrics);
1600                                         if(metadata.publisher && settings.tags.publisher)
1601                                                 writer.setFrame('TPUB', metadata.publisher);
1602                                         if(metadata.genre && settings.tags.genre)
1603                                                 writer.setFrame('TCON', [metadata.genre]);
1604                                         if(metadata.copyright && settings.tags.copyright)
1605                                                 writer.setFrame('TCOP', metadata.copyright);
1606                                         if (0 < parseInt(metadata.year)) {
1607                                                 if (settings.tags.date)
1608                                                         writer.setFrame('TDAT', metadata.date);
1609                                                 if (settings.tags.year)
1610                                                         writer.setFrame('TYER', metadata.year);
1611                                         }
1612                                         if (0 < parseInt(metadata.bpm) && settings.tags.bpm)
1613                                                 writer.setFrame('TBPM', metadata.bpm);
1614                                         if(metadata.composer && settings.tags.composer)
1615                                                 writer.setFrame('TCOM', [metadata.composer]);
1616                                         if(metadata.replayGain && settings.tags.replayGain)
1617                                                 writer.setFrame('TXXX', {
1618                                                         description: 'REPLAYGAIN_TRACK_GAIN',
1619                                                         value: metadata.replayGain
1620                                                 });
1621                                         writer.addTag();
1622                                         const taggedSongBuffer = Buffer.from(writer.arrayBuffer);
1623                                         fs.writeFileSync(writePath, taggedSongBuffer);
1624                                         fs.remove(tempPath);
1625                                 }
1626                         }
1627                         callback(null, {playlistData: playlistData, searched: t.searched});
1628                 })
1629         })
1630         })
1631         })
1632         })
1633         })
1634         })
1635         }
1636
1637         function checkIfAlreadyInQueue(id) {
1638                 let exists = false;
1639                 Object.keys(socket.downloadQueue).forEach(x=>{
1640                         if (socket.downloadQueue[x].id == id) {
1641                                 exists = socket.downloadQueue[i].queueId;
1642                         }
1643                 });
1644                 if (socket.currentItem && (socket.currentItem.id == id)) {
1645                         exists = socket.currentItem.queueId;
1646                 }
1647                 return exists;
1648         }
1649 });
1650
1651 // Helper functions
1652
1653 /**
1654  * Updates individual parameters in the settings file
1655  * @param config
1656  * @param value
1657  */
1658 function updateSettingsFile(config, value) {
1659         configFile.userDefined[config] = value;
1660
1661         fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
1662                 if (err) return;
1663                 logger.info("Settings updated");
1664
1665                 // FIXME: Endless Loop, due to call from initFolders()...crashes soon after startup
1666                 // initFolders();
1667         });
1668 }
1669
1670 function fixName (txt) {
1671   const regEx = /[\0\/\\:*?"<>|]/g;
1672   return txt.replace(regEx, '_');
1673 }
1674
1675 function antiDot(str){
1676         while(str[str.length-1] == "." || str[str.length-1] == " " || str[str.length-1] == "\n"){
1677                 str = str.substring(0,str.length-1);
1678         }
1679         if(str.length < 1){
1680                 str = "dot";
1681         }
1682         return fixName(str);
1683 }
1684
1685 /**
1686  * Initialize the temp folder for covers and main folder for downloads
1687  */
1688 function initFolders() {
1689         // Check if main folder exists
1690         if (!fs.existsSync(mainFolder)) {
1691                 mainFolder = defaultDownloadDir;
1692                 updateSettingsFile('downloadLocation', defaultDownloadDir);
1693         }
1694         //fs.removeSync(coverArtFolder);
1695         fs.ensureDirSync(coverArtFolder);
1696 }
1697
1698 /**
1699  * Creates the name of the tracks replacing wildcards to correct metadata
1700  * @param metadata
1701  * @param filename
1702  * @param playlist
1703  * @returns {XML|string|*}
1704  */
1705 function settingsRegex(metadata, filename, playlist) {
1706         filename = filename.replace(/%title%/g, metadata.title);
1707         filename = filename.replace(/%album%/g, metadata.album);
1708         filename = filename.replace(/%artist%/g, metadata.artist);
1709         filename = filename.replace(/%year%/g, metadata.year);
1710         filename = filename.replace(/%label%/g, metadata.publisher);
1711         if(typeof metadata.trackNumber != 'undefined'){
1712                 if(configFile.userDefined.padtrck){
1713                          filename = filename.replace(/%number%/g, pad(metadata.trackNumber, metadata.trackTotal));
1714                 }else{
1715                         filename = filename.replace(/%number%/g, metadata.trackNumber);
1716                 }
1717         } else {
1718                 filename = filename.replace(/%number%/g, '');
1719         }
1720         filename = filename.replace(/%explicit%/g, (metadata.explicit ? (filename.indexOf(/[^%]explicit/g)>-1 ? "" : "(Explicit Version)") : ""));
1721         return filename.trim();
1722 }
1723
1724 /**
1725  * Creates the name of the albums folder replacing wildcards to correct metadata
1726  * @param metadata
1727  * @param foldername
1728  * @returns {XML|string|*}
1729  */
1730 function settingsRegexAlbum(foldername, artist, album, year, rtype, explicit, publisher) {
1731         foldername = foldername.replace(/%album%/g, album);
1732         foldername = foldername.replace(/%artist%/g, artist);
1733         foldername = foldername.replace(/%year%/g, year);
1734         if (rtype){
1735                 foldername = foldername.replace(/%type%/g, rtype[0].toUpperCase() + rtype.substring(1));
1736         }else{
1737                 foldername = foldername.replace(/%type%/g, "");
1738         }
1739         foldername = foldername.replace(/%label%/g, publisher);
1740         foldername = foldername.replace(/%explicit%/g, (explicit ? (foldername.indexOf(/[^%]explicit/g)>-1 ? "" : "(Explicit)") : ""));
1741         return foldername.trim();
1742 }
1743
1744 function settingsRegexCover(foldername, artist, name) {
1745         foldername = foldername.replace(/%name%/g, name);
1746         foldername = foldername.replace(/%artist%/g, artist);
1747         return foldername;
1748 }
1749
1750 function settingsRegexArtistCover(foldername, artist) {
1751         foldername = foldername.replace(/%artist%/g, artist);
1752         return foldername;
1753 }
1754
1755 /**
1756  * I really don't understand what this does ... but it does something
1757  * @param str
1758  * @param max
1759  * @returns {String|string|*}
1760  */
1761 function pad(str, max) {
1762         str = str.toString();
1763         max = max.toString();
1764         return str.length < max.length || str.length == 1 ? pad("0" + str, max) : str;
1765 }
1766
1767 /**
1768  * Splits the %number%
1769  * @param string str
1770  * @return string
1771  */
1772 function splitNumber(str,total){
1773         str = str.toString();
1774         let i = str.indexOf("/");
1775         if(total && i > 0){
1776                 return str.slice(i+1, str.length);
1777         }else if(i > 0){
1778                 return str.slice(0, i);
1779         }else{
1780                 return str;
1781         }
1782         return i > 0 ? str.slice(0, i) : str;
1783 }
1784
1785 function slimDownTrackInfo(trackOld){
1786         let track = {};
1787         track['SNG_ID'] = trackOld["SNG_ID"]
1788         track['ARTISTS'] = trackOld["ARTISTS"]
1789         track["ALB_ID"] = trackOld["ALB_ID"]
1790         track["ALB_PICTURE"] = trackOld["ALB_PICTURE"]
1791         track["ART_PICTURE"] = trackOld["ART_PICTURE"]
1792         track["ALB_TITLE"] = trackOld["ALB_TITLE"]
1793         track["ART_NAME"] = trackOld["ART_NAME"]
1794         track["BPM"] = trackOld["BPM"]
1795         track["COPYRIGHT"] = trackOld["COPYRIGHT"]
1796         track["DISK_NUMBER"] = trackOld["DISK_NUMBER"]
1797         track["DURATION"] = trackOld["DURATION"]
1798         track["EXPLICIT_LYRICS"] = trackOld["EXPLICIT_LYRICS"]
1799         track["GAIN"] = trackOld["GAIN"]
1800         track["ISRC"] = trackOld["ISRC"]
1801         track["TYPE"] = trackOld["TYPE"]
1802         track["LYRICS_SYNC_JSON"] = trackOld["LYRICS_SYNC_JSON"]
1803         track["LYRICS_TEXT"] = trackOld["LYRICS_TEXT"]
1804         track["PHYSICAL_RELEASE_DATE"] = trackOld["PHYSICAL_RELEASE_DATE"]
1805         track["SNG_CONTRIBUTORS"] = trackOld["SNG_CONTRIBUTORS"]
1806         track["SNG_TITLE"] = trackOld["SNG_TITLE"]
1807         track["TRACK_NUMBER"] = trackOld["TRACK_NUMBER"]
1808         track["VERSION"] = trackOld["VERSION"]
1809         track["FILESIZE_FLAC"] = trackOld["FILESIZE_FLAC"]
1810         track["FILESIZE_MP3_320"] = trackOld["FILESIZE_MP3_320"]
1811         track["FILESIZE_MP3_256"] = trackOld["FILESIZE_MP3_256"]
1812         track["FILESIZE_MP3_128"] = trackOld["FILESIZE_MP3_128"]
1813         track.FILESIZE = trackOld.FILESIZE
1814         track["FALLBACK"] = trackOld["FALLBACK"]
1815         track.downloadUrl = trackOld.downloadUrl
1816         track.format = trackOld.format
1817         return track
1818 }
1819
1820 function slimDownAlbumInfo(ajsonOld){
1821         let ajson = {};
1822         ajson.artist = {}
1823         ajson.artist.name = ajsonOld.artist.name
1824         ajson.artist.picture_small = ajsonOld.artist.picture_small
1825         ajson.nb_tracks = ajsonOld.nb_tracks
1826         ajson.upc = ajsonOld.upc
1827         ajson.record_type = ajsonOld.record_type
1828         ajson.label = ajsonOld.label
1829         ajson.genres = ajsonOld.genres
1830         ajson.explicit_lyrics = ajsonOld.explicit_lyrics
1831         ajson.release_date = ajsonOld.release_date
1832         ajson.tracks = {
1833                 data: ajsonOld.tracks.data.map(x=>{
1834                         return {id: x.id};
1835                 })
1836         }
1837         ajson.tracks.total = ajsonOld.tracks.total
1838         return ajson
1839 }
1840
1841 function swichReleaseType(id){
1842         switch (id) {
1843                 case "0":
1844                         return "Album";
1845                 case "1":
1846                         return "Single";
1847                 case "3":
1848                         return "Ep";
1849                 default:
1850                         return id;
1851         }
1852 }
1853
1854 function parseMetadata(track, ajson, totalDiskNumber, settings, position, altmetadata){
1855         let metadata;
1856         if (track["VERSION"]) track["SNG_TITLE"] += " " + track["VERSION"];
1857         if (settings.removeAlbumVersion){
1858                 if(track["SNG_TITLE"].indexOf("Album Version")>-1){
1859                         track["SNG_TITLE"] = track["SNG_TITLE"].replace(/\(Album Version\)/g,"")
1860                         track["SNG_TITLE"].trim()
1861                 }
1862         }
1863         if(altmetadata){
1864                 metadata = altmetadata;
1865                 if(track["LYRICS_TEXT"] && !metadata.unsynchronisedLyrics){
1866                         metadata.unsynchronisedLyrics = {
1867                                 description: "",
1868                                 lyrics: track["LYRICS_TEXT"]
1869                         };
1870                 }
1871         }else{
1872                 metadata = {
1873                         title: track["SNG_TITLE"],
1874                         artist: track["ART_NAME"],
1875                         album: track["ALB_TITLE"],
1876                         trackNumber: track["TRACK_NUMBER"],
1877                         discNumber: track["DISK_NUMBER"],
1878                         explicit: track["EXPLICIT_LYRICS"],
1879                         ISRC: track["ISRC"],
1880                         albumArtist: ajson.artist.name,
1881                         trackTotal: ajson.nb_tracks,
1882                         rtype: ajson.record_type,
1883                         barcode: ajson.upc,
1884                         length: track["DURATION"]
1885                 };
1886                 if(track["COPYRIGHT"]){
1887                         metadata.copyright = track["COPYRIGHT"];
1888                 }
1889                 if (!metadata.rtype){
1890                         metadata.rtype = swichReleaseType(track["TYPE"])
1891                 }
1892                 if (ajson.explicit_lyrics){
1893                         metadata.albumExplicit = ajson.explicit_lyrics;
1894                 }
1895                 if(track["SNG_CONTRIBUTORS"]){
1896                         if(track["SNG_CONTRIBUTORS"].composer){
1897                                 metadata.composer = [];
1898                                 Array.from(new Set(track["SNG_CONTRIBUTORS"].composer)).forEach(function(x){
1899                                         if(metadata.composer.indexOf(x) == -1)
1900                                                 metadata.composer.push(x);
1901                                 });
1902                                 let separator = settings.multitagSeparator;
1903                                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1904                                 if (track.format != 9) metadata.composer = metadata.composer.join(separator);
1905                         }
1906                         if(track["SNG_CONTRIBUTORS"].musicpublisher){
1907                                 metadata.musicpublisher = [];
1908                                 Array.from(new Set(track["SNG_CONTRIBUTORS"].musicpublisher)).forEach(function(x){
1909                                         if(metadata.musicpublisher.indexOf(x) == -1)
1910                                                 metadata.musicpublisher.push(x);
1911                                 });
1912                                 let separator = settings.multitagSeparator;
1913                                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1914                                 if (track.format != 9) metadata.musicpublisher = metadata.musicpublisher.join(separator);
1915                         }
1916                         if(track["SNG_CONTRIBUTORS"].producer){
1917                                 metadata.producer = [];
1918                                 Array.from(new Set(track["SNG_CONTRIBUTORS"].producer)).forEach(function(x){
1919                                         if(metadata.producer.indexOf(x) == -1)
1920                                                 metadata.producer.push(x);
1921                                 });
1922                                 let separator = settings.multitagSeparator;
1923                                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1924                                 if (track.format != 9) metadata.producer = metadata.producer.join(separator);
1925                         }
1926                         if(track["SNG_CONTRIBUTORS"].engineer){
1927                                 metadata.engineer = [];
1928                                 Array.from(new Set(track["SNG_CONTRIBUTORS"].engineer)).forEach(function(x){
1929                                         if(metadata.engineer.indexOf(x) == -1)
1930                                                 metadata.engineer.push(x);
1931                                 });
1932                                 let separator = settings.multitagSeparator;
1933                                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1934                                 if (track.format != 9) metadata.engineer = metadata.engineer.join(separator);
1935                         }
1936                         if(track["SNG_CONTRIBUTORS"].writer){
1937                                 metadata.writer = [];
1938                                 Array.from(new Set(track["SNG_CONTRIBUTORS"].writer)).forEach(function(x){
1939                                         if(metadata.writer.indexOf(x) == -1)
1940                                                 metadata.writer.push(x);
1941                                 });
1942                                 let separator = settings.multitagSeparator;
1943                                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1944                                 if (track.format != 9) metadata.writer = metadata.writer.join(separator);
1945                         }
1946                         if(track["SNG_CONTRIBUTORS"].author){
1947                                 metadata.author = [];
1948                                 Array.from(new Set(track["SNG_CONTRIBUTORS"].author)).forEach(function(x){
1949                                         if(metadata.author.indexOf(x) == -1)
1950                                                 metadata.author.push(x);
1951                                 });
1952                                 let separator = settings.multitagSeparator;
1953                                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1954                                 if (track.format != 9) metadata.author = metadata.author.join(separator);
1955                         }
1956                         if(track["SNG_CONTRIBUTORS"].mixer){
1957                                 metadata.mixer = [];
1958                                 Array.from(new Set(track["SNG_CONTRIBUTORS"].mixer)).forEach(function(x){
1959                                         if(metadata.mixer.indexOf(x) == -1)
1960                                                 metadata.mixer.push(x);
1961                                 });
1962                                 let separator = settings.multitagSeparator;
1963                                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1964                                 if (track.format != 9) metadata.mixer = metadata.mixer.join(separator);
1965                         }
1966                 }
1967                 if(track["LYRICS_TEXT"]){
1968                         metadata.unsynchronisedLyrics = {
1969                                 description: "",
1970                                 lyrics: track["LYRICS_TEXT"]
1971                         };
1972                 }
1973                 if (track["GAIN"]) {
1974                         metadata.replayGain = track["GAIN"];
1975                 }
1976                 if(ajson.label){
1977                         metadata.publisher = ajson.label;
1978                 }
1979                 if (0 < parseInt(track["BPM"])) {
1980                         metadata.bpm = track["BPM"];
1981                 }
1982                 let separator = settings.multitagSeparator;
1983                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16))
1984                 if(track['ARTISTS']){
1985                         metadata.artists = [];
1986                         artistArray = []
1987                         track['ARTISTS'].forEach(function(artist){
1988                                 artistArray.push(artist['ART_NAME']);
1989                         });
1990                         Array.from(new Set(artistArray)).forEach(function(artist){
1991                                 if(metadata.artists.indexOf(artist) == -1)
1992                                         metadata.artists.push(artist);
1993                         });
1994                         if (track.format != 9) metadata.artists = metadata.artists.join(separator);
1995                 }
1996                 if(ajson.genres && ajson.genres.data[0] && ajson.genres.data[0].name){
1997                         metadata.genre = [];
1998                         genreArray = [];
1999                         ajson.genres.data.forEach(function(genre){
2000                                 genreArray.push(genre.name);
2001                         });
2002                         Array.from(new Set(genreArray)).forEach(function(genre){
2003                                 if(metadata.genre.indexOf(genre) == -1)
2004                                         metadata.genre.push(genre);
2005                         });
2006                         if (track.format != 9) metadata.genre = metadata.genre.join(separator);
2007                 }
2008                 if (track["ALB_PICTURE"]) {
2009                         metadata.image = Deezer.albumPicturesHost + track["ALB_PICTURE"]+"/"+settings.artworkSize+"x"+settings.artworkSize+"-000000-80-0-0"+(settings.PNGcovers ? ".png" : ".jpg");
2010                 }
2011                 if (ajson.artist.picture_small) {
2012                         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");
2013                 }
2014                 if (ajson.release_date) {
2015                         metadata.year = ajson.release_date.slice(0, 4);
2016                         metadata.date = {
2017                                 day: ajson.release_date.slice(8,10),
2018                                 month: ajson.release_date.slice(5,7),
2019                                 year: (settings.dateFormatYear == "2" ? ajson.release_date.slice(2, 4) : ajson.release_date.slice(0, 4))
2020                         }
2021                 } else if(track["PHYSICAL_RELEASE_DATE"]){
2022                         metadata.year = track["PHYSICAL_RELEASE_DATE"].slice(0, 4);
2023                         metadata.date = {
2024                                 day: track["PHYSICAL_RELEASE_DATE"].slice(8,10),
2025                                 month: track["PHYSICAL_RELEASE_DATE"].slice(5,7),
2026                                 year: (settings.dateFormatYear == "2" ? track["PHYSICAL_RELEASE_DATE"].slice(2, 4) : track["PHYSICAL_RELEASE_DATE"].slice(0, 4))
2027                         }
2028                 }
2029                 if (metadata.date){
2030                         let date
2031                         switch (settings.dateFormat){
2032                                 case "0": date = `${metadata.date.year}-${metadata.date.month}-${metadata.date.day}`; break;
2033                                 case "1": date = `${metadata.date.day}-${metadata.date.month}-${metadata.date.year}`; break;
2034                                 case "2": date = `${metadata.date.month}-${metadata.date.day}-${metadata.date.year}`; break;
2035                                 case "3": date = `${metadata.date.year}-${metadata.date.day}-${metadata.date.month}`; break;
2036                                 case "4": date = `${metadata.date.day}${metadata.date.month}`; break;
2037                                 default: date = `${metadata.date.day}${metadata.date.month}`; break;
2038                         }
2039                         metadata.date = date;
2040                 }
2041                 if(settings.plName && !(settings.createArtistFolder || settings.createAlbumFolder) && !settings.numplaylistbyalbum){
2042                         metadata.trackNumber = (position+1).toString();
2043                         metadata.trackTotal = settings.playlist.fullSize;
2044                         metadata.discNumber = "1";
2045                         metadata.discTotal = "1";
2046                 }
2047                 if (totalDiskNumber){
2048                         metadata.discTotal = totalDiskNumber;
2049                 }
2050         }
2051         return metadata;
2052 }
2053
2054 process.on('unhandledRejection', function (err) {
2055         logger.error(err.stack);
2056 });
2057 // Show crash error in console for debugging
2058 process.on('uncaughtException', function (err) {
2059         logger.error(err.stack);
2060 });
2061
2062 // Exporting vars
2063 module.exports.mainFolder = mainFolder;
2064 module.exports.defaultSettings = defaultSettings;
2065 module.exports.defaultDownloadDir = defaultDownloadDir;