Release 4.0.2
[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('flac-metadata');
18 const io = require('socket.io').listen(server, {log: false, wsEngine: 'ws'});
19 const fs = require('fs-extra');
20 const async = require('async');
21 const request = require('requestretry').defaults({maxAttempts: 2147483647, retryDelay: 1000, timeout: 8000});
22 const os = require('os');
23 const ID3Writer = require('./lib/browser-id3-writer');
24 const Deezer = require('./deezer-api');
25 const path = require('path');
26 const crypto = require('crypto');
27 const logger = require('./logger.js');
28 const Spotify = require('spotify-web-api-node');
29 const authCredentials = require('./authCredentials.js')
30
31 // Load Config File
32 var userdata = "";
33 var homedata = "";
34 if(process.env.APPDATA){
35         homedata = os.homedir();
36         userdata = process.env.APPDATA + path.sep + "Deezloader Remix\\";
37 }else if(process.platform == "darwin"){
38         homedata = os.homedir();
39         userdata = homedata + '/Library/Application Support/Deezloader Remix/';
40 }else if(process.platform == "android"){
41         homedata = os.homedir() + "/storage/shared";
42         userdata = homedata + "/Deezloader Remix/";
43 }else{
44         homedata = os.homedir();
45         userdata = homedata + '/.config/Deezloader Remix/';
46 }
47
48 if(!fs.existsSync(userdata+"config.json")){
49         fs.outputFileSync(userdata+"config.json",fs.readFileSync(__dirname+path.sep+"default.json",'utf8'));
50 }
51
52 var spotifyApi = new Spotify(authCredentials);
53
54 // Settings update fix
55 let configFile = require(userdata+path.sep+"config.json");
56 if( typeof configFile.userDefined.numplaylistbyalbum != "boolean" ||
57         typeof configFile.userDefined.syncedlyrics != "boolean" ||
58         typeof configFile.userDefined.padtrck != "boolean" ||
59         typeof configFile.userDefined.extendedTags != "boolean"||
60         typeof configFile.userDefined.partOfSet != "boolean"||
61         typeof configFile.userDefined.chartsCountry != "string"||
62         typeof configFile.userDefined.albumNameTemplate != "string" ||
63         typeof configFile.userDefined.spotifyUser != "string"){
64                 fs.outputFileSync(userdata+"config.json",fs.readFileSync(__dirname+path.sep+"default.json",'utf8'));
65                 configFile = require(userdata+path.sep+"config.json");
66 }
67
68 // Main Constants
69 const configFileLocation = userdata+"config.json";
70 const autologinLocation = userdata+"autologin";
71 const coverArtFolder = os.tmpdir() + path.sep + 'deezloader-imgs' + path.sep;
72 const defaultDownloadDir = homedata + path.sep + "Music" + path.sep + 'Deezloader' + path.sep;
73 const defaultSettings = require('./default.json').userDefined;
74
75 // Setup the folders START
76 let mainFolder = defaultDownloadDir;
77
78 if (configFile.userDefined.downloadLocation != null) {
79         mainFolder = configFile.userDefined.downloadLocation;
80 }
81
82 initFolders();
83 // END
84
85 // Route and Create server
86 app.use('/', express.static(__dirname + '/public/'));
87 server.listen(configFile.serverPort);
88 logger.logs('Info', 'Server is running @ localhost:' + configFile.serverPort);
89
90 //Autologin encryption/decryption
91 var ekey = "62I9smDurjvfOdn2JhUdi99yeoAhxikw";
92
93 function alencrypt(input) {
94         var iv = crypto.randomBytes(16);
95         var data = new Buffer(input).toString('binary');
96         key = new Buffer(ekey, "utf8");
97         var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
98         var encrypted;
99         encrypted =  cipher.update(data, 'utf8', 'binary') +  cipher.final('binary');
100         var encoded = new Buffer(iv, 'binary').toString('hex') + new Buffer(encrypted, 'binary').toString('hex');
101
102         return encoded;
103 }
104
105 function aldecrypt(encoded) {
106         var combined = new Buffer(encoded, 'hex');
107         key = new Buffer(ekey, "utf8");
108         // Create iv
109         var iv = new Buffer(16);
110         combined.copy(iv, 0, 0, 16);
111         edata = combined.slice(16).toString('binary');
112         // Decipher encrypted data
113         var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
114         var decrypted, plaintext;
115         plaintext = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));
116
117         return plaintext;
118 }
119
120 // START sockets clusterfuck
121 io.sockets.on('connection', function (socket) {
122         socket.downloadQueue = [];
123         socket.currentItem = null;
124         socket.lastQueueId = null;
125
126         socket.on("login", function (username, password, autologin) {
127                 Deezer.init(username, password, function (err) {
128                         if(err){
129                                 socket.emit("login", err.message);
130                                 logger.logs('Error',"Failed to login, "+err.message);
131                         }else{
132                                 if(autologin){
133                                         var data = username + "\n" + password;
134                                         fs.outputFile(autologinLocation, alencrypt(data) , function(){
135                                                 if(!err){
136                                                         logger.logs('Info',"Added autologin successfully");
137                                                 }else{
138                                                         logger.logs('Info',"Failed to add autologin file");
139                                                 }
140                                         });
141                                 }
142                                 socket.emit("login", "none");
143                                 logger.logs('Info',"Logged in successfully");
144                         }
145                 });
146         });
147
148         socket.on("autologin", function(){
149                 fs.readFile(autologinLocation, function(err, data){
150                         if(err){
151                                 logger.logs('Info',"No auto login found");
152                                 return;
153                         }
154                         try{
155                                 var fdata = aldecrypt(data.toString('utf8'));
156
157                         }catch(e){
158                                 logger.logs('Warning',"Invalid autologin file, deleting");
159                                 fs.unlink(autologinLocation,function(){
160                                 });
161                                 return;
162                         }
163                         fdata = fdata.split('\n');
164                         socket.emit("autologin",fdata[0],fdata[1]);
165                 });
166         });
167
168         socket.on("logout", function(){
169                 logger.logs('Info',"Logged out");
170                 fs.unlink(autologinLocation,function(){
171                 });
172                 return;
173         });
174
175         Deezer.onDownloadProgress = function (track, progress) {
176                 if (!track.trackSocket) {
177                         return;
178                 }
179
180                 if(track.trackSocket.currentItem.type == "track"){
181                         let complete;
182                         if (!track.trackSocket.currentItem.percentage) {
183                                 track.trackSocket.currentItem.percentage = 0;
184                         }
185                         if(configFile.userDefined.hifi){
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
197                         let percentage = (progress / complete) * 100;
198
199                         if ((percentage - track.trackSocket.currentItem.percentage > 1) || (progress == complete)) {
200                                 track.trackSocket.currentItem.percentage = percentage;
201                                 track.trackSocket.emit("downloadProgress", {
202                                         queueId: track.trackSocket.currentItem.queueId,
203                                         percentage: track.trackSocket.currentItem.percentage
204                                 });
205                         }
206                 }
207         };
208
209         function addToQueue(object) {
210                 socket.downloadQueue.push(object);
211                 socket.emit('addToQueue', object);
212
213                 queueDownload(getNextDownload());
214         }
215
216         function getNextDownload() {
217                 if (socket.currentItem != null || socket.downloadQueue.length == 0) {
218                         if (socket.downloadQueue.length == 0 && socket.currentItem == null) {
219                                 socket.emit("emptyDownloadQueue", {});
220                         }
221                         return null;
222                 }
223                 socket.currentItem = socket.downloadQueue[0];
224                 return socket.currentItem;
225         }
226
227         //currentItem: the current item being downloaded at that moment such as a track or an album
228         //downloadQueue: the tracks in the queue to be downloaded
229         //lastQueueId: the most recent queueID
230         //queueId: random number generated when user clicks download on something
231         function queueDownload(downloading) {
232                 if (!downloading) return;
233
234                 // New batch emits new message
235                 if (socket.lastQueueId != downloading.queueId) {
236                         if (downloading.type != "spotifyplaylist"){
237                                 socket.emit("downloadStarted", {queueId: downloading.queueId});
238                         }
239                         socket.lastQueueId = downloading.queueId;
240                 }
241
242                 if (downloading.type == "track") {
243                         logger.logs('Info',"Registered a track "+downloading.id);
244                         downloadTrack([downloading.id,0], downloading.settings, null, function (err) {
245                                 if (err) {
246                                         downloading.failed++;
247                                 } else {
248                                         downloading.downloaded++;
249                                 }
250                                 socket.emit("updateQueue", downloading);
251                                 if (socket.downloadQueue[0] && (socket.downloadQueue[0].queueId == downloading.queueId)) {
252                                         socket.downloadQueue.shift();
253                                 }
254                                 socket.currentItem = null;
255                                 //fs.rmdirSync(coverArtDir);
256                                 queueDownload(getNextDownload());
257                         });
258                 } else if (downloading.type == "playlist") {
259                         logger.logs('Info',"Registered a playlist "+downloading.id);
260                         Deezer.getPlaylistTracks(downloading.id, function (tracks, err) {
261                                 downloading.settings.plName = downloading.name;
262                                 async.eachSeries(tracks.data, function (t, callback) {
263                                         if (downloading.cancelFlag) {
264                                                 logger.logs('Info',"Stopping the playlist queue");
265                                                 callback("stop");
266                                                 return;
267                                         }
268                                         downloading.settings.playlist = {
269                                                 position: tracks.data.indexOf(t),
270                                                 fullSize: tracks.data.length
271                                         };
272                                         downloadTrack([t.id,0], downloading.settings, null, function (err) {
273                                                 if (!err) {
274                                                         downloading.downloaded++;
275                                                 } else {
276                                                         downloading.failed++;
277                                                 }
278                                                 socket.emit("downloadProgress", {
279                                                         queueId: downloading.queueId,
280                                                         percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
281                                                 });
282                                                 socket.emit("updateQueue", downloading);
283                                                 callback();
284                                         });
285                                 }, function (err) {
286                                         logger.logs('Info',"Playlist finished "+downloading.name);
287                                         if(typeof socket.downloadQueue[0] != 'undefined'){
288                                                 socket.emit("downloadProgress", {
289                                                         queueId: socket.downloadQueue[0].queueId,
290                                                         percentage: 100
291                                                 });
292                                         }
293                                         if (downloading && socket.downloadQueue[0] && socket.downloadQueue[0].queueId == downloading.queueId) socket.downloadQueue.shift();
294                                         socket.currentItem = null;
295                                         //fs.rmdirSync(coverArtDir);
296                                         queueDownload(getNextDownload());
297                                 });
298                         });
299                 } else if (downloading.type == "spotifyplaylist") {
300                                 spotifyApi.clientCredentialsGrant().then(function(creds) {
301                                         downloading.settings.plName = downloading.name;
302                                         spotifyApi.setAccessToken(creds.body['access_token']);
303                                         numPages=Math.floor((downloading.size-1)/100);
304                                         let pages = []
305                                         downloading.playlistContent = new Array(downloading.size);
306                                         for (let offset = 0; offset<=numPages; offset++){
307                                                 pages.push(new Promise(function(resolvePage) {
308                                                         spotifyApi.getPlaylistTracks(downloading.settings.spotifyUser, downloading.id, {fields: "", offset: offset}).then(function(resp) {
309                                                                 resp.body['items'].forEach((t, index) => {
310                                                                         downloading.playlistContent[(offset*100)+index] = new Promise(function(resolve, reject) {
311                                                                                 Deezer.track2ID(t.track.artists[0].name, t.track.name, function (response,err){
312                                                                                         resolve([response,0]);
313                                                                                 });
314                                                                         });
315                                                                 });
316                                                                 resolvePage();
317                                                         }, function(err) {logger.logs('Error','Something went wrong!'+err)});
318                                                 }));
319                                         }
320                                         logger.logs("Info","Waiting for all pages");
321                                         Promise.all(pages).then((val)=>{
322                                                 logger.logs("Info","Waiting for all tracks to be converted");
323                                                 Promise.all(downloading.playlistContent).then((values)=>{
324                                                         logger.logs("Info","All tracks converted, starting download");
325                                                         socket.emit("downloadStarted", {queueId: downloading.queueId});
326                                                         async.eachSeries(values, function (t, callback) {
327                                                                 if (downloading.cancelFlag) {
328                                                                         logger.logs('Info',"Stopping the playlist queue");
329                                                                         callback("stop");
330                                                                         return;
331                                                                 }
332                                                                 downloading.settings.playlist = {
333                                                                         position: values.indexOf(t),
334                                                                         fullSize: values.length
335                                                                 };
336                                                                 downloadTrack(t, downloading.settings, null, function (err) {
337                                                                         if (!err) {
338                                                                                 downloading.downloaded++;
339                                                                         } else {
340                                                                                 downloading.failed++;
341                                                                         }
342                                                                         socket.emit("downloadProgress", {
343                                                                                 queueId: downloading.queueId,
344                                                                                 percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
345                                                                         });
346                                                                         socket.emit("updateQueue", downloading);
347                                                                         callback();
348                                                                 });
349                                                         }, function (err) {
350                                                                 logger.logs('Info',"Playlist finished "+downloading.name);
351                                                                 if(typeof socket.downloadQueue[0] != 'undefined'){
352                                                                         socket.emit("downloadProgress", {
353                                                                                 queueId: socket.downloadQueue[0].queueId,
354                                                                                 percentage: 100
355                                                                         });
356                                                                 }
357                                                                 if (downloading && socket.downloadQueue[0] && socket.downloadQueue[0].queueId == downloading.queueId) socket.downloadQueue.shift();
358                                                                 socket.currentItem = null;
359                                                                 //fs.rmdirSync(coverArtDir);
360                                                                 queueDownload(getNextDownload());
361                                                         });
362                                                 }).catch((err)=>{
363                                                         logger.logs('Error','Something went wrong!'+err);
364                                                 });
365                                         }).catch((err)=>{
366                                                 logger.logs('Error','Something went wrong!'+err);
367                                         });
368                                 }, function(err) {logger.logs('Error','Something went wrong!'+err)});
369                 } else if (downloading.type == "album") {
370                         logger.logs('Info',"Registered an album "+downloading.id);
371                         Deezer.getAlbumTracks(downloading.id, function (tracks, err) {
372                                 downloading.settings.tagPosition = true;
373                                 downloading.settings.albName = downloading.name;
374                                 downloading.settings.artName = downloading.artist;
375                                 async.eachSeries(tracks.data, function (t, callback) {
376                                         if (downloading.cancelFlag) {
377                                                 logger.logs('Info',"Stopping the album queue");
378                                                 callback("stop");
379                                                 return;
380                                         }
381                                         downloading.settings.playlist = {
382                                                 position: tracks.data.indexOf(t),
383                                                 fullSize: tracks.data.length
384                                         };
385                                         downloadTrack([t.id,0], downloading.settings, null, function (err) {
386                                                 if (!err) {
387                                                         downloading.downloaded++;
388                                                 } else {
389                                                         downloading.failed++;
390                                                 }
391                                                 socket.emit("downloadProgress", {
392                                                         queueId: downloading.queueId,
393                                                         percentage: ((downloading.downloaded+downloading.failed) / downloading.size) * 100
394                                                 });
395                                                 socket.emit("updateQueue", downloading);
396                                                 callback();
397                                         });
398                                 }, function (err) {
399                                         if (downloading.countPerAlbum) {
400                                                 if (socket.downloadQueue.length > 1 && socket.downloadQueue[1].queueId == downloading.queueId) {
401                                                         socket.downloadQueue[1].download = downloading.downloaded;
402                                                 }
403                                                 socket.emit("updateQueue", downloading);
404                                         }
405                                         logger.logs('Info',"Album finished "+downloading.name);
406                                         if(typeof socket.downloadQueue[0] != 'undefined'){
407                                                 socket.emit("downloadProgress", {
408                                                         queueId: socket.downloadQueue[0].queueId,
409                                                         percentage: 100
410                                                 });
411                                         }
412                                         if (downloading && socket.downloadQueue[0] && socket.downloadQueue[0].queueId == downloading.queueId) socket.downloadQueue.shift();
413                                         socket.currentItem = null;
414                                         queueDownload(getNextDownload());
415                                 });
416                         });
417                 }
418         }
419
420         socket.on("downloadtrack", function (data) {
421                 Deezer.getTrack(data.id, configFile.userDefined.hifi, function (track, err) {
422                         if (err) {
423                                 return;
424                         }
425                         let queueId = "id" + Math.random().toString(36).substring(2);
426                         let _track = {
427                                 name: track["SNG_TITLE"],
428                                 size: 1,
429                                 downloaded: 0,
430                                 failed: 0,
431                                 queueId: queueId,
432                                 id: track["SNG_ID"],
433                                 type: "track"
434                         };
435                         if (track["VERSION"]) _track.name = _track.name + " " + track["VERSION"];
436                         _track.settings = data.settings || {};
437                         addToQueue(_track);
438                 });
439         });
440
441         socket.on("downloadplaylist", function (data) {
442                 Deezer.getPlaylist(data.id, function (playlist, err) {
443                         if (err) {
444                                 return;
445                         }
446                         Deezer.getPlaylistSize(data.id, function (size, err) {
447                                 if (err) {
448                                         return;
449                                 }
450                                 let queueId = "id" + Math.random().toString(36).substring(2);
451                                 let _playlist = {
452                                         name: playlist["title"],
453                                         size: size,
454                                         downloaded: 0,
455                                         failed: 0,
456                                         queueId: queueId,
457                                         id: playlist["id"],
458                                         type: "playlist"
459                                 };
460                                 _playlist.settings = data.settings || {};
461                                 addToQueue(_playlist);
462                         });
463                 });
464         });
465
466         socket.on("downloadspotifyplaylist", function (data) {
467                 spotifyApi.clientCredentialsGrant().then(function(creds) {
468                         spotifyApi.setAccessToken(creds.body['access_token']);
469                         spotifyApi.getPlaylist(data.settings.spotifyUser, data.id, {fields: "id,name,tracks.total"}).then(function(resp) {
470                                 let queueId = "id" + Math.random().toString(36).substring(2);
471                                 let _playlist = {
472                                         name: resp.body["name"],
473                                         size: resp.body["tracks"]["total"],
474                                         downloaded: 0,
475                                         failed: 0,
476                                         queueId: queueId,
477                                         id: resp.body["id"],
478                                         type: "spotifyplaylist"
479                                 };
480                                 _playlist.settings = data.settings || {};
481                                 addToQueue(_playlist);
482                         }, function(err) {
483                                 logger.logs('Error','Something went wrong!'+err);
484                         });
485                 },
486                 function(err) {
487                         logger.logs('Error','Something went wrong!'+err);
488                 });
489         });
490
491         socket.on("downloadalbum", function (data) {
492                 Deezer.getAlbum(data.id, function (album, err) {
493                         if (err) {
494                                 return;
495                         }
496                         Deezer.getAlbumSize(data.id, function (size, err) {
497                                 if (err) {
498                                         return;
499                                 }
500                                 let queueId = "id" + Math.random().toString(36).substring(2);
501                                 let _album = {
502                                         name: album["title"],
503                                         label: album["label"],
504                                         artist: album["artist"].name,
505                                         size: size,
506                                         downloaded: 0,
507                                         failed: 0,
508                                         queueId: queueId,
509                                         id: album["id"],
510                                         type: "album"
511                                 };
512                                 _album.settings = data.settings || {};
513                                 addToQueue(_album);
514                         });
515                 });
516         });
517
518         socket.on("downloadartist", function (data) {
519                 Deezer.getArtist(data.id, function (artist, err) {
520                         if (err) {
521                                 return;
522                         }
523                         Deezer.getArtistAlbums(data.id, function (albums, err) {
524                                 if (err) {
525                                         return;
526                                 }
527                                 for (let i = 0; i < albums.data.length; i++) {
528                                         Deezer.getAlbumSize(albums.data[i].id, function(size, err){
529                                                 if(err) {
530                                                   return;
531                                                 }
532                                                 let queueId = "id" + Math.random().toString(36).substring(2);
533                                                 let album = albums.data[i];
534                                                 let _album = {
535                                                         name: album["title"],
536                                                         artist: artist.name,
537                                                         size: size,
538                                                         downloaded: 0,
539                                                         failed: 0,
540                                                         queueId: queueId,
541                                                         id: album["id"],
542                                                         type: "album",
543                                                         countPerAlbum: true
544                                                 };
545                                                 _album.settings = data.settings || {};
546                                                 addToQueue(_album);
547                                         });
548                                 }
549                         });
550                 });
551         });
552
553         socket.on("getChartsTopCountry", function () {
554                 Deezer.getChartsTopCountry(function (charts, err) {
555                         if(err){
556                                 return;
557                         }
558                         if(charts){
559                                 charts = charts.data || [];
560                         }else{
561                                 charts = [];
562                         }
563                         socket.emit("getChartsTopCountry", {charts: charts.data, err: err});
564                 });
565         });
566
567         socket.on("getChartsCountryList", function (data) {
568                 Deezer.getChartsTopCountry(function (charts, err) {
569                         if(err){
570                                 return;
571                         }
572                         if(charts){
573                                 charts = charts.data || [];
574                         }else{
575                                 charts = [];
576                         }
577                         let countries = [];
578                         for (let i = 0; i < charts.length; i++) {
579                                 let obj = {
580                                         country: charts[i].title.replace("Top ", ""),
581                                         picture_small: charts[i].picture_small,
582                                         picture_medium: charts[i].picture_medium,
583                                         picture_big: charts[i].picture_big
584                                 };
585                                 countries.push(obj);
586                         }
587                         socket.emit("getChartsCountryList", {countries: countries, selected: data.selected});
588                 });
589         });
590
591         socket.on("getMePlaylistList", function (d) {
592                 logger.logs("Info","Loading Personal Playlists")
593                 Deezer.getMePlaylists(function (data, err) {
594                         if(err){
595                                 return;
596                         }
597                         if(data){
598                                 data = data.data || [];
599                         }else{
600                                 data = [];
601                         }
602                         var playlists = [];
603                         for (let i = 0; i < data.length; i++) {
604                                 let obj = {
605                                         title: data[i].title,
606                                         image: data[i].picture_small,
607                                         songs: data[i].nb_tracks,
608                                         link: data[i].link
609                                 };
610                                 playlists.push(obj);
611                         }
612                         if (configFile.userDefined.spotifyUser){
613                                 spotifyApi.clientCredentialsGrant().then(function(creds) {
614                                         spotifyApi.setAccessToken(creds.body['access_token']);
615                                         spotifyApi.getUserPlaylists(configFile.userDefined.spotifyUser, {fields: "total"}).then(data=>{
616                                                 let total = data.body.total
617                                                 let numPages=Math.floor((total-1)/20);
618                                                 let pages = [];
619                                                 var playlistList = new Array(total);
620                                                 for (let offset = 0; offset<=numPages; offset++){
621                                                         pages.push(new Promise(function(resolvePage) {
622                                                                 spotifyApi.getUserPlaylists(configFile.userDefined.spotifyUser, {fields: "items(images,name,owner.id,tracks.total,uri)", offset: offset*20}).then(data=>{
623                                                                         data.body.items.forEach((playlist, i)=>{
624                                                                                 playlistList[(offset*20)+i] = {
625                                                                                         title: playlist.name,
626                                                                                         image: (playlist.images[0] ? playlist.images[0].url : ""),
627                                                                                         songs: playlist.tracks.total,
628                                                                                         link: playlist.uri
629                                                                                 };
630                                                                         });
631                                                                         resolvePage();
632                                                                 });
633                                                         }));
634                                                 }
635                                                 Promise.all(pages).then(()=>{
636                                                         playlists = playlists.concat(playlistList);
637                                                         socket.emit("getMePlaylistList", {playlists: playlists});
638                                                 });
639                                         }).catch(err=>{
640                                                 logger.logs("Error",err);
641                                         });
642                                 }).catch(err=>{
643                                         logger.logs("Error",err);
644                                 });
645                         }else{
646                                 socket.emit("getMePlaylistList", {playlists: playlists});
647                         }
648                 });
649         });
650
651         socket.on("getChartsTrackListByCountry", function (data) {
652                 if (!data.country) {
653                         socket.emit("getChartsTrackListByCountry", {err: "No country passed"});
654                         return;
655                 }
656
657                 Deezer.getChartsTopCountry(function (charts, err) {
658                         if(err){
659                                 return;
660                         }
661                         if(charts){
662                                 charts = charts.data || [];
663                         }else{
664                                 charts = [];
665                         }
666                         let countries = [];
667                         for (let i = 0; i < charts.length; i++) {
668                                 countries.push(charts[i].title.replace("Top ", ""));
669                         }
670
671                         if (countries.indexOf(data.country) == -1) {
672                                 socket.emit("getChartsTrackListByCountry", {err: "Country not found"});
673                                 return;
674                         }
675
676                         let playlistId = charts[countries.indexOf(data.country)].id;
677
678                         Deezer.getPlaylistTracks(playlistId, function (tracks, err) {
679                                 if (err) {
680                                         socket.emit("getChartsTrackListByCountry", {err: err});
681                                         return;
682                                 }
683                                 socket.emit("getChartsTrackListByCountry", {
684                                         playlist: charts[countries.indexOf(data.country)],
685                                         tracks: tracks.data
686                                 });
687                         });
688                 });
689         });
690
691         socket.on("search", function (data) {
692                 data.type = data.type || "track";
693                 if (["track", "playlist", "album", "artist"].indexOf(data.type) == -1) {
694                         data.type = "track";
695                 }
696
697                 // Remove "feat."  "ft." and "&" (causes only problems)
698                 data.text = data.text.replace(/ feat[\.]? /g, " ").replace(/ ft[\.]? /g, " ").replace(/\(feat[\.]? /g, " ").replace(/\(ft[\.]? /g, " ").replace(/\&/g, "");
699
700                 Deezer.search(encodeURIComponent(data.text), data.type, function (searchObject, err) {
701                         try {
702                                 socket.emit("search", {type: data.type, items: searchObject.data});
703                         } catch (e) {
704                                 socket.emit("search", {type: data.type, items: []});
705                         }
706                 });
707         });
708
709         socket.on("getInformation", function (data) {
710                 if (!data.type || (["track", "playlist", "album", "artist"].indexOf(data.type) == -1) || !data.id) {
711                         socket.emit("getInformation", {err: -1, response: {}, id: data.id});
712                         return;
713                 }
714
715                 let reqType = data.type.charAt(0).toUpperCase() + data.type.slice(1);
716
717                 Deezer["get" + reqType](data.id, function (response, err) {
718                         if (err) {
719                                 socket.emit("getInformation", {err: "wrong id "+reqType, response: {}, id: data.id});
720                                 return;
721                         }
722                         socket.emit("getInformation", {response: response, id: data.id});
723                 });
724         });
725
726         socket.on("getTrackList", function (data) {
727                 if (!data.type || (["playlist", "album", "artist"].indexOf(data.type) == -1) || !data.id) {
728                         socket.emit("getTrackList", {err: -1, response: {}, id: data.id, reqType: data.type});
729                         return;
730                 }
731
732                 if (data.type == 'artist') {
733                         Deezer.getArtistAlbums(data.id, function (response, err) {
734                                 if (err) {
735                                         socket.emit("getTrackList", {err: "wrong id artist", response: {}, id: data.id, reqType: data.type});
736                                         return;
737                                 }
738                                 socket.emit("getTrackList", {response: response, id: data.id, reqType: data.type});
739                         });
740                 } else {
741                         let reqType = data.type.charAt(0).toUpperCase() + data.type.slice(1);
742
743                         Deezer["get" + reqType + "Tracks"](data.id, function (response, err) {
744                                 if (err) {
745                                         socket.emit("getTrackList", {err: "wrong id "+reqType, response: {}, id: data.id, reqType: data.type});
746                                         return;
747                                 }
748                                 socket.emit("getTrackList", {response: response, id: data.id, reqType: data.type});
749                         });
750                 }
751
752         });
753
754         socket.on("cancelDownload", function (data) {
755                 if (!data.queueId) {
756                         return;
757                 }
758
759                 let cancel = false;
760                 let cancelSuccess;
761
762                 for (let i = 0; i < socket.downloadQueue.length; i++) {
763                         if (data.queueId == socket.downloadQueue[i].queueId) {
764                                 socket.downloadQueue.splice(i, 1);
765                                 i--;
766                                 cancel = true;
767                         }
768                 }
769
770                 if (socket.currentItem && socket.currentItem.queueId == data.queueId) {
771                         cancelSuccess = Deezer.cancelDecryptTrack();
772                         cancel = cancel || cancelSuccess;
773                 }
774
775
776                 if (cancelSuccess && socket.currentItem) {
777                         socket.currentItem.cancelFlag = true;
778                 }
779                 if (cancel) {
780                         socket.emit("cancelDownload", {queueId: data.queueId});
781                 }
782         });
783
784         socket.on("downloadAlreadyInQueue", function (data) {
785                 if (data.id) {
786                         return;
787                 }
788                 let isInQueue = checkIfAlreadyInQueue(data.id);
789                 if (isInQueue) {
790                         socket.emit("downloadAlreadyInQueue", {alreadyInQueue: true, id: data.id, queueId: isInQueue});
791                 } else {
792                         socket.emit("downloadAlreadyInQueue", {alreadyInQueue: false, id: data.id});
793                 }
794         });
795
796         socket.on("getUserSettings", function () {
797                 let settings = configFile.userDefined;
798                 if (!settings.downloadLocation) {
799                         settings.downloadLocation = mainFolder;
800                 }
801
802                 socket.emit('getUserSettings', {settings: settings});
803         });
804
805         socket.on("saveSettings", function (settings) {
806                 if (settings.userDefined.downloadLocation == defaultDownloadDir) {
807                         settings.userDefined.downloadLocation = null;
808                 } else {
809                         settings.userDefined.downloadLocation = path.resolve(settings.userDefined.downloadLocation + path.sep) + path.sep;
810                         mainFolder = settings.userDefined.downloadLocation;
811                 }
812
813                 configFile.userDefined = settings.userDefined;
814                 fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
815                         if (err) return;
816                         logger.logs('Info',"Settings updated");
817                         initFolders();
818                 });
819         });
820
821         function downloadTrack(id, settings, altmetadata, callback) {
822                 logger.logs('Info',"Getting track data");
823                 Deezer.getTrack(id[0], configFile.userDefined.hifi, function (track, err) {
824                         if (err) {
825                                 if(id[1] != 0){
826                                         logger.logs('Warning',"Failed to download track, falling on alternative");
827                                         downloadTrack([id[1],0], settings, null, function(err){
828                                                 callback(err);
829                                         });
830                                 }else{
831                                         logger.logs('Error',"Failed to download track");
832                                         callback(err);
833                                 }
834                                 return;
835                         }
836                         logger.logs('Info',"Getting album data");
837                         Deezer.getAlbum(track["ALB_ID"], function(res, err){
838                                 if(err){
839                                         if(id[1] != 0){
840                                                 logger.logs('Warning',"Failed to download track, falling on alternative");
841                                                 downloadTrack([id[1],0], settings, null, function(err){
842                                                         callback(err);
843                                                 });
844                                         }else{
845                                                 logger.logs('Error',"Failed to download track");
846                                                 callback(new Error("Album does not exists."));
847                                         }
848                                         return;
849                                 }
850                                 logger.logs('Info',"Getting ATrack data");
851                                 Deezer.getATrack(res.tracks.data[res.tracks.data.length - 1].id, function(tres){
852                                         track.trackSocket = socket;
853
854                                         settings = settings || {};
855                                         if (track["VERSION"]) track["SNG_TITLE"] += " " + track["VERSION"];
856                                         var ajson = res;
857                                         var tjson = tres;
858                                         if(track["SNG_CONTRIBUTORS"]){
859                                                 if(track["SNG_CONTRIBUTORS"].composer){
860                                                         var composertag = "";
861                                                         for (var i = 0; i < track["SNG_CONTRIBUTORS"].composer.length; i++) {
862                                                                 composertag += track["SNG_CONTRIBUTORS"].composer[i] + ", ";
863                                                         }
864                                                         composertag = composertag.substring(0,composertag.length-2);
865                                                 }
866                                                 if(track["SNG_CONTRIBUTORS"].musicpublisher){
867                                                         var publishertag = "";
868                                                         for (var i = 0; i < track["SNG_CONTRIBUTORS"].musicpublisher.length; i++) {
869                                                                 publishertag += track["SNG_CONTRIBUTORS"].musicpublisher[i] + ", ";
870                                                         }
871                                                         publishertag = publishertag.substring(0,publishertag.length-2);
872                                                 }
873                                                 if(track["SNG_CONTRIBUTORS"].producer){
874                                                         var producertag = "";
875                                                         for (var i = 0; i < track["SNG_CONTRIBUTORS"].producer.length; i++) {
876                                                                 producertag += track["SNG_CONTRIBUTORS"].producer[i] + ", ";
877                                                         }
878                                                         producertag = producertag.substring(0,producertag.length-2);
879                                                 }
880                                                 if(track["SNG_CONTRIBUTORS"].engineer){
881                                                         var engineertag = "";
882                                                         for (var i = 0; i < track["SNG_CONTRIBUTORS"].engineer.length; i++) {
883                                                                 engineertag += track["SNG_CONTRIBUTORS"].engineer[i] + ", ";
884                                                         }
885                                                         engineertag = engineertag.substring(0,engineertag.length-2);
886                                                 }
887                                                 if(track["SNG_CONTRIBUTORS"].writer){
888                                                         var writertag = "";
889                                                         for (var i = 0; i < track["SNG_CONTRIBUTORS"].writer.length; i++) {
890                                                                 writertag += track["SNG_CONTRIBUTORS"].writer[i] + ", ";
891                                                         }
892                                                         writertag = writertag.substring(0,writertag.length-2);
893                                                 }
894                                                 if(track["SNG_CONTRIBUTORS"].author){
895                                                         var authortag = "";
896                                                         for (var i = 0; i < track["SNG_CONTRIBUTORS"].author.length; i++) {
897                                                                 authortag += track["SNG_CONTRIBUTORS"].author[i] + ", ";
898                                                         }
899                                                         authortag = authortag.substring(0,authortag.length-2);
900                                                 }
901                                                 if(track["SNG_CONTRIBUTORS"].mixer){
902                                                         var mixertag = "";
903                                                         for (var i = 0; i < track["SNG_CONTRIBUTORS"].mixer.length; i++) {
904                                                                 mixertag += track["SNG_CONTRIBUTORS"].mixer[i] + ", ";
905                                                         }
906                                                         mixertag = mixertag.substring(0,mixertag.length-2);
907                                                 }
908                                         }
909                                         let metadata;
910                                         if(altmetadata){
911                                                 metadata = altmetadata;
912                                                 if(track["LYRICS_TEXT"] && !metadata.unsynchronisedLyrics){
913                                                         metadata.unsynchronisedLyrics = {
914                                                                 description: "",
915                                                                 lyrics: track["LYRICS_TEXT"]
916                                                         };
917                                                 }
918                                         }else{
919                                                 metadata = {
920                                                         title: track["SNG_TITLE"],
921                                                         artist: track["ART_NAME"],
922                                                         album: track["ALB_TITLE"],
923                                                         performerInfo: ajson.artist.name,
924                                                         trackNumber: track["TRACK_NUMBER"] + "/" + ajson.nb_tracks,
925                                                         partOfSet: track["DISK_NUMBER"] + "/" + tjson.disk_number,
926                                                         explicit: track["EXPLICIT_LYRICS"],
927                                                         ISRC: track["ISRC"],
928                                                 };
929                                                 if (configFile.userDefined.extendedTags){
930                                                         metadata.length= track["DURATION"];
931                                                         metadata.BARCODE= ajson.upc;
932                                                         metadata.rtype= ajson.record_type;
933                                                         if(track["COPYRIGHT"]){
934                                                                 metadata.copyright = track["COPYRIGHT"];
935                                                         }
936                                                         if(composertag){
937                                                                 metadata.composer = composertag;
938                                                         }
939                                                         if(mixertag){
940                                                                 metadata.mixer = mixertag;
941                                                         }
942                                                         if(authortag){
943                                                                 metadata.author = authortag;
944                                                         }
945                                                         if(writertag){
946                                                                 metadata.writer = writertag;
947                                                         }
948                                                         if(engineertag){
949                                                                 metadata.engineer = engineertag;
950                                                         }
951                                                         if(producertag){
952                                                                 metadata.producer = producertag;
953                                                         }
954                                                         if(track["LYRICS_TEXT"]){
955                                                                 metadata.unsynchronisedLyrics = {
956                                                                         description: "",
957                                                                         lyrics: track["LYRICS_TEXT"]
958                                                                 };
959                                                         }
960                                                         if (track["GAIN"]) {
961                                                                 metadata.trackgain = track["GAIN"];
962                                                         }
963                                                 }
964
965
966                                                 if(ajson.label){
967                                                         metadata.publisher = ajson.label;
968                                                 }
969                                                 if(settings.plName && !(settings.createArtistFolder || settings.createAlbumFolder) && !configFile.userDefined.numplaylistbyalbum){
970                                                         metadata.trackNumber = (parseInt(settings.playlist.position)+1).toString() + "/" + settings.playlist.fullSize;
971                                                         metadata.partOfSet = "1/1";
972                                                 }
973                                                 if(settings.artName){
974                                                         metadata.trackNumber = (settings.playlist.position+1).toString() + "/" + ajson.nb_tracks;
975                                                 }
976                                                 if (0 < parseInt(track["BPM"])) {
977                                                         metadata.bpm = track["BPM"];
978                                                 }
979                                                 if(ajson.genres && ajson.genres.data[0] && ajson.genres.data[0].name){
980                                                         metadata.genre = ajson.genres.data[0].name;
981                                                     if (track.format == 9){
982                                                             metadata.genre = ajson.genres.data[0].name;
983                                                     } else {
984                                                         genreArray = [];
985                                                         var first = true;
986                                                         ajson.genres.data.forEach(function(genre){
987                                                                 genreArray.push(genre.name);
988                                                         });
989                                                         Array.from(new Set(genreArray)).forEach(function(genre){
990                                                                 if(first){
991                                                                         metadata.genre = genre;
992                                                                         first = false;
993                                                                 } else{
994                                                                         if(metadata.genre.indexOf(genre) == -1)
995                                                                                 metadata.genre += String.fromCharCode(parseInt("\u0000",16)) + genre;
996                                                                 }
997                                                         });
998                                                 }
999                                                 }
1000                                                 if (track["ALB_PICTURE"]) {
1001                                                         metadata.image = Deezer.albumPicturesHost + track["ALB_PICTURE"] + settings.artworkSize;
1002                                                 }
1003
1004                                                 if (ajson.release_date) {
1005                                                         metadata.year = ajson.release_date.slice(0, 4);
1006                                                         metadata.date = ajson.release_date;
1007                                                 }else if(track["PHYSICAL_RELEASE_DATE"]){
1008                                                         metadata.year = track["PHYSICAL_RELEASE_DATE"].slice(0, 4);
1009                                                         metadata.date = track["PHYSICAL_RELEASE_DATE"];
1010                                                 }
1011                                         }
1012                                         let filename = fixName(`${metadata.artist} - ${metadata.title}`);
1013                                         if (settings.filename) {
1014                                                 filename = fixName(settingsRegex(metadata, settings.filename, settings.playlist));
1015                                         }
1016
1017                                         let filepath = mainFolder;
1018                                         if (settings.createArtistFolder || settings.createAlbumFolder) {
1019                                                 if(settings.plName){
1020                                                         filepath += antiDot(fixName(settings.plName)) + path.sep;
1021                                                 }
1022                                                 if (settings.createArtistFolder) {
1023                                                         if(settings.artName){
1024                                                                 filepath += antiDot(fixName(settings.artName)) + path.sep;
1025                                                         }else{
1026                                                                 filepath += antiDot(fixName(metadata.artist)) + path.sep;
1027                                                         }
1028                                                 }
1029
1030                                                 if (settings.createAlbumFolder) {
1031                                                         if(settings.artName){
1032                                                                 filepath += antiDot(fixName(settingsRegexAlbum(metadata,settings.foldername,settings.artName,settings.albName))) + path.sep;
1033                                                         }else{
1034                                                                 filepath += antiDot(fixName(settingsRegexAlbum(metadata,settings.foldername,metadata.performerInfo,metadata.album))) + path.sep;
1035                                                         }
1036                                                 }
1037                                         } else if (settings.plName) {
1038                                                 filepath += antiDot(fixName(settings.plName)) + path.sep;
1039                                         } else if (settings.artName) {
1040                                                 filepath += antiDot(fixName(settingsRegexAlbum(metadata,settings.foldername,settings.artName,settings.albName))) + path.sep;
1041                                         }
1042
1043                                         let writePath;
1044                                         if(track.format == 9){
1045                                                 writePath = filepath + filename + '.flac';
1046                                         }else{
1047                                                 writePath = filepath + filename + '.mp3';
1048                                         }
1049                                         if(track["LYRICS_SYNC_JSON"] && configFile.userDefined.syncedlyrics){
1050                                                 var lyricsbuffer = "";
1051                                                 for(var i=0;i<track["LYRICS_SYNC_JSON"].length;i++){
1052                                                         if(track["LYRICS_SYNC_JSON"][i].lrc_timestamp){
1053                                                                 lyricsbuffer += track["LYRICS_SYNC_JSON"][i].lrc_timestamp+track["LYRICS_SYNC_JSON"][i].line+"\r\n";
1054                                                         }else if(i+1 < track["LYRICS_SYNC_JSON"].length){
1055                                                                 lyricsbuffer += track["LYRICS_SYNC_JSON"][i+1].lrc_timestamp+track["LYRICS_SYNC_JSON"][i].line+"\r\n";
1056                                                         }
1057                                                 }
1058                                                 if(track.format == 9){
1059                                                         fs.outputFile(writePath.substring(0,writePath.length-5)+".lrc",lyricsbuffer,function(){});
1060                                                 }else{
1061                                                         fs.outputFile(writePath.substring(0,writePath.length-4)+".lrc",lyricsbuffer,function(){});
1062                                                 }
1063                                         }
1064                                         logger.logs('Info','Downloading file to ' + writePath);
1065                                         if (fs.existsSync(writePath)) {
1066                                                 logger.logs('Info',"Already downloaded: " + metadata.artist + ' - ' + metadata.title);
1067                                                 callback();
1068                                                 return;
1069                                         }
1070
1071                                         //Get image
1072                                         if (metadata.image) {
1073                                                 let imgPath;
1074                                                 //If its not from an album but a playlist.
1075                                                 if(!settings.tagPosition && !settings.createAlbumFolder){
1076                                                         imgPath = coverArtFolder + fixName(metadata.ISRC) + ".jpg";
1077                                                 }else{
1078                                                         imgPath = filepath + "folder.jpg";
1079                                                 }
1080                                                 if(fs.existsSync(imgPath) && !imgPath.includes(coverArtFolder)){
1081                                                         metadata.imagePath = (imgPath).replace(/\\/g, "/");
1082                                                         logger.logs('Info',"Starting the download process CODE:1");
1083                                                         condownload();
1084                                                 }else{
1085                                                         request.get(metadata.image, {encoding: 'binary'}, function(error,response,body){
1086                                                                 if(error){
1087                                                                         logger.logs('Error', error.stack);
1088                                                                         metadata.image = undefined;
1089                                                                         metadata.imagePath = undefined;
1090                                                                         return;
1091                                                                 }
1092                                                                 fs.outputFile(imgPath,body,'binary',function(err){
1093                                                                         if(err){
1094                                                                                 logger.logs('Error', err.stack);
1095                                                                         metadata.image = undefined;
1096                                                                         metadata.imagePath = undefined;
1097                                                                                 return;
1098                                                                         }
1099                                                                         metadata.imagePath = (imgPath).replace(/\\/g, "/");
1100                                                                         logger.logs('Info',"Starting the download process CODE:2");
1101                                                                         condownload();
1102                                                                 })
1103                                                         });
1104                                                 }
1105                                         }else{
1106                                                 metadata.image = undefined;
1107                                                 logger.logs('Info',"Starting the download process CODE:3");
1108                                                 condownload();
1109                                         }
1110                                         function condownload(){
1111                                                 var tempPath = writePath+".temp";
1112                                                 logger.logs('Info',"Downloading and decrypting");
1113                                                 Deezer.decryptTrack(tempPath,track, function (err) {
1114                                                         if (err && err.message == "aborted") {
1115                                                                 socket.currentItem.cancelFlag = true;
1116                                                                 logger.logs('Info',"Track got aborted");
1117                                                                 callback();
1118                                                                 return;
1119                                                         }
1120                                                         if (err) {
1121                                                                 Deezer.hasTrackAlternative(id[0], function (alternative, err) {
1122                                                                         if (err || !alternative) {
1123                                                                                 logger.logs('Error',"Failed to download: " + metadata.artist + " - " + metadata.title);
1124                                                                                 callback(err);
1125                                                                                 return;
1126                                                                         }
1127                                                                         logger.logs('Error',"Failed to download: " + metadata.artist + " - " + metadata.title+", falling on alternative");
1128                                                                         downloadTrack([alternative.SNG_ID,0], settings, metadata, callback);
1129                                                                 });
1130                                                                 return;
1131                                                         }
1132                                                         if (settings.createM3UFile && settings.playlist) {
1133                                                                 if(track.format == 9){
1134                                                                         fs.appendFileSync(filepath + "playlist.m3u", filename + ".flac\r\n");
1135                                                                 }else{
1136                                                                         fs.appendFileSync(filepath + "playlist.m3u", filename + ".mp3\r\n");
1137                                                                 }
1138                                                         }
1139                                                         logger.logs('Info',"Downloaded: " + metadata.artist + " - " + metadata.title);
1140                                                         metadata.artist = '';
1141                                                         var first = true;
1142                                                         artistArray = []
1143                                                         track['ARTISTS'].forEach(function(artist){
1144                                                                 artistArray.push(artist['ART_NAME']);
1145                                                         });
1146                                                         var separator = String.fromCharCode(parseInt("\u0000",16));
1147                                                         if (track.format == 9)
1148                                                             separator = ', ';
1149                                                         Array.from(new Set(artistArray)).forEach(function(artist){
1150                                                                 if(first){
1151                                                                         metadata.artist = artist;
1152                                                                         first = false;
1153                                                                 } else{
1154                                                                         if(metadata.artist.indexOf(artist) == -1)
1155                                                                                 metadata.artist += separator + artist;
1156                                                                 }
1157                                                         });
1158
1159                                                         if(track.format == 9){
1160                                                                 let flacComments = [
1161                                                                         'TITLE=' + metadata.title,
1162                                                                         'ALBUM=' + metadata.album,
1163                                                                         'ALBUMARTIST=' + metadata.performerInfo,
1164                                                                         'ARTIST=' + metadata.artist,
1165                                                                         'TRACKNUMBER=' + splitNumber(metadata.trackNumber,false),
1166                                                                         'DISCNUMBER=' + splitNumber(metadata.partOfSet,false),
1167                                                                         'TRACKTOTAL=' + splitNumber(metadata.trackNumber,true),
1168                                                                         'DISCTOTAL=' + splitNumber(metadata.partOfSet,true),
1169                                                                         'ITUNESADVISORY=' + metadata.explicit,
1170                                                                         'ISRC=' + metadata.ISRC
1171                                                                 ];
1172                                                                 if(configFile.userDefined.extendedTags){
1173                                                                         flacComments.push(
1174                                                                                 'LENGTH=' + metadata.length,
1175                                                                                 'BARCODE=' + metadata.BARCODE
1176                                                                         );
1177                                                                 }
1178                                                                 if(metadata.unsynchronisedLyrics){
1179                                                                         flacComments.push('LYRICS='+metadata.unsynchronisedLyrics.lyrics);
1180                                                                 }
1181                                                                 if(metadata.genre){
1182                                                                         flacComments.push('GENRE=' + metadata.genre);
1183                                                                 }
1184                                                                 if(metadata.copyright){
1185                                                                         flacComments.push('COPYRIGHT=' + metadata.copyright);
1186                                                                 }
1187                                                                 if (0 < parseInt(metadata.year)) {
1188                                                                         flacComments.push('DATE=' + metadata.date);
1189                                                                         flacComments.push('YEAR=' + metadata.year);
1190                                                                 }
1191                                                                 if (0 < parseInt(metadata.bpm)) {
1192                                                                         flacComments.push('BPM=' + metadata.bpm);
1193                                                                 }
1194                                                                 if(metadata.composer){
1195                                                                         flacComments.push('COMPOSER=' + metadata.composer);
1196                                                                 }
1197                                                                 if(metadata.publisher){
1198                                                                         flacComments.push('ORGANIZATION=' + metadata.publisher);
1199                                                                 }
1200                                                                 if(metadata.mixer){
1201                                                                         flacComments.push('MIXER=' + metadata.mixer);
1202                                                                 }
1203                                                                 if(metadata.author){
1204                                                                         flacComments.push('AUTHOR=' + metadata.author);
1205                                                                 }
1206                                                                 if(metadata.writer){
1207                                                                         flacComments.push('WRITER=' + metadata.writer);
1208                                                                 }
1209                                                                 if(metadata.engineer){
1210                                                                         flacComments.push('ENGINEER=' + metadata.engineer);
1211                                                                 }
1212                                                                 if(metadata.producer){
1213                                                                         flacComments.push('PRODUCER=' + metadata.producer);
1214                                                                 }
1215                                                                 if(metadata.trackgain){
1216                                                                         flacComments.push('REPLAYGAIN_TRACK_GAIN=' + metadata.trackgain);
1217                                                                 }
1218                                                                 const reader = fs.createReadStream(tempPath);
1219                                                                 const writer = fs.createWriteStream(writePath);
1220                                                                 let processor = new mflac.Processor({parseMetaDataBlocks: true});
1221
1222                                                                 let vendor = 'reference libFLAC 1.2.1 20070917';
1223                                                                 let cover = null;
1224                                                                 if(metadata.imagePath){
1225                                                                         cover = fs.readFileSync(metadata.imagePath);
1226                                                                 }
1227                                                                 let mdbVorbisPicture;
1228                                                                 let mdbVorbisComment;
1229                                                                 processor.on('preprocess', (mdb) => {
1230                                                                         // Remove existing VORBIS_COMMENT and PICTURE blocks, if any.
1231                                                                         if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type) {
1232                                                                                 mdb.remove();
1233                                                                         } else if (mflac.Processor.MDB_TYPE_PICTURE === mdb.type) {
1234                                                                                 mdb.remove();
1235                                                                         }
1236
1237                                                                         if (mdb.isLast) {
1238                                                                                 var res = 0;
1239                                                                                 if(configFile.userDefined.artworkSize.includes("1400")){
1240                                                                                         res = 1400;
1241                                                                                 }else if(configFile.userDefined.artworkSize.includes("1200")){
1242                                                                                         res = 1200;
1243                                                                                 }else if(configFile.userDefined.artworkSize.includes("1000")){
1244                                                                                         res = 1000;
1245                                                                                 }else if(configFile.userDefined.artworkSize.includes("800")){
1246                                                                                         res = 800;
1247                                                                                 }else if(configFile.userDefined.artworkSize.includes("500")){
1248                                                                                         res = 500;
1249                                                                                 }
1250                                                                                 if(cover){
1251                                                                                         mdbVorbisPicture = mflac.data.MetaDataBlockPicture.create(true, 3, 'image/jpeg', '', res, res, 24, 0, cover);
1252                                                                                 }
1253                                                                                 mdbVorbisComment = mflac.data.MetaDataBlockVorbisComment.create(false, vendor, flacComments);
1254                                                                                 mdb.isLast = false;
1255                                                                         }
1256                                                                 });
1257
1258                                                                 processor.on('postprocess', (mdb) => {
1259                                                                         if (mflac.Processor.MDB_TYPE_VORBIS_COMMENT === mdb.type && null !== mdb.vendor) {
1260                                                                                 vendor = mdb.vendor;
1261                                                                         }
1262
1263                                                                         if (mdbVorbisPicture && mdbVorbisComment) {
1264                                                                                 processor.push(mdbVorbisComment.publish());
1265                                                                                 processor.push(mdbVorbisPicture.publish());
1266                                                                         }else if(mdbVorbisComment){
1267                                                                                 processor.push(mdbVorbisComment.publish());
1268                                                                         }
1269                                                                 });
1270
1271                                                                 reader.on('end', () => {
1272                                                                         fs.remove(tempPath);
1273                                                                 });
1274
1275                                                                 reader.pipe(processor).pipe(writer);
1276                                                         }else{
1277                                                                 const songBuffer = fs.readFileSync(tempPath);
1278                                                                 const writer = new ID3Writer(songBuffer);
1279                                                                 writer.setFrame('TIT2', metadata.title)
1280                                                                         .setFrame('TPE1', [metadata.artist])
1281                                                                         .setFrame('TALB', metadata.album)
1282                                                                         .setFrame('TPE2', metadata.performerInfo)
1283                                                                         .setFrame('TRCK', (configFile.userDefined.partOfSet ? metadata.trackNumber : splitNumber(metadata.trackNumber,false)))
1284                                                                         .setFrame('TPOS', (configFile.userDefined.partOfSet ? metadata.partOfSet : splitNumber(metadata.partOfSet,false)))
1285                                                                         .setFrame('TSRC', metadata.ISRC);
1286                                                                 if (configFile.userDefined.extendedTags){
1287                                                                         writer.setFrame('TLEN', metadata.length)
1288                                                                                 .setFrame('TXXX', {
1289                                                                                         description: 'BARCODE',
1290                                                                                         value: metadata.BARCODE
1291                                                                                 })
1292                                                                 }
1293                                                                 if(metadata.imagePath){
1294                                                                         const coverBuffer = fs.readFileSync(metadata.imagePath);
1295                                                                         writer.setFrame('APIC', {
1296                                                                                 type: 3,
1297                                                                                 data: coverBuffer,
1298                                                                                 description: ''
1299                                                                         });
1300                                                                 }
1301                                                                 if(metadata.unsynchronisedLyrics){
1302                                                                         writer.setFrame('USLT', metadata.unsynchronisedLyrics);
1303                                                                 }
1304                                                                 if(metadata.publisher){
1305                                                                         writer.setFrame('TPUB', metadata.publisher);
1306                                                                 }
1307                                                                 if(metadata.genre){
1308                                                                         writer.setFrame('TCON', [metadata.genre]);
1309                                                                 }
1310                                                                 if(metadata.copyright){
1311                                                                         writer.setFrame('TCOP', metadata.copyright);
1312                                                                 }
1313                                                                 if (0 < parseInt(metadata.year)) {
1314                                                                         writer.setFrame('TDAT', metadata.date);
1315                                                                         writer.setFrame('TYER', metadata.year);
1316                                                                 }
1317                                                                 if (0 < parseInt(metadata.bpm)) {
1318                                                                         writer.setFrame('TBPM', metadata.bpm);
1319                                                                 }
1320                                                                 if(metadata.composer){
1321                                                                         writer.setFrame('TCOM', [metadata.composer]);
1322                                                                 }
1323                                                                 if(metadata.trackgain){
1324                                                                         writer.setFrame('TXXX', {
1325                                                                                 description: 'REPLAYGAIN_TRACK_GAIN',
1326                                                                                 value: metadata.trackgain
1327                                                                         });
1328                                                                 }
1329                                                                 writer.addTag();
1330
1331                                                                 const taggedSongBuffer = Buffer.from(writer.arrayBuffer);
1332                                                                 fs.writeFileSync(writePath, taggedSongBuffer);
1333                                                                 fs.remove(tempPath);
1334                                                         }
1335
1336                                                         callback();
1337                                                 });
1338                                         }
1339                                 });
1340                         });
1341                 });
1342         }
1343
1344         function checkIfAlreadyInQueue(id) {
1345                 let exists = false;
1346                 for (let i = 0; i < socket.downloadQueue.length; i++) {
1347                         if (socket.downloadQueue[i].id == id) {
1348                                 exists = socket.downloadQueue[i].queueId;
1349                         }
1350                 }
1351                 if (socket.currentItem && (socket.currentItem.id == id)) {
1352                         exists = socket.currentItem.queueId;
1353                 }
1354                 return exists;
1355         }
1356 });
1357
1358 // Helper functions
1359
1360 /**
1361  * Updates individual parameters in the settings file
1362  * @param config
1363  * @param value
1364  */
1365 function updateSettingsFile(config, value) {
1366         configFile.userDefined[config] = value;
1367
1368         fs.outputFile(configFileLocation, JSON.stringify(configFile, null, 2), function (err) {
1369                 if (err) return;
1370                 logger.logs('Info',"Settings updated");
1371
1372                 // FIXME: Endless Loop, due to call from initFolders()...crashes soon after startup
1373                 // initFolders();
1374         });
1375 }
1376
1377 function fixName (txt) {
1378   const regEx = /[\0\/\\:*?"<>|]/g;
1379   return txt.replace(regEx, '_');
1380 }
1381
1382 function antiDot(str){
1383         while(str[str.length-1] == "." || str[str.length-1] == " " || str[str.length-1] == "\n"){
1384                 str = str.substring(0,str.length-1);
1385         }
1386         if(str.length < 1){
1387                 str = "dot";
1388         }
1389         return fixName(str);
1390 }
1391
1392 /**
1393  * Initialize the temp folder for covers and main folder for downloads
1394  */
1395 function initFolders() {
1396         // Check if main folder exists
1397         if (!fs.existsSync(mainFolder)) {
1398                 mainFolder = defaultDownloadDir;
1399                 updateSettingsFile('downloadLocation', defaultDownloadDir);
1400         }
1401         //fs.removeSync(coverArtFolder);
1402         fs.ensureDirSync(coverArtFolder);
1403 }
1404
1405 /**
1406  * Creates the name of the tracks replacing wildcards to correct metadata
1407  * @param metadata
1408  * @param filename
1409  * @param playlist
1410  * @returns {XML|string|*}
1411  */
1412 function settingsRegex(metadata, filename, playlist) {
1413         filename = filename.replace(/%title%/g, metadata.title);
1414         filename = filename.replace(/%album%/g, metadata.album);
1415         filename = filename.replace(/%artist%/g, metadata.artist);
1416         filename = filename.replace(/%year%/g, metadata.year);
1417         if(typeof metadata.trackNumber != 'undefined'){
1418                 if(configFile.userDefined.padtrck){
1419                          filename = filename.replace(/%number%/g, pad(splitNumber(metadata.trackNumber, false), splitNumber(metadata.trackNumber, true)));
1420                 }else{
1421                         filename = filename.replace(/%number%/g, splitNumber(metadata.trackNumber, false));
1422                 }
1423         } else {
1424                 filename = filename.replace(/%number%/g, '');
1425         }
1426         return filename;
1427 }
1428
1429 /**
1430  * Creates the name of the albums folder replacing wildcards to correct metadata
1431  * @param metadata
1432  * @param foldername
1433  * @returns {XML|string|*}
1434  */
1435 function settingsRegexAlbum(metadata, foldername, artist, album) {
1436         foldername = foldername.replace(/%album%/g, album);
1437         foldername = foldername.replace(/%artist%/g, artist);
1438         foldername = foldername.replace(/%year%/g, metadata.year);
1439         foldername = foldername.replace(/%type%/g, metadata.rtype);
1440         return foldername;
1441 }
1442
1443 /**
1444  * I really don't understand what this does ... but it does something
1445  * @param str
1446  * @param max
1447  * @returns {String|string|*}
1448  */
1449 function pad(str, max) {
1450         str = str.toString();
1451         max = max.toString();
1452         return str.length < max.length || str.length == 1 ? pad("0" + str, max) : str;
1453 }
1454
1455 /**
1456  * Splits the %number%
1457  * @param string str
1458  * @return string
1459  */
1460 function splitNumber(str,total){
1461         str = str.toString();
1462         var i = str.indexOf("/");
1463         if(total && i > 0){
1464                 return str.slice(i+1, str.length);
1465         }else if(i > 0){
1466                 return str.slice(0, i);
1467         }else{
1468                 return str;
1469         }
1470         return i > 0 ? str.slice(0, i) : str;
1471 }
1472
1473 // Show crash error in console for debugging
1474 process.on('uncaughtException', function (err) {
1475         logger.logs('Error',err.stack,function(){
1476                 socket.emit("message", "Critical Error, report to the developer", err.stack);
1477         });
1478 });
1479
1480 // Exporting vars
1481 module.exports.mainFolder = mainFolder;
1482 module.exports.defaultSettings = defaultSettings;
1483 module.exports.defaultDownloadDir = defaultDownloadDir;