app.js 74 KB

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