Fixed %explicit% for Album Folders
[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         request({
101                 url: "https://notabug.org/RemixDevs/DeezloaderRemix/raw/master/update.json",
102                 json: true
103         }, function(error, response, body) {
104                 if (!error && response.statusCode === 200) {
105                         logger.info("Checking for updates")
106                         let [currentVersion_MAJOR, currentVersion_MINOR, currentVersion_PATCH] = package.version.split(".");
107                         let [lastVersion_MAJOR, lastVersion_MINOR, lastVersion_PATCH] = body.version.split(".");
108                         if (parseInt(lastVersion_MAJOR) > parseInt(currentVersion_MAJOR) || parseInt(lastVersion_MINOR) > parseInt(currentVersion_MINOR) || parseInt(lastVersion_PATCH) > parseInt(currentVersion_PATCH)) {
109                                 logger.info("Update Available");
110                                 socket.emit("message", {title: `Version ${lastVersion_MAJOR}.${lastVersion_MINOR}.${lastVersion_PATCH} is 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                         .replace(/—/g, "-");
904
905                 Deezer.search(encodeURIComponent(data.text), data.type, function (searchObject, err) {
906                         try {
907                                 socket.emit("search", {type: data.type, items: searchObject.data});
908                         } catch (e) {
909                                 socket.emit("search", {type: data.type, items: []});
910                         }
911                 });
912         });
913
914         socket.on("getTrackList", function (data) {
915                 if (!data.type || (["playlist", "album", "artist", "spotifyplaylist"].indexOf(data.type) == -1) || !data.id) {
916                         socket.emit("getTrackList", {err: -1, response: {}, id: data.id, reqType: data.type});
917                         return;
918                 }
919                 if (data.type == 'artist') {
920                         Deezer.getArtistAlbums(data.id, function (response, err) {
921                                 if (err) {
922                                         socket.emit("getTrackList", {err: "wrong id artist", response: {}, id: data.id, reqType: data.type});
923                                         return;
924                                 }
925                                 socket.emit("getTrackList", {response: response, id: data.id, reqType: data.type});
926                         });
927                 }else if(data.type == "spotifyplaylist"){
928                         spotyUser = data.id.slice(data.id.indexOf("user:")+5);
929                         spotyUser = spotyUser.substring(0, spotyUser.indexOf(":"));
930                         playlistID = data.id.slice(data.id.indexOf("playlist:")+9);
931
932                         spotifyApi.clientCredentialsGrant().then(function(creds) {
933                                 spotifyApi.setAccessToken(creds.body['access_token']);
934                                 return spotifyApi.getPlaylistTracks(spotyUser, playlistID, {fields: "items(track(artists,name,duration_ms,preview_url,explicit)),total"})
935                         }).then(function(resp) {
936                                 numPages=Math.floor((resp.body["total"]-1)/100);
937                                 let pages = []
938                                 let response = new Array(resp.body["total"]);
939                                 resp.body["items"].map((t,i)=>{
940                                         response[i]={
941                                                 explicit_lyrics: t.track.explicit,
942                                                 preview: t.track.preview_url,
943                                                 title: t.track.name,
944                                                 artist: {
945                                                         name: t.track.artists[0].name
946                                                 },
947                                                 duration: Math.floor(t.track.duration_ms/1000)
948                                         };
949                                 })
950                                 if (resp.body["total"]>100){
951                                         for (let offset = 1; offset<=numPages; offset++){
952                                                 pages.push(new Promise(function(resolvePage) {
953                                                         spotifyApi.getPlaylistTracks(spotyUser, playlistID, {fields: "items(track(artists,name,duration_ms,preview_url,explicit))", offset: offset*100}).then(function(resp){
954                                                                 resp.body['items'].forEach((t, index) => {
955                                                                         response[index+offset*100]={
956                                                                                 explicit_lyrics: t.track.explicit,
957                                                                                 preview: t.track.preview_url,
958                                                                                 title: t.track.name,
959                                                                                 artist: {
960                                                                                         name: t.track.artists[0].name
961                                                                                 },
962                                                                                 duration: Math.floor(t.track.duration_ms/1000)
963                                                                         };
964                                                                 });
965                                                                 resolvePage();
966                                                         });
967                                                 }));
968                                         }
969                                 }
970                                 Promise.all(pages).then((val)=>{
971                                         socket.emit("getTrackList", {response: {'data': response}, id: data.id, reqType: data.type});
972                                 })
973                         });
974                 }else{
975                         let reqType = data.type.charAt(0).toUpperCase() + data.type.slice(1);
976                         Deezer["get" + reqType + "Tracks"](data.id, function (response, err) {
977                                 if (err) {
978                                         socket.emit("getTrackList", {err: "wrong id "+reqType, response: {}, id: data.id, reqType: data.type});
979                                         return;
980                                 }
981                                 socket.emit("getTrackList", {response: response, id: data.id, reqType: data.type});
982                         });
983                 }
984         });
985
986         function socketCancelDownload(queueId){
987                 if (!queueId) {
988                         return;
989                 }
990                 let cancel = false;
991                 let cancelSuccess;
992                 if (socket.downloadQueue[queueId]){
993                         cancel = true;
994                         delete socket.downloadQueue[queueId];
995                 }
996                 if (socket.currentItem && socket.currentItem.queueId == queueId) {
997                         cancelSuccess = Deezer.cancelDecryptTrack(queueId);
998                         socket.trackQueue = queue({
999                                 autostart: true,
1000                                 concurrency: socket.trackQueue.concurrency
1001                         })
1002                         cancel = cancel || cancelSuccess;
1003                 }
1004                 if (cancel) {
1005                         socket.emit("cancelDownload", {queueId: queueId});
1006                 }
1007         }
1008         socket.on("cancelDownload", function (data) {socketCancelDownload(data.queueId)});
1009
1010         socket.on("cancelAllDownloads", function(data){
1011                 data.queueList.forEach(x=>{
1012                         socketCancelDownload(x);
1013                 })
1014         })
1015
1016         socket.on("downloadAlreadyInQueue", function (data) {
1017                 if (data.id) {
1018                         return;
1019                 }
1020                 let isInQueue = checkIfAlreadyInQueue(data.id);
1021                 if (isInQueue) {
1022                         socket.emit("downloadAlreadyInQueue", {alreadyInQueue: true, id: data.id, queueId: isInQueue});
1023                 } else {
1024                         socket.emit("downloadAlreadyInQueue", {alreadyInQueue: false, id: data.id});
1025                 }
1026         });
1027
1028         socket.on("getUserSettings", function () {
1029                 let settings = configFile.userDefined;
1030                 if (!settings.downloadLocation) {
1031                         settings.downloadLocation = mainFolder;
1032                 }
1033                 socket.emit('getUserSettings', {settings: settings});
1034         });
1035
1036         socket.on("saveSettings", function (settings) {
1037                 if (settings.userDefined.downloadLocation == defaultDownloadDir) {
1038                         settings.userDefined.downloadLocation = "";
1039                 } else {
1040                         settings.userDefined.downloadLocation = path.resolve(settings.userDefined.downloadLocation + path.sep) + path.sep;
1041                         mainFolder = settings.userDefined.downloadLocation;
1042                 }
1043
1044                 if (settings.userDefined.queueConcurrency < 1) settings.userDefined.queueConcurrency = 1;
1045
1046                 if (settings.userDefined.queueConcurrency != socket.trackQueue.concurrency){
1047                         socket.trackQueue.concurrency = settings.userDefined.queueConcurrency;
1048                 }
1049
1050                 if (settings.userDefined.chartsCountry != configFile.userDefined.chartsCountry){
1051                         socket.emit("setChartsCountry", {selected: settings.userDefined.chartsCountry});
1052                         socketGetChartsTrackListByCountry(settings.userDefined.chartsCountry);
1053                 }
1054
1055                 if (settings.userDefined.spotifyUser != configFile.userDefined.spotifyUser){
1056                         socketGetMePlaylistList(settings.userDefined.spotifyUser);
1057                 }
1058
1059                 configFile.userDefined = settings.userDefined;
1060                 fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
1061                         if (err) return;
1062                         logger.info("Settings updated");
1063                         initFolders();
1064                 });
1065         });
1066
1067         function downloadTrack(t, settings, altmetadata, callback) {
1068                 if (!socket.downloadQueue[t.queueId]) {
1069                         logger.error(`Failed to download ${t.artist} - ${t.name}: Not in queue`);
1070                         callback(new Error("Not in queue"));
1071                         return;
1072                 }
1073                 if (t.id == 0){
1074                         logger.error(`Failed to download ${t.artist} - ${t.name}: Wrong ID`);
1075                         callback(new Error("Wrong ID"));
1076                         return;
1077                 }
1078                 settings = settings || {};
1079                 let temp;
1080                 temp = new Promise((resolve, reject)=>{
1081                         if (!settings.trackInfo){
1082                                 logger.info("Getting track data");
1083                                 if (parseInt(t.id)<0){
1084                                         Deezer.getLocalTrack(t.id, function (trackInfo, err) {
1085                                                 if (err) {
1086                                                         if(!t.searched){
1087                                                                 logger.warn("Failed to download track, searching for alternative");
1088                                                                 Deezer.track2ID(t.artist, t.name, null, data=>{
1089                                                                         if (t.id != 0){
1090                                                                                 t.searched = true;
1091                                                                                 t.id = data.id;
1092                                                                                 t.artist = data.artist;
1093                                                                                 t.name = data.name;
1094                                                                                 downloadTrack(t, settings, null, callback);
1095                                                                         }else{
1096                                                                                 logger.error(`Failed to download ${t.artist} - ${t.name}: Searched alternative; Not found`);
1097                                                                                 callback(new Error("Searched alternative; Not found"));
1098                                                                         }
1099                                                                 });
1100                                                         }else{
1101                                                                 logger.error(`Failed to download ${t.artist} - ${t.name}: ${err}`);
1102                                                                 callback(err);
1103                                                         }
1104                                                         return;
1105                                                 }
1106                                                 resolve(trackInfo);
1107                                         });
1108                                 }else{
1109                                         Deezer.getTrack(t.id, settings.maxBitrate, settings.fallbackBitrate, function (trackInfo, err) {
1110                                                 if (err) {
1111                                                         if(t.fallback){
1112                                                                 logger.warn("Failed to download track, falling on alternative");
1113                                                                 t.id = t.fallback
1114                                                                 t.fallback = 0
1115                                                                 downloadTrack(t, settings, null, callback);
1116                                                         }else if(!t.searched){
1117                                                                 logger.warn("Failed to download track, searching for alternative");
1118                                                                 Deezer.track2ID(t.artist, t.name, null, data=>{
1119                                                                         if (t.id != 0){
1120                                                                                 t.searched = true;
1121                                                                                 t.id = data.id;
1122                                                                                 t.artist = data.artist;
1123                                                                                 t.name = data.name;
1124                                                                                 downloadTrack(t, settings, null, callback);
1125                                                                         }else{
1126                                                                                 logger.error(`Failed to download ${t.artist} - ${t.name}: Searched alternative; Not found`);
1127                                                                                 callback(new Error("Searched alternative; Not found"));
1128                                                                         }
1129                                                                 });
1130                                                         }else{
1131                                                                 logger.error(`Failed to download ${t.artist} - ${t.name}: ${err}`);
1132                                                                 callback(err);
1133                                                         }
1134                                                         return;
1135                                                 }
1136                                                 resolve(trackInfo);
1137                                         });
1138                                 }
1139                         }else{
1140                                 resolve(settings.trackInfo);
1141                         }
1142                 })
1143                 temp.then(data=>{
1144                 let track = data;
1145                 track.trackSocket = socket;
1146                 temp = new Promise((resolve, reject)=>{
1147                         if (parseInt(t.id)>0 && !altmetadata){
1148                                 if (!settings.albumInfo){
1149                                         logger.info("Getting album data");
1150                                         Deezer.getAlbum(track["ALB_ID"], function(res, err){
1151                                                 if(err){
1152                                                         logger.warn("Album not found, trying to reach deeper");
1153                                                         Deezer.getAAlbum(track["ALB_ID"], function(res, err){
1154                                                                 if(err){
1155                                                                         if(t.fallback){
1156                                                                                 logger.warn("Failed to download track, falling on alternative");
1157                                                                                 t.id = t.fallback
1158                                                                                 t.fallback = 0
1159                                                                                 settings.trackInfo = null;
1160                                                                                 downloadTrack(t, settings, null, callback);
1161                                                                         }else if(!t.searched){
1162                                                                                 logger.warn("Failed to download track, searching for alternative");
1163                                                                                 Deezer.track2ID(t.artist, t.name, null, data=>{
1164                                                                                         if (t.id != 0){
1165                                                                                                 t.searched = true;
1166                                                                                                 t.id = data.id;
1167                                                                                                 t.artist = data.artist;
1168                                                                                                 t.name = data.name;
1169                                                                                                 downloadTrack(t, settings, null, callback);
1170                                                                                         }else{
1171                                                                                                 logger.error(`Failed to download ${t.artist} - ${t.name}: Searched alternative album; Not found`);
1172                                                                                                 callback(new Error("Searched alternative album; Not found"));
1173                                                                                         }
1174                                                                                 });
1175                                                                         }else{
1176                                                                                 logger.error(`Failed to download ${t.artist} - ${t.name}: ${err}`);
1177                                                                                 callback(err);
1178                                                                         }
1179                                                                         return;
1180                                                                 }
1181                                                                 resolve(res);
1182                                                         })
1183                                                         return;
1184                                                 }
1185                                                 resolve(res);
1186                                         })
1187                                 }else{
1188                                         resolve(settings.albumInfo)
1189                                 }
1190                         }else{
1191                                 resolve({artist:{}})
1192                         }
1193                 });
1194                 temp.then(albumres=>{
1195                 let ajson = albumres;
1196                 if (ajson.totalDiskNumber){
1197                         temp = new Promise((resolve, reject) =>{
1198                                 resolve(ajson.totalDiskNumber)
1199                         })
1200                 }else{
1201                         if (((settings.tags.discTotal || settings.createCDFolder) && parseInt(t.id)>0) && !altmetadata){
1202                                 logger.info("Getting total disc number");
1203                                 temp = new Promise((resolve, reject) =>{
1204                                         Deezer.getATrack(ajson.tracks.data[ajson.tracks.data.length-1].id, function(tres){
1205                                                 resolve(tres.disk_number);
1206                                         });
1207                                 })
1208                         }else{
1209                                 temp = new Promise((resolve, reject) =>{
1210                                         resolve(null)
1211                                 })
1212                         }
1213                 }
1214                 temp.then(discTotal=>{
1215                 let totalDiskNumber = discTotal;
1216                 if ((settings.tags.bpm && parseInt(t.id)>0) && !altmetadata){
1217                         logger.info("Getting BPM");
1218                         temp = new Promise((resolve, reject) =>{
1219                                 Deezer.getATrack(t.id, function(tres, err){
1220                                         if (err) resolve(0);
1221                                         resolve(tres.bpm);
1222                                 });
1223                         })
1224                 }else{
1225                         temp = new Promise((resolve, reject) =>{
1226                                 resolve(0);
1227                         })
1228                 }
1229                 temp.then(bpm=>{
1230                 track.BPM = bpm;
1231                 let metadata = parseMetadata(track, ajson, totalDiskNumber, settings, parseInt(t.index), altmetadata);
1232                 if (settings.saveFullArtists && settings.multitagSeparator != null){
1233                         let filename = fixName(`${metadata.artists} - ${metadata.title}`);
1234                 }else{
1235                         let filename = fixName(`${metadata.artist} - ${metadata.title}`);
1236                 }
1237                 if (settings.filename) {
1238                         filename = fixName(settingsRegex(metadata, settings.filename, settings.playlist, settings.saveFullArtists && settings.multitagSeparator != null, settings.paddingSize));
1239                 }
1240                 let filepath = mainFolder;
1241                 let artistPath;
1242                 if (settings.createArtistFolder || settings.createAlbumFolder) {
1243                         if(settings.plName){
1244                                 filepath += antiDot(fixName(settings.plName)) + path.sep;
1245                         }
1246                         if (settings.createArtistFolder) {
1247                                 if(settings.artName){
1248                                         filepath += antiDot(fixName(settings.artName)) + path.sep;
1249                                 }else{
1250                                         filepath += antiDot(fixName(metadata.albumArtist)) + path.sep;
1251                                 }
1252                                 artistPath = filepath;
1253                         }
1254
1255                         if (settings.createAlbumFolder) {
1256                                 if(settings.artName){
1257                                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,settings.artName,settings.albName,metadata.year,metadata.rtype,metadata.albumExplicit,metadata.publisher))) + path.sep;
1258                                 }else{
1259                                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,metadata.albumArtist,metadata.album,metadata.year,metadata.rtype,metadata.albumExplicit,metadata.publisher))) + path.sep;
1260                                 }
1261                         }
1262                 } else if (settings.plName) {
1263                         filepath += antiDot(fixName(settings.plName)) + path.sep;
1264                 } else if (settings.artName) {
1265                         filepath += antiDot(fixName(settingsRegexAlbum(settings.foldername,settings.artName,settings.albName,metadata.year,metadata.rtype,metadata.albumExplicit,metadata.publisher))) + path.sep;
1266                 }
1267                 let coverpath = filepath;
1268                 if (metadata.discTotal > 1 && (settings.artName || settings.createAlbumFolder) && settings.createCDFolder){
1269                         filepath += `CD${metadata.discNumber +  path.sep}`
1270                 }
1271                 let writePath;
1272                 if(track.format == 9){
1273                         writePath = filepath + filename + '.flac';
1274                 }else{
1275                         writePath = filepath + filename + '.mp3';
1276                 }
1277                 if(track["LYRICS_SYNC_JSON"] && settings.syncedlyrics){
1278                         let lyricsbuffer = "";
1279                         for(let i=0;i<track["LYRICS_SYNC_JSON"].length;i++){
1280                                 if(track["LYRICS_SYNC_JSON"][i].lrc_timestamp){
1281                                         lyricsbuffer += track["LYRICS_SYNC_JSON"][i].lrc_timestamp+track["LYRICS_SYNC_JSON"][i].line+"\r\n";
1282                                 }else if(i+1 < track["LYRICS_SYNC_JSON"].length){
1283                                         lyricsbuffer += track["LYRICS_SYNC_JSON"][i+1].lrc_timestamp+track["LYRICS_SYNC_JSON"][i].line+"\r\n";
1284                                 }
1285                         }
1286                         fs.outputFile(writePath.substring(0,writePath.lastIndexOf('.'))+".lrc",lyricsbuffer,function(){});
1287                 }
1288                 let playlistData = [0,""]
1289                 if (settings.createM3UFile && (settings.plName || settings.albName)) {
1290                         if (t.index){
1291                                 playlistData = [parseInt(t.index), writePath];
1292                         }else{
1293                                 playlistData = [metadata.trackNumber-1, writePath];
1294                         }
1295                 }
1296                 if (fs.existsSync(writePath)) {
1297                         logger.info("Already downloaded: " + metadata.artist + ' - ' + metadata.title);
1298                         callback(null, {playlistData: playlistData, searched: t.searched});
1299                         return;
1300                 }else{
1301                         logger.info('Downloading file to ' + writePath);
1302                 }
1303                 //Get image
1304                 temp = new Promise((resolve, reject)=>{
1305                         if (metadata.image) {
1306                                 let imgPath;
1307                                 //If its not from an album but a playlist.
1308                                 if(!(settings.albName || settings.createAlbumFolder)){
1309                                         imgPath = coverArtFolder + (metadata.barcode ? fixName(metadata.barcode) : fixName(`${metadata.albumArtist} - ${metadata.album}`))+(settings.PNGcovers ? ".png" : ".jpg");
1310                                 }else{
1311                                         if (settings.saveArtwork)
1312                                                 imgPath = coverpath + fixName(settingsRegexCover(settings.coverImageTemplate,settings.artName,settings.albName))+(settings.PNGcovers ? ".png" : ".jpg");
1313                                         else
1314                                                 imgPath = coverArtFolder + fixName(metadata.barcode ? fixName(metadata.barcode) : fixName(`${metadata.albumArtist} - ${metadata.album}`))+(settings.PNGcovers ? ".png" : ".jpg");
1315                                 }
1316                                 if(fs.existsSync(imgPath)){
1317                                         metadata.imagePath = (imgPath).replace(/\\/g, "/");
1318                                         logger.info("Starting the download process CODE:1");
1319                                         resolve();
1320                                 }else{
1321                                         request.get(metadata.image, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
1322                                                 if(error){
1323                                                         logger.error(error.stack);
1324                                                         metadata.image = undefined;
1325                                                         metadata.imagePath = undefined;
1326                                                         return;
1327                                                 }
1328                                                 fs.outputFile(imgPath,body,'binary',function(err){
1329                                                         if(err){
1330                                                                 logger.error(err.stack);
1331                                                         metadata.image = undefined;
1332                                                         metadata.imagePath = undefined;
1333                                                                 return;
1334                                                         }
1335                                                         metadata.imagePath = (imgPath).replace(/\\/g, "/");
1336                                                         logger.info("Starting the download process CODE:2");
1337                                                         resolve();
1338                                                 })
1339                                         });
1340                                 }
1341                         }else{
1342                                 metadata.image = undefined;
1343                                 logger.info("Starting the download process CODE:3");
1344                                 resolve();
1345                         }
1346                 })
1347                 temp.then(()=>{
1348                 temp = new Promise((resolve, reject)=>{
1349                         if (metadata.artistImage && settings.saveArtworkArtist) {
1350                                 let imgPath;
1351                                 if(settings.createArtistFolder){
1352                                         imgPath = artistPath + antiDot(fixName(settingsRegexArtistCover(settings.artistImageTemplate,metadata.albumArtist)))+(settings.PNGcovers ? ".png" : ".jpg");
1353                                         if(fs.existsSync(imgPath)){
1354                                                 resolve();
1355                                         }else{
1356                                                 request.get(metadata.artistImage, {strictSSL: false,encoding: 'binary'}, function(error,response,body){
1357                                                         if(error){
1358                                                                 logger.error(error.stack);
1359                                                                 return;
1360                                                         }
1361                                                         if (body.indexOf("unauthorized")>-1) return resolve();
1362                                                         fs.outputFile(imgPath,body,'binary',function(err){
1363                                                                 if(err){
1364                                                                         logger.error(err.stack);
1365                                                                         return;
1366                                                                 }
1367                                                                 logger.info("Saved Artist Image");
1368                                                                 resolve();
1369                                                         })
1370                                                 });
1371                                         }
1372                                 }else{
1373                                         resolve();
1374                                 }
1375                         }else{
1376                                 resolve();
1377                         }
1378                 })
1379                 temp.then(()=>{
1380                 let tempPath
1381                 if(parseInt(t.id)>0)
1382                         tempPath = writePath+".temp"
1383                 else
1384                         tempPath = writePath;
1385                 logger.info("Downloading and decrypting");
1386                 Deezer.decryptTrack(tempPath, track, t.queueId, function (err) {
1387                         if (err && err.message == "aborted") {
1388                                 logger.info("Track got aborted");
1389                                 t.trackSocket = null
1390                                 callback(null, {playlistData: playlistData, searched: t.searched});
1391                                 return;
1392                         }
1393                         if (err) {
1394                                 if (t.fallback){
1395                                         logger.warn("Failed to download: " + metadata.artist + " - " + metadata.title+", falling on alternative");
1396                                         t.id = t.fallback
1397                                         t.fallback = 0
1398                                         settings.trackInfo = null;
1399                                         downloadTrack(t, settings, JSON.parse(JSON.stringify(metadata)), callback);
1400                                 }else if(!t.searched){
1401                                         logger.warn("Failed to download track, searching for alternative");
1402                                         Deezer.track2ID(t.artist, t.name, null, data=>{
1403                                                 t.searched = true;
1404                                                 t.id = data.id;
1405                                                 t.artist = data.artist;
1406                                                 t.name = data.name;
1407                                                 downloadTrack(t, settings, JSON.parse(JSON.stringify(metadata)), callback);
1408                                         });
1409                                 }else{
1410                                         logger.error(`Failed to download ${t.artist} - ${t.name}: ${err}`);
1411                                         callback(err)
1412                                 }
1413                                 return;
1414                         }
1415                         logger.info("Downloaded: " + metadata.artist + " - " + metadata.title);
1416                         if (parseInt(t.id)>0){
1417                                 if(track.format == 9){
1418                                         let flacComments = [];
1419                                         if (settings.tags.title)
1420                                                 flacComments.push('TITLE=' + metadata.title);
1421                                         if (settings.tags.album)
1422                                                 flacComments.push('ALBUM=' + metadata.album);
1423                                         if (settings.tags.albumArtist)
1424                                                 flacComments.push('ALBUMARTIST=' + metadata.albumArtist);
1425                                         if (settings.tags.trackNumber)
1426                                                 flacComments.push('TRACKNUMBER=' + metadata.trackNumber);
1427                                         if (settings.tags.discNumber)
1428                                                 flacComments.push('DISCNUMBER=' + metadata.discNumber);
1429                                         if (settings.tags.trackTotal)
1430                                                 flacComments.push('TRACKTOTAL=' + metadata.trackTotal);
1431                                         if (settings.tags.explicit)
1432                                                 flacComments.push('ITUNESADVISORY=' + metadata.explicit);
1433                                         if (settings.tags.isrc)
1434                                                 flacComments.push('ISRC=' + metadata.ISRC);
1435                                         if (settings.tags.artist && metadata.artists)
1436                                                 if (Array.isArray(metadata.artists)){
1437                                                         metadata.artists.forEach(x=>{
1438                                                                 flacComments.push('ARTIST=' + x);
1439                                                         });
1440                                                 }else{
1441                                                         flacComments.push('ARTIST=' + metadata.artists);
1442                                                 }
1443                                         if (settings.tags.discTotal)
1444                                                 flacComments.push('DISCTOTAL='+splitNumber(metadata.discTotal,true));
1445                                         if (settings.tags.length)
1446                                                 flacComments.push('LENGTH=' + metadata.length);
1447                                         if (settings.tags.barcode && metadata.barcode)
1448                                                 flacComments.push('BARCODE=' + metadata.barcode);
1449                                         if (metadata.unsynchronisedLyrics && settings.tags.unsynchronisedLyrics)
1450                                                 flacComments.push('LYRICS='+metadata.unsynchronisedLyrics.lyrics);
1451                                         if (metadata.genre && settings.tags.genre)
1452                                                 if (Array.isArray(metadata.genre)){
1453                                                         metadata.genre.forEach(x=>{
1454                                                                 flacComments.push('GENRE=' + x);
1455                                                         });
1456                                                 }else{
1457                                                         flacComments.push('GENRE=' + metadata.genre);
1458                                                 }
1459                                         if (metadata.copyright && settings.tags.copyright)
1460                                                 flacComments.push('COPYRIGHT=' + metadata.copyright);
1461                                         if (0 < parseInt(metadata.year)){
1462                                                 if (settings.tags.year)
1463                                                         flacComments.push('YEAR=' + metadata.year);
1464                                                 if (settings.tags.date)
1465                                                 flacComments.push('DATE=' + metadata.date);
1466                                         }
1467                                         if (0 < parseInt(metadata.bpm) && settings.tags.bpm)
1468                                                 flacComments.push('BPM=' + metadata.bpm);
1469                                         if(metadata.publisher && settings.tags.publisher)
1470                                                 flacComments.push('PUBLISHER=' + metadata.publisher);
1471                                         if(metadata.composer && settings.tags.composer)
1472                                                 if (Array.isArray(metadata.composer)){
1473                                                         metadata.composer.forEach(x=>{
1474                                                                 flacComments.push('COMPOSER=' + x);
1475                                                         });
1476                                                 }else{
1477                                                         flacComments.push('COMPOSER=' + metadata.composer);
1478                                                 }
1479                                         if(metadata.musicpublisher && settings.tags.musicpublisher)
1480                                                 if (Array.isArray(metadata.musicpublisher)){
1481                                                         metadata.musicpublisher.forEach(x=>{
1482                                                                 flacComments.push('ORGANIZATION=' + x);
1483                                                         });
1484                                                 }else{
1485                                                         flacComments.push('ORGANIZATION=' + metadata.musicpublisher);
1486                                                 }
1487                                         if(metadata.mixer && settings.tags.mixer)
1488                                                 if (Array.isArray(metadata.mixer)){
1489                                                         metadata.mixer.forEach(x=>{
1490                                                                 flacComments.push('MIXER=' + x);
1491                                                         });
1492                                                 }else{
1493                                                         flacComments.push('MIXER=' + metadata.mixer);
1494                                                 }
1495                                         if(metadata.author && settings.tags.author)
1496                                                 if (Array.isArray(metadata.author)){
1497                                                         metadata.author.forEach(x=>{
1498                                                                 flacComments.push('AUTHOR=' + x);
1499                                                         });
1500                                                 }else{
1501                                                         flacComments.push('AUTHOR=' + metadata.author);
1502                                                 }
1503                                         if(metadata.writer && settings.tags.writer)
1504                                                 if (Array.isArray(metadata.writer)){
1505                                                         metadata.writer.forEach(x=>{
1506                                                                 flacComments.push('WRITER=' + x);
1507                                                         });
1508                                                 }else{
1509                                                         flacComments.push('WRITER=' + metadata.writer);
1510                                                 }
1511                                         if(metadata.engineer && settings.tags.engineer)
1512                                                 if (Array.isArray(metadata.engineer)){
1513                                                         metadata.engineer.forEach(x=>{
1514                                                                 flacComments.push('ENGINEER=' + x);
1515                                                         });
1516                                                 }else{
1517                                                         flacComments.push('ENGINEER=' + metadata.engineer);
1518                                                 }
1519                                         if(metadata.producer && settings.tags.producer)
1520                                                 if (Array.isArray(metadata.producer)){
1521                                                         metadata.producer.forEach(x=>{
1522                                                                 flacComments.push('PRODUCER=' + x);
1523                                                         });
1524                                                 }else{
1525                                                         flacComments.push('PRODUCER=' + metadata.producer);
1526                                                 }
1527                                         if(metadata.replayGain && settings.tags.replayGain)
1528                                                 flacComments.push('REPLAYGAIN_TRACK_GAIN=' + metadata.replayGain);
1529
1530                                         const reader = fs.createReadStream(tempPath);
1531                                         const writer = fs.createWriteStream(writePath);
1532                                         let processor = new mflac.Processor({parseMetaDataBlocks: true});
1533                                         let vendor = 'reference libFLAC 1.2.1 20070917';
1534                                         let cover = null;
1535                                         if(metadata.imagePath && settings.tags.cover){
1536                                                 cover = fs.readFileSync(metadata.imagePath);
1537                                         }
1538                                         let mdbVorbisPicture;
1539                                         let mdbVorbisComment;
1540                                         processor.on('preprocess', (mdb) => {
1541                                                 // Remove existing VORBIS_COMMENT and PICTURE blocks, if any.
1542                                                 if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type) {
1543                                                         mdb.remove();
1544                                                 } else if (mflac.Processor.MDB_TYPE_PICTURE === mdb.type) {
1545                                                         mdb.remove();
1546                                                 }
1547                                                 if (mdb.isLast) {
1548                                                         if(cover){
1549                                                                 mdbVorbisPicture = mflac.data.MetaDataBlockPicture.create(true, 3, `image/${(settings.PNGcovers ? "png" : "jpeg")}`, '', settings.artworkSize, settings.artworkSize, 24, 0, cover);
1550                                                         }
1551                                                         mdbVorbisComment = mflac.data.MetaDataBlockVorbisComment.create(!cover, vendor, flacComments);
1552                                                         mdb.isLast = false;
1553                                                 }
1554                                         });
1555                                         processor.on('postprocess', (mdb) => {
1556                                                 if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type && null !== mdb.vendor) {
1557                                                         vendor = mdb.vendor;
1558                                                 }
1559                                                 if (mdbVorbisPicture && mdbVorbisComment) {
1560                                                                 processor.push(mdbVorbisComment.publish());
1561                                                                 processor.push(mdbVorbisPicture.publish());
1562                                                         }else if(mdbVorbisComment){
1563                                                                 processor.push(mdbVorbisComment.publish());
1564                                                 }
1565                                         });
1566                                         reader.on('end', () => {
1567                                                 fs.remove(tempPath);
1568                                         });
1569                                         reader.pipe(processor).pipe(writer);
1570                                 }else{
1571                                         const songBuffer = fs.readFileSync(tempPath);
1572                                         const writer = new ID3Writer(songBuffer);
1573                                         if (settings.tags.title)
1574                                                 writer.setFrame('TIT2', metadata.title);
1575                                         if (settings.tags.artist)
1576                                                 writer.setFrame('TPE1', [metadata.artists]);
1577                                         if (settings.tags.album)
1578                                                 writer.setFrame('TALB', metadata.album)
1579                                         if (settings.tags.albumArtist && metadata.albumArtist)
1580                                                 writer.setFrame('TPE2', metadata.albumArtist)
1581                                         if (settings.tags.trackNumber)
1582                                                 writer.setFrame('TRCK', (settings.tags.trackTotal ? metadata.trackNumber+"/"+metadata.trackTotal : metadata.trackNumber))
1583                                         if (settings.tags.discNumber)
1584                                                 writer.setFrame('TPOS', (settings.tags.discTotal ? metadata.discNumber+"/"+metadata.discTotal : metadata.discNumber))
1585                                         if (settings.tags.isrc)
1586                                                 writer.setFrame('TSRC', metadata.ISRC);
1587
1588                                         if (settings.tags.length)
1589                                                 writer.setFrame('TLEN', metadata.length);
1590                                         if (settings.tags.barcode && metadata.barcode)
1591                                                 writer.setFrame('TXXX', {
1592                                                         description: 'BARCODE',
1593                                                         value: metadata.barcode
1594                                                 });
1595                                         if(metadata.imagePath && settings.tags.cover){
1596                                                 const coverBuffer = fs.readFileSync(metadata.imagePath);
1597                                                 writer.setFrame('APIC', {
1598                                                         type: 3,
1599                                                         data: coverBuffer,
1600                                                         description: ''
1601                                                 });
1602                                         }
1603                                         if(metadata.unsynchronisedLyrics && settings.tags.unsynchronisedLyrics)
1604                                                 writer.setFrame('USLT', metadata.unsynchronisedLyrics);
1605                                         if(metadata.publisher && settings.tags.publisher)
1606                                                 writer.setFrame('TPUB', metadata.publisher);
1607                                         if(metadata.genre && settings.tags.genre)
1608                                                 writer.setFrame('TCON', [metadata.genre]);
1609                                         if(metadata.copyright && settings.tags.copyright)
1610                                                 writer.setFrame('TCOP', metadata.copyright);
1611                                         if (0 < parseInt(metadata.year)) {
1612                                                 if (settings.tags.date)
1613                                                         writer.setFrame('TDAT', metadata.date);
1614                                                 if (settings.tags.year)
1615                                                         writer.setFrame('TYER', metadata.year);
1616                                         }
1617                                         if (0 < parseInt(metadata.bpm) && settings.tags.bpm)
1618                                                 writer.setFrame('TBPM', metadata.bpm);
1619                                         if(metadata.composer && settings.tags.composer)
1620                                                 writer.setFrame('TCOM', [metadata.composer]);
1621                                         if(metadata.replayGain && settings.tags.replayGain)
1622                                                 writer.setFrame('TXXX', {
1623                                                         description: 'REPLAYGAIN_TRACK_GAIN',
1624                                                         value: metadata.replayGain
1625                                                 });
1626                                         writer.addTag();
1627                                         const taggedSongBuffer = Buffer.from(writer.arrayBuffer);
1628                                         fs.writeFileSync(writePath, taggedSongBuffer);
1629                                         fs.remove(tempPath);
1630                                 }
1631                         }
1632                         callback(null, {playlistData: playlistData, searched: t.searched});
1633                 })
1634         })
1635         })
1636         })
1637         })
1638         })
1639         })
1640         }
1641
1642         function checkIfAlreadyInQueue(id) {
1643                 let exists = false;
1644                 Object.keys(socket.downloadQueue).forEach(x=>{
1645                         if (socket.downloadQueue[x].id == id) {
1646                                 exists = socket.downloadQueue[i].queueId;
1647                         }
1648                 });
1649                 if (socket.currentItem && (socket.currentItem.id == id)) {
1650                         exists = socket.currentItem.queueId;
1651                 }
1652                 return exists;
1653         }
1654 });
1655
1656 // Helper functions
1657
1658 /**
1659  * Updates individual parameters in the settings file
1660  * @param config
1661  * @param value
1662  */
1663 function updateSettingsFile(config, value) {
1664         configFile.userDefined[config] = value;
1665
1666         fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
1667                 if (err) return;
1668                 logger.info("Settings updated");
1669
1670                 // FIXME: Endless Loop, due to call from initFolders()...crashes soon after startup
1671                 // initFolders();
1672         });
1673 }
1674
1675 function fixName (txt) {
1676   const regEx = /[\0\/\\:*?"<>|]/g;
1677   return txt.replace(regEx, '_');
1678 }
1679
1680 function antiDot(str){
1681         while(str[str.length-1] == "." || str[str.length-1] == " " || str[str.length-1] == "\n"){
1682                 str = str.substring(0,str.length-1);
1683         }
1684         if(str.length < 1){
1685                 str = "dot";
1686         }
1687         return fixName(str);
1688 }
1689
1690 /**
1691  * Initialize the temp folder for covers and main folder for downloads
1692  */
1693 function initFolders() {
1694         // Check if main folder exists
1695         if (!fs.existsSync(mainFolder)) {
1696                 mainFolder = defaultDownloadDir;
1697                 updateSettingsFile('downloadLocation', defaultDownloadDir);
1698         }
1699         //fs.removeSync(coverArtFolder);
1700         fs.ensureDirSync(coverArtFolder);
1701 }
1702
1703 /**
1704  * Creates the name of the tracks replacing wildcards to correct metadata
1705  * @param metadata
1706  * @param filename
1707  * @param playlist
1708  * @returns {XML|string|*}
1709  */
1710 function settingsRegex(metadata, filename, playlist, saveFullArtists, paddingSize) {
1711         filename = filename.replace(/%title%/g, metadata.title);
1712         filename = filename.replace(/%album%/g, metadata.album);
1713         filename = filename.replace(/%artist%/g, (saveFullArtists ? metadata.artists : metadata.artist));
1714         filename = filename.replace(/%year%/g, metadata.year);
1715         filename = filename.replace(/%label%/g, metadata.publisher);
1716         if(typeof metadata.trackNumber != 'undefined'){
1717                 if(configFile.userDefined.padtrck){
1718                          filename = filename.replace(/%number%/g, pad(metadata.trackNumber, (parseInt(paddingSize)>0 ? parseInt(paddingSize) : metadata.trackTotal)));
1719                 }else{
1720                         filename = filename.replace(/%number%/g, metadata.trackNumber);
1721                 }
1722         } else {
1723                 filename = filename.replace(/%number%/g, '');
1724         }
1725         filename = filename.replace(/%explicit%/g, (metadata.explicit==="1" ? (filename.indexOf(/[^%]explicit/g)>-1 ? "" : "(Explicit Version)") : ""));
1726         return filename.trim();
1727 }
1728
1729 /**
1730  * Creates the name of the albums folder replacing wildcards to correct metadata
1731  * @param metadata
1732  * @param foldername
1733  * @returns {XML|string|*}
1734  */
1735 function settingsRegexAlbum(foldername, artist, album, year, rtype, explicit, publisher) {
1736         foldername = foldername.replace(/%album%/g, album);
1737         foldername = foldername.replace(/%artist%/g, artist);
1738         foldername = foldername.replace(/%year%/g, year);
1739         if (rtype){
1740                 foldername = foldername.replace(/%type%/g, rtype[0].toUpperCase() + rtype.substring(1));
1741         }else{
1742                 foldername = foldername.replace(/%type%/g, "");
1743         }
1744         foldername = foldername.replace(/%label%/g, publisher);
1745         foldername = foldername.replace(/%explicit%/g, (explicit ? (foldername.indexOf(/[^%]explicit/g)>-1 ? "" : "(Explicit)") : ""));
1746         return foldername.trim();
1747 }
1748
1749 function settingsRegexCover(foldername, artist, name) {
1750         foldername = foldername.replace(/%name%/g, name);
1751         foldername = foldername.replace(/%artist%/g, artist);
1752         return foldername;
1753 }
1754
1755 function settingsRegexArtistCover(foldername, artist) {
1756         foldername = foldername.replace(/%artist%/g, artist);
1757         return foldername;
1758 }
1759
1760 /**
1761  * I really don't understand what this does ... but it does something
1762  * @param str
1763  * @param max
1764  * @returns {String|string|*}
1765  */
1766 function pad(str, max) {
1767         str = str.toString();
1768         max = max.toString();
1769         return str.length < max.length || str.length == 1 ? pad("0" + str, max) : str;
1770 }
1771
1772 /**
1773  * Splits the %number%
1774  * @param string str
1775  * @return string
1776  */
1777 function splitNumber(str,total){
1778         str = str.toString();
1779         let i = str.indexOf("/");
1780         if(total && i > 0){
1781                 return str.slice(i+1, str.length);
1782         }else if(i > 0){
1783                 return str.slice(0, i);
1784         }else{
1785                 return str;
1786         }
1787         return i > 0 ? str.slice(0, i) : str;
1788 }
1789
1790 function slimDownTrackInfo(trackOld){
1791         let track = {};
1792         track['SNG_ID'] = trackOld["SNG_ID"]
1793         track['ARTISTS'] = trackOld["ARTISTS"]
1794         track["ALB_ID"] = trackOld["ALB_ID"]
1795         track["ALB_PICTURE"] = trackOld["ALB_PICTURE"]
1796         track["ART_PICTURE"] = trackOld["ART_PICTURE"]
1797         track["ALB_TITLE"] = trackOld["ALB_TITLE"]
1798         track["ART_NAME"] = trackOld["ART_NAME"]
1799         track["BPM"] = trackOld["BPM"]
1800         track["COPYRIGHT"] = trackOld["COPYRIGHT"]
1801         track["DISK_NUMBER"] = trackOld["DISK_NUMBER"]
1802         track["DURATION"] = trackOld["DURATION"]
1803         track["EXPLICIT_LYRICS"] = trackOld["EXPLICIT_LYRICS"]
1804         track["GAIN"] = trackOld["GAIN"]
1805         track["ISRC"] = trackOld["ISRC"]
1806         track["TYPE"] = trackOld["TYPE"]
1807         track["LYRICS_SYNC_JSON"] = trackOld["LYRICS_SYNC_JSON"]
1808         track["LYRICS_TEXT"] = trackOld["LYRICS_TEXT"]
1809         track["PHYSICAL_RELEASE_DATE"] = trackOld["PHYSICAL_RELEASE_DATE"]
1810         track["SNG_CONTRIBUTORS"] = trackOld["SNG_CONTRIBUTORS"]
1811         track["SNG_TITLE"] = trackOld["SNG_TITLE"]
1812         track["TRACK_NUMBER"] = trackOld["TRACK_NUMBER"]
1813         track["VERSION"] = trackOld["VERSION"]
1814         track["FILESIZE_FLAC"] = trackOld["FILESIZE_FLAC"]
1815         track["FILESIZE_MP3_320"] = trackOld["FILESIZE_MP3_320"]
1816         track["FILESIZE_MP3_256"] = trackOld["FILESIZE_MP3_256"]
1817         track["FILESIZE_MP3_128"] = trackOld["FILESIZE_MP3_128"]
1818         track.FILESIZE = trackOld.FILESIZE
1819         track["FALLBACK"] = trackOld["FALLBACK"]
1820         track.downloadUrl = trackOld.downloadUrl
1821         track.format = trackOld.format
1822         return track
1823 }
1824
1825 function slimDownAlbumInfo(ajsonOld){
1826         let ajson = {};
1827         ajson.artist = {}
1828         ajson.artist.name = ajsonOld.artist.name
1829         ajson.artist.picture_small = ajsonOld.artist.picture_small
1830         ajson.nb_tracks = ajsonOld.nb_tracks
1831         ajson.upc = ajsonOld.upc
1832         ajson.record_type = ajsonOld.record_type
1833         ajson.label = ajsonOld.label
1834         ajson.genres = ajsonOld.genres
1835         ajson.explicit_lyrics = ajsonOld.explicit_lyrics
1836         ajson.release_date = ajsonOld.release_date
1837         ajson.tracks = {
1838                 data: ajsonOld.tracks.data.map(x=>{
1839                         return {id: x.id};
1840                 })
1841         }
1842         ajson.tracks.total = ajsonOld.tracks.total
1843         return ajson
1844 }
1845
1846 function swichReleaseType(id){
1847         switch (id) {
1848                 case "0":
1849                         return "Album";
1850                 case "1":
1851                         return "Single";
1852                 case "3":
1853                         return "EP";
1854                 default:
1855                         return id;
1856         }
1857 }
1858
1859 function uniqueArray(origin, destination, removeDupes=true){
1860         Array.from(new Set(origin)).forEach(function(x){
1861                 if(destination.indexOf(x) == -1)
1862                         destination.push(x);
1863         });
1864         if (removeDupes){
1865                 destination.forEach((name,index)=>{
1866                         destination.forEach((name2,index2)=>{
1867                                 if(!(index===index2) && (name.indexOf(name2)!== -1)){
1868                                         destination.splice(index, 1);
1869                                 }
1870                         })
1871                 })
1872         }
1873 }
1874
1875 function parseMetadata(track, ajson, totalDiskNumber, settings, position, altmetadata){
1876         let metadata;
1877         if (track["VERSION"]) track["SNG_TITLE"] += " " + track["VERSION"];
1878         if (settings.removeAlbumVersion){
1879                 if(track["SNG_TITLE"].indexOf("Album Version")>-1){
1880                         track["SNG_TITLE"] = track["SNG_TITLE"].replace(/\(Album Version\)/g,"")
1881                         track["SNG_TITLE"].trim()
1882                 }
1883         }
1884         if(altmetadata){
1885                 metadata = altmetadata;
1886                 if(track["LYRICS_TEXT"] && !metadata.unsynchronisedLyrics){
1887                         metadata.unsynchronisedLyrics = {
1888                                 description: "",
1889                                 lyrics: track["LYRICS_TEXT"]
1890                         };
1891                 }
1892         }else{
1893                 let separator = settings.multitagSeparator;
1894                 if (separator == "null") separator = String.fromCharCode(parseInt("\u0000",16));
1895                 metadata = {
1896                         title: track["SNG_TITLE"],
1897                         artist: track["ART_NAME"],
1898                         album: track["ALB_TITLE"],
1899                         trackNumber: track["TRACK_NUMBER"],
1900                         discNumber: track["DISK_NUMBER"],
1901                         explicit: track["EXPLICIT_LYRICS"],
1902                         ISRC: track["ISRC"],
1903                         albumArtist: ajson.artist.name,
1904                         trackTotal: ajson.nb_tracks,
1905                         rtype: ajson.record_type,
1906                         barcode: ajson.upc,
1907                         length: track["DURATION"]
1908                 };
1909                 if(track["COPYRIGHT"]){
1910                         metadata.copyright = track["COPYRIGHT"];
1911                 }
1912                 if (!metadata.rtype){
1913                         metadata.rtype = swichReleaseType(track["TYPE"])
1914                 }
1915                 if (ajson.explicit_lyrics){
1916                         metadata.albumExplicit = ajson.explicit_lyrics;
1917                 }
1918                 if(track["SNG_CONTRIBUTORS"]){
1919                         if(track["SNG_CONTRIBUTORS"].composer){
1920                                 metadata.composer = [];
1921                                 uniqueArray(track["SNG_CONTRIBUTORS"].composer, metadata.composer, settings.removeDupedTags)
1922                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.composer = metadata.composer.join(separator);
1923                         }
1924                         if(track["SNG_CONTRIBUTORS"].musicpublisher){
1925                                 metadata.musicpublisher = [];
1926                                 uniqueArray(track["SNG_CONTRIBUTORS"].musicpublisher, metadata.musicpublisher, settings.removeDupedTags)
1927                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.musicpublisher = metadata.musicpublisher.join(separator);
1928                         }
1929                         if(track["SNG_CONTRIBUTORS"].producer){
1930                                 metadata.producer = [];
1931                                 uniqueArray(track["SNG_CONTRIBUTORS"].producer, metadata.producer, settings.removeDupedTags)
1932                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.producer = metadata.producer.join(separator);
1933                         }
1934                         if(track["SNG_CONTRIBUTORS"].engineer){
1935                                 metadata.engineer = [];
1936                                 uniqueArray(track["SNG_CONTRIBUTORS"].engineer, metadata.engineer, settings.removeDupedTags)
1937                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.engineer = metadata.engineer.join(separator);
1938                         }
1939                         if(track["SNG_CONTRIBUTORS"].writer){
1940                                 metadata.writer = [];
1941                                 uniqueArray(track["SNG_CONTRIBUTORS"].writer, metadata.writer, settings.removeDupedTags)
1942                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.writer = metadata.writer.join(separator);
1943                         }
1944                         if(track["SNG_CONTRIBUTORS"].author){
1945                                 metadata.author = [];
1946                                 uniqueArray(track["SNG_CONTRIBUTORS"].author, metadata.author, settings.removeDupedTags)
1947                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.author = metadata.author.join(separator);
1948                         }
1949                         if(track["SNG_CONTRIBUTORS"].mixer){
1950                                 metadata.mixer = [];
1951                                 uniqueArray(track["SNG_CONTRIBUTORS"].mixer, metadata.mixer, settings.removeDupedTags)
1952                                 if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.mixer = metadata.mixer.join(separator);
1953                         }
1954                 }
1955                 if(track["LYRICS_TEXT"]){
1956                         metadata.unsynchronisedLyrics = {
1957                                 description: "",
1958                                 lyrics: track["LYRICS_TEXT"]
1959                         };
1960                 }
1961                 if (track["GAIN"]) {
1962                         metadata.replayGain = track["GAIN"];
1963                 }
1964                 if(ajson.label){
1965                         metadata.publisher = ajson.label;
1966                 }
1967                 if (0 < parseInt(track["BPM"])) {
1968                         metadata.bpm = track["BPM"];
1969                 }
1970                 if(track['ARTISTS']){
1971                         metadata.artists = [];
1972                         artistArray = []
1973                         track['ARTISTS'].forEach(function(artist){
1974                                 artistArray.push(artist['ART_NAME']);
1975                         });
1976                         uniqueArray(artistArray, metadata.artists, settings.removeDupedTags)
1977                         let posMainArtist = metadata.artists.indexOf(metadata.albumArtist)
1978                         if (posMainArtist !== -1 && posMainArtist !== 0 && settings.removeDupedTags){
1979                                 let element = metadata.artists[posMainArtist];
1980                 metadata.artists.splice(posMainArtist, 1);
1981                 metadata.artists.splice(0, 0, element);
1982                         }
1983                         if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.artists = metadata.artists.join(separator);
1984                 }
1985                 if(ajson.genres && ajson.genres.data[0] && ajson.genres.data[0].name){
1986                         metadata.genre = [];
1987                         genreArray = [];
1988                         ajson.genres.data.forEach(function(genre){
1989                                 genreArray.push(genre.name);
1990                         });
1991                         uniqueArray(genreArray, metadata.genre, false)
1992                         if (!(track.format == 9 && separator==String.fromCharCode(parseInt("\u0000",16)))) metadata.genre = metadata.genre.join(separator);
1993                 }
1994                 if (track["ALB_PICTURE"]) {
1995                         metadata.image = Deezer.albumPicturesHost + track["ALB_PICTURE"]+"/"+settings.artworkSize+"x"+settings.artworkSize+"-000000-80-0-0"+(settings.PNGcovers ? ".png" : ".jpg");
1996                 }
1997                 if (ajson.artist.picture_small) {
1998                         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");
1999                 }
2000                 if (ajson.release_date) {
2001                         metadata.year = ajson.release_date.slice(0, 4);
2002                         metadata.date = {
2003                                 day: ajson.release_date.slice(8,10),
2004                                 month: ajson.release_date.slice(5,7),
2005                                 year: (settings.dateFormatYear == "2" ? ajson.release_date.slice(2, 4) : ajson.release_date.slice(0, 4))
2006                         }
2007                 } else if(track["PHYSICAL_RELEASE_DATE"]){
2008                         metadata.year = track["PHYSICAL_RELEASE_DATE"].slice(0, 4);
2009                         metadata.date = {
2010                                 day: track["PHYSICAL_RELEASE_DATE"].slice(8,10),
2011                                 month: track["PHYSICAL_RELEASE_DATE"].slice(5,7),
2012                                 year: (settings.dateFormatYear == "2" ? track["PHYSICAL_RELEASE_DATE"].slice(2, 4) : track["PHYSICAL_RELEASE_DATE"].slice(0, 4))
2013                         }
2014                 }
2015                 if (metadata.date){
2016                         let date
2017                         switch (settings.dateFormat){
2018                                 case "0": date = `${metadata.date.year}-${metadata.date.month}-${metadata.date.day}`; break;
2019                                 case "1": date = `${metadata.date.day}-${metadata.date.month}-${metadata.date.year}`; break;
2020                                 case "2": date = `${metadata.date.month}-${metadata.date.day}-${metadata.date.year}`; break;
2021                                 case "3": date = `${metadata.date.year}-${metadata.date.day}-${metadata.date.month}`; break;
2022                                 case "4": date = `${metadata.date.day}${metadata.date.month}`; break;
2023                                 default: date = `${metadata.date.day}${metadata.date.month}`; break;
2024                         }
2025                         metadata.date = date;
2026                 }
2027                 if(settings.plName && !(settings.createArtistFolder || settings.createAlbumFolder) && !settings.numplaylistbyalbum){
2028                         metadata.trackNumber = (position+1).toString();
2029                         metadata.trackTotal = settings.playlist.fullSize;
2030                         metadata.discNumber = "1";
2031                         metadata.discTotal = "1";
2032                 }
2033                 if (totalDiskNumber){
2034                         metadata.discTotal = totalDiskNumber;
2035                 }
2036         }
2037         return metadata;
2038 }
2039
2040 process.on('unhandledRejection', function (err) {
2041         logger.error(err.stack);
2042 });
2043 // Show crash error in console for debugging
2044 process.on('uncaughtException', function (err) {
2045         logger.error(err.stack);
2046 });
2047
2048 // Exporting vars
2049 module.exports.mainFolder = mainFolder;
2050 module.exports.defaultSettings = defaultSettings;
2051 module.exports.defaultDownloadDir = defaultDownloadDir;