Sandbird

Untitled

Sep 7th, 2021
54
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* JSHINT PARAMS - Useful for debugging the script, do not delete
  2. * < see http://jshint.com/docs/options/ > for more details. */
  3.  
  4. /* jslint bitwise: true */
  5. /* jslint esnext: true */
  6. /* jslint moz: true */
  7. /* jshint expr: true */
  8. /* jshint sub:true*/
  9. /* jshint multistr: true */
  10. /* globals postMessage, self, aesjs, hex_md5, unsafeWindow, GM_info, opr, safari*/
  11.  
  12. /* jslint ignore: start */
  13. const W = window;
  14. const D = document;
  15. const uW = unsafeWindow;
  16. /* jslint ignore: end */
  17.  
  18. // START OF METADATAS ------------------------------------------------------------------------------------------------------------------------
  19.  
  20. // ==UserScript==
  21. //
  22. // @name Deezer:Download
  23. // @description Download music from Deezer in any chosen format. Supports HQ even without Premium subscription. Also recommend 'Deezer Premium Enabler' to have a better experience.
  24. // @version 2.1.0
  25. // @author Deki Haker, Kawashi666 & some others.
  26. // @namespace Script from Original 'Deezer:Download', revisited by some developers & contributors.
  27. // @id 13fbc8ca-c92d-4032-aa17-276e77cd8552-Deezer-Download-Revived
  28. //
  29. // @domain deezer.com
  30. // @match http*://*.deezer.com/*
  31. // @run-at document-end
  32. // @delay 1000
  33. // @priority 2
  34. //
  35. // @developer Jonathan Tavares [ Revisions, fixes, proofreading, compatibility ] ,
  36. // K-mik@Z [ Fixes, proofreading (code), style, translation, compatibility and 2 or 3 tricks ] < cool2larime@yahoo.fr > ,
  37. // [...]
  38. //
  39. // @licence BEER-WARE Licence
  40. /* < https://framabin.org/?34d387c34fbe5ac5#4AdsiBRoQGJ0JfW1ueKLMoC/Cn6yph5NxwyEfISQ24o= >
  41. */
  42. // @icon https://e-cdns-files.dzcdn.net/images/common/favicon/favicon-96x96-v00400107.png
  43. //
  44. // @screenshot https://framapic.org/pBnowQP75uGT/6CnUCfbvFx7W.png
  45. //
  46. // @contributor Jonathan Tavares, K-mik@Z,
  47. // you [...] - `Try To Take Over The World`
  48. //
  49. // @name:en Deezer:Download [Revived] - Download your music easily.
  50. // @description:en Download the currently playing song (or any song from the current tracklist) in any chosen format. Supports HQ even without Premium subscription.
  51. //
  52. // @name:fr Deezer:Download [Revived] - Télécharger vos musiques facilement.
  53. // @description:fr-FR Téléchargez la chanson en cours de lecture (ou n'importe quelle chanson de la liste en cours) dans n'importe quel format. Supporte le HQ même sans abonnement Premium.
  54. //
  55. // @name:pt Deezer:Download [Revived] - Baixe sua música facilmente.
  56. // @description:pt-BR Faz o download da música atual (ou qualquer musica na playlist atual) em qualquer formato escolhido. Suporta HQ mesmo sem ter assinatura Premium.
  57. //
  58. /* Compatibility & Security
  59. * ------------------------- < see https://www.chromium.org/developers/design-documents/user-scripts > */
  60. //
  61. // @include http*://*.deezer.com/*
  62. // @connect self
  63. //
  64. // @grant unsafeWindow
  65. // @noframes
  66. // @require https://gist.githubusercontent.com/arantius/3123124/raw/grant-none-shim.js
  67. //
  68. // @require https://cdnjs.cloudflare.com/ajax/libs/aes-js/2.1.0/index.js
  69. // @require https://greasyfork.org/scripts/38763-pajhome-md5/code/PajHome%20MD5.js
  70. //
  71. // @compatible chrom[e|ium] TamperMonkey || ViolentMonkey
  72. // @compatible vivaldi TamperMonkey || ViolentMonkey
  73. // @compatible opera TamperMonkey || ViolentMonkey
  74. // @compatible firefox TamperMonkey || ViolentMonkey || GreaseMonkey (+ FIXME)
  75. //
  76. // ==/UserScript==
  77.  
  78. // END METADATAS ------------------------------------------------------------------------------------------------------------------------------
  79.  
  80.  
  81. // VERSIONS -----------------------------------------------------------------------------------------------------------------------------------
  82. // 2.0.1 : Released Tue Mar 20 2018 13:39:45 GMT+0100 (CET) > 20/03/2018
  83. //
  84. //
  85. //
  86. // MODIFICATIONS made in this version ---------------------------------------------------------------------------------------------------------
  87. /*
  88. * FIXME: - GreaseMonkey ( Works, but you needs to reclick a second time on the link after the conversion is done. )
  89. *
  90. * PERFORMED: - Style :
  91. * (*) Resizing the min/max-width window and some other CSS tricks (spacing) for more readability.
  92. * (*) Put the name of the artist in capital letters and place it before the title.
  93. * (*) Changing Logo of Deezer.
  94. * - Accessibility :
  95. * (*) Reduction of the choice in the name proposition (redundancy).
  96. * (*) Reduction of the choice in the download links (redundancy).
  97. * (*) Adding an escape mode to quit the app.
  98. * (*) Adding a message on the GUI, for GreaseMonkey's users (only), explaining the FIXME.
  99. * - Codage :
  100. * (*) Integration `convert size script` to render the file size more `human readable` (in Mo).
  101. * (*) Reorder some lines in `Blowfish.prototype` and some others, `here and then`.
  102. * (*) Puting in the script the search for term in regex from the url.
  103. * (*) Replacement, where possible, 'single quote' instead of "double quote" in the code.
  104. * (*) Replace as soon as possible semicolons `;` with commas `,` without breaking the code.
  105. * (*) Create `two debug mode (GM_Debug & WORKER_Debug)` with two possible values, 0 or 1 (for having log only on `prod mode`).
  106. * (*) Added console.warn () and console.error () in addition to console.log () (better to debug).
  107. * (*) Creation of a function by stylesheet with its own Id, to facilitate integration.
  108. * (*) Internationalization: Create a translate fn with dictionary (dict) `_getI18N` to translate more easily ( Using translate(`wordToTranslate`) ).
  109. * And assume `en` default when no supported lng are found.
  110. * (*) Reduce the content page loading by adding globals vars outside the code and a `;` at the beginning of the main function.
  111. * - Compatibility & Security :
  112. * (*) Use of the file grant-none-shim.js. This script is intended to be used with @require, for Greasemonkey scripts using `@grant none`.
  113. * It emulates the GM_ APIs as closely as possible, using modern browser features like DOM storage.
  114. * For explanation and license, see in the JS file itself.
  115. * (*) Emulate `unsafeWindow` with gm_win for browsers that don’t support it.
  116. * (*) Using `unsafeWindow.console.log()`, or rather `gm_win.console.log()` instead of `console.log()`. GreaseMonkey trick.
  117. * (*) Adding metadata delay and priority to avoid conflicts with a third-party script.
  118. * (*) Using `noframes` to support `non-frames` capable browsers
  119. * (*) Adding a function to determinate browser prefs lng and to propose GUI translated in case.
  120. * (*) Tested in different browsers (firefox, opera, vivaldi and chrom[e|ium]) with different script Managers (TamperMonkey, ViolentMonkey and GreaseMonkey).
  121. * (*) Adding `patch` to manually trigger deferred DOMContentLoaded.
  122. *
  123. * ABORT: - Adding MD5, SHA1 & SHA-256 sums in externs links of metadatas.
  124. * (If the content of the external resource doesn't match the selected hash, then the resource is not delivered to the userscript).
  125. * ( It's not supported by chrome only).
  126. *
  127. * TODO: - Getting the TrackList when the page change.
  128. * - Making new test with checksums in view of chrome.
  129. *
  130. * IDEAS: - Have a return of test with ( edge || TamperMonkey and perhaps safari || GreaseKit ) - Maybe with user feedback.
  131. * - Improve my English ;)
  132. *
  133. * SPECIAL THANKS: - Stack Overflow < https://stackoverflow.com/ > for the wealth of information and tips provided.
  134. *
  135. */
  136.  
  137. // GLOBALS VARS ( /!\ WARNING ) ----------------------------------------------------------------------------------------------------------------------------------
  138.  
  139. const GM_Debug = 1; //Possible values ( 0 =N || 1 =Y ).
  140. //Write: if(GM_Debug) { gm_win.console.log(`yourMsg` + `whatYouWant`); }; where gm_win === unsafeWindow.
  141.  
  142. // Now all you have to change one character to enable/disable logging when debugging.
  143. // For debugging set var(s) GM_Debug and/or WORKER_Debug value(s) to 1. (searching with CTRL-F, only 2 vars).
  144. // Don't forget to return to 0 after. Logs are only useful in prod and slow down the operation.
  145.  
  146. const AcceptLngs = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pt-BR'];
  147.  
  148. // LANGUAGES MAP If you add new languages in the `LANGUAGES MAP`, add them in the var `AcceptLngs` too (see above).
  149. // Try to respect alphabetical order.
  150. const _getI18N = {
  151. 'Click to open the app' : {
  152. 'de' : `Klicken Sie, um die Anwendung zu öffnen`,
  153. 'en' : `Click to open the app`,
  154. 'es' : `Haga clic para abrir la aplicación`,
  155. 'fr' : `Cliquer pour ouvrir l'application`,
  156. 'it' : `Clicca per aprire l'applicazione`,
  157. 'pt' : `Clique para abrir o aplicativo`,
  158. 'pt-BR' : `Clique para abrir o aplicativo`
  159. },
  160. 'Current track' : {
  161. 'de' : `Aktueller Titel`,
  162. 'en' : `Current track`,
  163. 'es' : `Pista actual`,
  164. 'fr' : `Piste actuelle`,
  165. 'it' : `Traccia corrente`,
  166. 'pt' : `Faixa atual`,
  167. 'pt-BR' : `Faixa atual`
  168. },
  169. 'Available links' : {
  170. 'de' : `Verfügbare Links`,
  171. 'en' : `Available links`,
  172. 'es' : `Enlaces disponibles`,
  173. 'fr' : `Liens disponibles`,
  174. 'it' : `Collegamenti disponibili`,
  175. 'pt' : `Links disponíveis`,
  176. 'pt-BR' : `Links disponíveis`
  177. },
  178. 'Track list' : {
  179. 'de' : `Liste der Titel`,
  180. 'en' : `Track list`,
  181. 'es' : `Lista de canciones`,
  182. 'fr' : `Liste des pistes`,
  183. 'it' : `Elenco dei brani`,
  184. 'pt' : `Lista de trilhas`,
  185. 'pt-BR' : `Lista de trilhas`
  186. },
  187. 'refresh track list' : {
  188. 'de' : `aktualisiere die Liste der Tracks`,
  189. 'en' : `refresh track list`,
  190. 'es' : `actualizar la lista de pistas`,
  191. 'fr' : `actualiser la liste des pistes`,
  192. 'it' : `aggiorna la lista delle tracce`,
  193. 'pt' : `atualize a lista de faixas`,
  194. 'pt-BR' : `atualize a lista de faixas`
  195. },
  196. 'Choose' : {
  197. 'de' : `Wählen`,
  198. 'en' : `Choose`,
  199. 'es' : `Escoger`,
  200. 'fr' : `Choisir`,
  201. 'it' : `Scegliere`,
  202. 'pt' : `Escolher`,
  203. 'pt-BR' : `Escolher`
  204. },
  205. 'Choose the file name' : {
  206. 'de' : `Wählen Sie den Dateinamen`,
  207. 'en' : `Choose the file name`,
  208. 'es' : `Elige el nombre del archivo`,
  209. 'fr' : `Choisir le nom du fichier`,
  210. 'it' : `Scegli il nome del file`,
  211. 'pt' : `Escolha o nome do arquivo`,
  212. 'pt-BR' : `Escolha o nome do arquivo`
  213. },
  214. 'Title' : {
  215. 'de' : `Titel`,
  216. 'en' : `Title`,
  217. 'es' : `Título`,
  218. 'fr' : `Titre`,
  219. 'it' : `Titolo`,
  220. 'pt' : `Título`,
  221. 'pt-BR' : `Título`
  222. },
  223. 'Artist' : {
  224. 'de' : `Künstler`,
  225. 'en' : `Artist`,
  226. 'es' : `Artista`,
  227. 'fr' : `Artiste`,
  228. 'it' : `Artista`,
  229. 'pt' : `Artista`,
  230. 'pt-BR' : `Artista`
  231. }
  232. };
  233.  
  234. // Emulate `unsafeWindow` in browsers that don’t support it. ( http://mths.be/unsafewindow )
  235. const gm_win = (function gmWin(W) {
  236. var a,
  237. e = D.createElement('p'),
  238. onclick = e.getAttribute('onclick'); // get old onclick attribute
  239. try {
  240. a = uW === W ? false : uW;
  241. } finally {
  242. return a || (function() {
  243. // if onclick is not a function, it's not IE7, so use setAttribute
  244. if(typeof(onclick) != 'function') {
  245. // for FF,IE8,Chrome
  246. e.setAttribute('onclick','return W;' + onclick);
  247. // if onclick is a function, use the IE7 method and call onclick() in the anonymous function
  248. } else {
  249. // for IE7
  250. e.onclick = function() {
  251. onclick();
  252. return W;
  253. };
  254. }
  255. }());
  256. }
  257. })();
  258. // You can now use `unsafeWindow`, ehm, safely.
  259. //if(GM_Debug) { gm_win.console.log(gm_win); }
  260. // If the current document uses a JavaScript library, you can use it in your user script like this:
  261. //if(GM_Debug) { gm_win.console.log(gm_win.jQuery); }
  262.  
  263. // DETERMINING THE LANGUAGE OF THE GUI. (Thx to Paul S. - Stack Overflow)
  264. const nav = W.navigator;
  265. const clientLngs = [
  266. nav.language + '' ||
  267. nav.browserLanguage + '' || // Adding `+ ''` for suppress `TypeError: lang.split is not a function`
  268. nav.userLanguage + '',
  269. nav.languages + '',
  270. 'en'+ ''
  271. ].filter(Boolean);
  272. const getRoot = lng => lng.split('-')[0];
  273. const lngRootIncludes = lng => {
  274. let root = getRoot(lng);
  275. return AcceptLngs.includes(root);
  276. };
  277. const candidateLng = clientLngs.find(
  278. lng => lngRootIncludes(lng)
  279. );
  280. const candidateLngRoot = getRoot(candidateLng);
  281. const translate = content => {
  282. let dict = _getI18N[content];
  283. if (!dict) return content;
  284. return dict[candidateLng] || // e.g. first try ru-MD
  285. dict[candidateLngRoot] || // then fall back to ru
  286. content; // then fall back to input (if nothing found, default app lng `en`).
  287. };
  288. // END GLOBALS VARS ----------------------------------------------------------------------------------------------------------------------------------
  289.  
  290.  
  291. // STARTING THE CODE NOW -----------------------------------------------------------------------------------------------------------------------------
  292. ;(function _main() { // Please, don't touch the `;` at the beginning.
  293. 'use strict';
  294.  
  295. // PATCH
  296. // The following manual DOMContentLoaded triggering technique may be of interest for a fix in regards to DOMContentLoaded event being triggered too early.
  297. if( D.createEvent ) { // < https://stackoverflow.com/questions/942921/lazy-loading-the-addthis-script-or-lazy-loading-external-js-content-dependent#answer-943315 >
  298. const evt = document.createEvent('MutationEvents');
  299. evt.initMutationEvent('DOMContentLoaded', true, true, document, '', '', '', 0);
  300. D.dispatchEvent(evt);
  301. }
  302.  
  303. if( GM_Debug ) {
  304. const arr = [];
  305. const ua = navigator.userAgent;
  306. arr.push( `---------------------------------------------------------------------------` );
  307. arr.push( `_Report date [ ` + (new Date()) + ` > ` + (new Date().toLocaleDateString(candidateLngRoot)) + ` ]` );
  308. // Determining Browser type // https://stackoverflow.com/questions/42015724/best-practice-for-browser-details-detection-should-i-do-it-in-client-side-usi
  309. const _wichBrwsr_ = (function _getBrwsr(){
  310. let isOpera = ( !!W.opr && !!opr.addons ) || !!W.opera || ua.indexOf(' OPR/') >= 0, // Opera 8.0+
  311. isFirefox = typeof InstallTrigger !== 'undefined', // Firefox 1.0+
  312. isSafari =
  313. /constructor/i.test( W.HTMLElement ) ||
  314. (function (p) {
  315. return p.toString() === '[object SafariRemoteNotification]';
  316. })( !W['safari'] || (typeof safari !== 'undefined' && safari.pushNotification) ), // Safari 3.0+ "[object HTMLElementConstructor]"
  317. isIE = /*@cc_on!@*/false || !!D.documentMode, // Internet Explorer 6-11
  318. isEdge = !isIE && !!W.StyleMedia, // Edge 20+
  319. isChrome = !!W.chrome && !!W.chrome.webstore; // Chrome 1+
  320. if ( isOpera ) { return 'Opera'; }
  321. if ( isFirefox ) { return 'Firefox'; }
  322. if ( isSafari ) { return 'Safari'; }
  323. if ( isIE ) { return 'IE'; }
  324. if ( isEdge ) { return 'Edge'; }
  325. if ( isChrome ) { return 'Chrome'; }
  326. return 'Unknown';
  327. }()),
  328. _whichMajVer_ = (function getBrowserMajorVersion() {
  329. let tem,
  330. M = ua.match( /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i ) || [];
  331. if ( /trident/i.test(M[1]) ) {
  332. tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
  333. return ( tem[1] || '' );
  334. }
  335. if ( M[1] === 'Chrome' ) {
  336. tem = ua.match( /\b(OPR|Edge)\/(\d+)/ );
  337. if ( tem !== null ) return tem[2];
  338. }
  339. M = M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
  340. if ( ( tem = ua.match( /version\/(\d+)/i ) ) !== null ) M.splice(1, 1, tem[1]);
  341. return M[1];
  342. }());
  343. arr.push( `_Browser [ ` + navigator.appCodeName + ` ` + _wichBrwsr_ + ` ` + _whichMajVer_ + ` (` + candidateLng + `) ]` );
  344. // What's your script manager?
  345. arr.push( `_Script Manager [ ` + GM_info.scriptHandler + ` v.` + GM_info.version + ` ]` );
  346. for (var j = 0, count = arr.length; j < count; j++) {
  347. gm_win.console.log( arr[j] );
  348. }
  349. // Is script updated?
  350. let isUpdate = GM_info.scriptWillUpdate,
  351. msgScriptInfos = `_Infos script [ ` + GM_info.script.name + ` v.` + GM_info.script.version;
  352. if ( GM_info.scriptWillUpdate == 1 ) {
  353. gm_win.console.log(
  354. msgScriptInfos += ` ( is updated: ` + isUpdate + `, last update: ` + GM_info.script.lastUpdated + ` ) ]`
  355. );
  356. }
  357. else { gm_win.console.log( msgScriptInfos += ` ( is updated: ` + isUpdate + ` ) ]` ); }
  358. gm_win.console.log( `---------------------------------------------------------------------------` ); gm_win.console.log( ); // Line break
  359. }
  360. // For metadatas of the script:
  361. //if(GM_Debug) { gm_win.console.info(`UserScript Metadatas: ` + `%s`, GM_info.scriptMetaStr); } //unsafeWindow is not defined here, use gm_win instead.
  362. //if(GM_Debug) { gm_win.console.info(`UserScript Metadatas: ` + `%s`, JSON.stringify(GM_info.script)); } //unsafeWindow is not defined here, use gm_win instead.
  363.  
  364. /*
  365. // DETERMINING THE LANGUAGE OF THE GUI.
  366. nav = window.navigator,
  367. navLng = nav.language || nav.browserLanguage || nav.userLanguage,
  368. prefs = nav.languages,
  369. navPrefLng = prefs[0],
  370. defaultLng = 'en',
  371. lng = navLng, //It must be set for translation
  372.  
  373. // CHECK THE LANGUAGE OF THE NAVIGATOR.
  374. checkLng = function checkLng() {
  375. // isL10nAvailable:
  376. if (AcceptLngs.indexOf(lng) < -1) {
  377. lng = lng;
  378. return lng;
  379. }
  380. },
  381. // SEARCH IF LANGUAGE OF THE APP IS AVAILABLE.
  382. isLngDispo = function isLngDispo() {
  383. // isSubstrL10nAvailable:
  384. checkLng();
  385. if (AcceptLngs.indexOf(lng) === -1) {
  386. lng = lng.substring(0,2);
  387. return lng;
  388. }
  389. },
  390. // TO DO IF LANGUAGE SUPPORTED ISN'T FIND.
  391. setLng = function setLng() {
  392. // isL10nNoAvailable:
  393. isLngDispo();
  394. if (AcceptLngs.indexOf(lng) === -1) {
  395. lng = defaultLng;
  396. return lng;
  397. }
  398. };
  399.  
  400. // APPLYING LANGUAGE FOR THE APP.
  401. setLng();
  402. */
  403.  
  404. /*
  405. function convertDate(date) {
  406. var matches,
  407. convertedDate,
  408. fraction;
  409. // we have a date, so just return it.
  410. if (typeof(date) == 'object') {
  411. return date;
  412. };
  413.  
  414. matches = date.toString().match( /(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})([\.,]\d{1,3})?)?(Z|\+00:?00)?/ );
  415.  
  416. if (matches) {
  417. for (var i = 1; i <= 6; i++) {
  418. matches[i] = parseInt( matches[i], 10 ) || 0;
  419. }
  420.  
  421. // month starts on 0
  422. matches[2] -= 1;
  423.  
  424. fraction = matches[7] ? 1000 * ('0' + matches[7]) : null;
  425.  
  426. if (matches[8]) {
  427. convertedDate = new Date( Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], fraction) );
  428. } else {
  429. convertedDate = new Date( matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], fraction );
  430. }
  431. } else if (typeof(date) == 'number') {
  432. // UNIX timestamp
  433. convertedDate = new Date();
  434. convertedDate.setTime(date);
  435. } else if (date.match( /([A-Z][a-z]{2}) ([A-Z][a-z]{2}) (\d+) (\d+:\d+:\d+) ([+-]\d+) (\d+)/ )) {
  436. // This format `Wed Jul 20 13:03:39 +0000 2011` is parsed by
  437. // webkit/firefox, but not by IE, so we must parse it manually.
  438. convertedDate = new Date();
  439. convertedDate.setTime(Date.parse([
  440. RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$6, RegExp.$4, RegExp.$5
  441. ].join(' ')));
  442. } else if (date.match(/\d+ \d+:\d+:\d+ [+-]\d+ \d+/)) {
  443. // a valid javascript format with timezone info
  444. convertedDate = new Date();
  445. convertedDate.setTime(Date.parse(date));
  446. } else {
  447. // an arbitrary javascript string
  448. convertedDate = new Date();
  449. convertedDate.setTime(Date.parse(date));
  450. }
  451.  
  452. return convertedDate;
  453. }
  454. */
  455. /* https://stackoverflow.com/questions/4784745/how-can-i-measure-the-execution-time-of-a-script
  456. function time_my_script(script) {
  457. const start = performance.now();
  458. script();
  459. return performance.now() - start;
  460. }
  461. */
  462. //TESTING URLS WITH REGEX
  463. if ( D.domain == 'deezer.com' ) {
  464. if ( !!location.href.match( /(login|register|signout|blabla)/ ) ) {
  465. if( GM_Debug ) { gm_win.console.info( `This url contains an unwanted word, don\'t apply Downloader App, applying Deezer:Download Logo only` ); }
  466. _dzLogoCss_(); if( GM_Debug ) { gm_win.console.info( `Applying Deezer:Download Logo.` ); }
  467. } else { // This url not contains unwanted things
  468. if( GM_Debug ) { gm_win.console.info( `Nothing prohibited here... Yeah, we loose ourselves ! 😊` ); }
  469. _dzLogoCss_(); if( GM_Debug ) { gm_win.console.log( `Applying Deezer:Download Logo. ✓` ); };
  470. _dzApp_(uW, D); if( GM_Debug ) { gm_win.console.log( `Create the app. ✓` ); }
  471.  
  472. // FIXME: Having an info for greasemonkey's users into the GUI
  473. if ( GM_info.scriptHandler === 'Greasemonkey' ) { // User is running Greasemonkey, do this.
  474. _gmAlertCss_(); if(GM_Debug) { gm_win.console.info( `Applying FIXME for `, GM_info.scriptHandler ); }
  475. }
  476. }
  477. if( GM_Debug ) {
  478. var i,
  479. output = '',
  480. // Remember when we started
  481. start = performance.now();
  482. for ( i = 1; i <= 1e6; i++ )
  483. output += i;
  484. // Remember when we finished
  485. var end = performance.now();
  486. // Now calculate and output the difference
  487. gm_win.console.info( `All done ! 😊 The page Loading in: ` + (end - start) + ` ms ( for more precision, https://developers.google.com/web/updates/2012/08/When-milliseconds-are-not-enough-performance-now )` );
  488. gm_win.console.log( ); // Line break
  489. }
  490. }
  491.  
  492. function _dzApp_(W, D) {
  493.  
  494. // WORKER
  495. function createWorker(code) {
  496.  
  497. const blobURL = URL.createObjectURL(new Blob(['(', code.toString(), ')()'], {
  498. type: 'application/javascript'
  499. })),
  500. worker = new Worker(blobURL);
  501. URL.revokeObjectURL(blobURL);
  502. return worker;
  503. }
  504.  
  505.  
  506. const mainWk = createWorker(function() { // gm_win && _getI18N are undefined here < FIND a soluce
  507.  
  508.  
  509. const WORKER_Debug = 0; //Possible values ( 0 =N || 1 =Y ).
  510. //`Debug mode` ONLY for the var mainWk. For all the rest, we use GM_Debug.
  511. //Write: if(WORKER_Debug) { gm_win.console.log(``yourMsg` + `whatYouWant``); } where gm_win = unsafeWindow.
  512.  
  513.  
  514. // BLOWFISH LIBRARY, adapted from https://github.com/agorlov/javascript-blowfish (MIT-licensed)
  515. // Modified to work with byte arrays, and also supports encryption / decryption in-place for buffers.
  516. // Cannot be @require-d, as it is part of worker code. Workers share NO data so everything must be embedded.
  517. const Blowfish = function(key, mode) {
  518. this.key = key;
  519. if (mode === 'ecb' || mode === 'cbc') this.mode = mode;
  520. this.sBox0 = Blowfish.sBox0.slice();
  521. this.sBox1 = Blowfish.sBox1.slice();
  522. this.sBox2 = Blowfish.sBox2.slice();
  523. this.sBox3 = Blowfish.sBox3.slice();
  524. this.pArray = Blowfish.pArray.slice();
  525. this.generateSubkeys(key);
  526. };
  527. Blowfish.prototype = {
  528. sBox0: null,
  529. sBox1: null,
  530. sBox2: null,
  531. sBox3: null,
  532. pArray: null,
  533. key: null,
  534. mode: 'ecb',
  535. iv: 'abc12345',
  536. trimZeros: (input) => input.replace(/\0+$/g, ''),
  537. fixNegative: (number) => (number >>> 0),
  538. num2block32: (num) => [num >>> 24, num << 8 >>> 24, num << 16 >>> 24, num << 24 >>> 24],
  539. block32toNum: function(block32) {
  540. return this.fixNegative(block32[0] << 24 | block32[1] << 16 | block32[2] << 8 | block32[3]);
  541. },
  542. xor: function(a, b) {
  543. return this.fixNegative(a ^ b);
  544. },
  545. addMod32: function(a, b) {
  546. return this.fixNegative((a + b) | 0);
  547. },
  548. split64by32: function(block64) {
  549. return [this.block32toNum(block64.slice(0, 4)), this.block32toNum(block64.slice(4, 8))];
  550. },
  551. encrypt: function(data, iv) {
  552. if (this.mode === 'ecb') return this.encryptECB(data);
  553. if (this.mode === 'cbc') return this.encryptCBC(data, iv);
  554. throw new Error( `BF: unknown cipher mode` );
  555. },
  556. decrypt: function(data, iv) {
  557. if (this.mode === 'ecb') return this.decryptECB(data);
  558. if (this.mode === 'cbc') return this.decryptCBC(data, iv);
  559. throw new Error('BF: unknown cipher mode');
  560. },
  561. encryptECB: function(data) {
  562. let blocks = Math.ceil(data.length / 8),
  563. encrypted = [];
  564. for (var i = 0; i < blocks; i++) {
  565. var block = data.slice(i * 8, (i + 1) * 8);
  566. if (block.length < 8) throw new Error( `BF: data length not multiple of 8` ); // var count = 8 - block.length; while (0 < count--) block += "\0";
  567. var xL,
  568. xR,
  569. xLxR = this.split64by32(block);
  570. xL = xLxR[0],
  571. xR = xLxR[1],
  572. xLxR = this.encipher(xL, xR),
  573. xL = xLxR[0],
  574. xR = xLxR[1];
  575. if (data instanceof Uint8Array) {
  576. data.set(this.num2block32(xL), i * 8),
  577. data.set(this.num2block32(xR), i * 8 + 4);
  578. } else encrypted = encrypted.concat(this.num2block32(xL), this.num2block32(xR));
  579. }
  580. if (!(data instanceof Uint8Array)) return encrypted;
  581. },
  582. encryptCBC: function(data, iv) {
  583. let blocks = Math.ceil(data.length / 8),
  584. ivL,
  585. ivR,
  586. ivLivR,
  587. encrypted = [];
  588. ivLivR = this.split64by32(iv),
  589. ivL = ivLivR[0],
  590. ivR = ivLivR[1];
  591. for (var i = 0; i < blocks; i++) {
  592. var block = data.slice(i * 8, (i + 1) * 8);
  593. if (block.length < 8) throw new Error( `BF: data length not multiple of 8` ); // var count = 8 - block.length; while (0 < count--) block += "\0";
  594. var xL,
  595. xR,
  596. xLxR;
  597. xLxR = this.split64by32(block),
  598. xL = xLxR[0],
  599. xR = xLxR[1],
  600. xL = this.xor(xL, ivL),
  601. xR = this.xor(xR, ivR),
  602. xLxR = this.encipher(xL, xR),
  603. xL = xLxR[0],
  604. xR = xLxR[1],
  605. ivL = xL,
  606. ivR = xR;
  607. if (data instanceof Uint8Array) {
  608. data.set(this.num2block32(xL), i * 8),
  609. data.set(this.num2block32(xR), i * 8 + 4);
  610. } else encrypted = encrypted.concat(this.num2block32(xL), this.num2block32(xR));
  611. }
  612. if (!(data instanceof Uint8Array)) return encrypted;
  613. },
  614. decryptECB: function(data) {
  615. let blocks = Math.ceil(data.length / 8),
  616. decrypted = [];
  617. for (var i = 0; i < blocks; i++) {
  618. var block = data.slice(i * 8, (i + 1) * 8);
  619. if (block.length < 8) throw new Error( `BF: ciphertext too short (must be multiple of 8 bytes)` );
  620. var xL,
  621. xR,
  622. xLxR;
  623. xLxR = this.split64by32(block),
  624. xL = xLxR[0],
  625. xR = xLxR[1],
  626. xLxR = this.decipher(xL, xR),
  627. xL = xLxR[0],
  628. xR = xLxR[1];
  629. if (data instanceof Uint8Array) {
  630. data.set(this.num2block32(xL), i * 8),
  631. data.set(this.num2block32(xR), i * 8 + 4);
  632. } else decrypted = decrypted.concat(this.num2block32(xL), this.num2block32(xR));
  633. }
  634. if (!(data instanceof Uint8Array)) return decrypted;
  635. },
  636. decryptCBC: function(data, iv) {
  637. let blocks = Math.ceil(data.length / 8),
  638. ivL,
  639. ivR,
  640. ivLtmp,
  641. ivRtmp,
  642. ivLivR,
  643. decrypted = [];
  644. ivLivR = this.split64by32(iv),
  645. ivL = ivLivR[0],
  646. ivR = ivLivR[1];
  647. for (var i = 0; i < blocks; i++) {
  648. var block = data.slice(i * 8, (i + 1) * 8);
  649. if (block.length < 8) throw new Error( `BF: ciphertext too short (must be multiple of 8 bytes)` );
  650. var xL,
  651. xR,
  652. xLxR;
  653. xLxR = this.split64by32(block),
  654. xL = xLxR[0],
  655. xR = xLxR[1],
  656. ivLtmp = xL,
  657. ivRtmp = xR,
  658. xLxR = this.decipher(xL, xR),
  659. xL = xLxR[0],
  660. xR = xLxR[1],
  661. xL = this.xor(xL, ivL),
  662. xR = this.xor(xR, ivR),
  663. ivL = ivLtmp,
  664. ivR = ivRtmp;
  665. if (data instanceof Uint8Array) {
  666. data.set(this.num2block32(xL), i * 8),
  667. data.set(this.num2block32(xR), i * 8 + 4);
  668. } else decrypted = decrypted.concat(this.num2block32(xL), this.num2block32(xR));
  669. }
  670. if (!(data instanceof Uint8Array)) return decrypted;
  671. },
  672. F: function(xL) {
  673. let a = xL >>> 24,
  674. b = xL << 8 >>> 24,
  675. c = xL << 16 >>> 24,
  676. d = xL << 24 >>> 24,
  677. res = this.addMod32(this.sBox0[a], this.sBox1[b]);
  678. res = this.xor(res, this.sBox2[c]),
  679. res = this.addMod32(res, this.sBox3[d]);
  680. return res;
  681. },
  682. encipher: function(xL, xR) {
  683. let tmp;
  684. for (var i = 0; i < 16; i++) {
  685. xL = this.xor(xL, this.pArray[i]),
  686. xR = this.xor(this.F(xL), xR),
  687. tmp = xL,
  688. xL = xR,
  689. xR = tmp;
  690. }
  691. tmp = xL,
  692. xL = xR,
  693. xR = tmp,
  694. xR = this.xor(xR, this.pArray[16]),
  695. xL = this.xor(xL, this.pArray[17]);
  696. return [xL, xR];
  697. },
  698. decipher: function(xL, xR) {
  699. let tmp;
  700. xL = this.xor(xL, this.pArray[17]),
  701. xR = this.xor(xR, this.pArray[16]),
  702. tmp = xL,
  703. xL = xR,
  704. xR = tmp;
  705. for (var i = 15; i >= 0; i--) {
  706. tmp = xL,
  707. xL = xR,
  708. xR = tmp,
  709. xR = this.xor(this.F(xL), xR),
  710. xL = this.xor(xL, this.pArray[i]);
  711. }
  712. return [xL, xR];
  713. },
  714. generateSubkeys: function(key) {
  715. var data = 0,
  716. k = 0,
  717. i,
  718. j;
  719. for (i = 0; i < 18; i++) {
  720. for (j = 4; j > 0; j--) {
  721. data = this.fixNegative(data << 8 | key[k]),
  722. k = (k + 1) % key.length;
  723. }
  724. this.pArray[i] = this.xor(this.pArray[i], data),
  725. data = 0;
  726. }
  727. let block64 = [0, 0];
  728. for (i = 0; i < 18; i += 2) {
  729. block64 = this.encipher(block64[0], block64[1]);
  730. this.pArray[i] = block64[0];
  731. this.pArray[i + 1] = block64[1];
  732. }
  733. for (i = 0; i < 256; i += 2) {
  734. block64 = this.encipher(block64[0], block64[1]);
  735. this.sBox0[i] = block64[0];
  736. this.sBox0[i + 1] = block64[1];
  737. }
  738. for (i = 0; i < 256; i += 2) {
  739. block64 = this.encipher(block64[0], block64[1]);
  740. this.sBox1[i] = block64[0];
  741. this.sBox1[i + 1] = block64[1];
  742. }
  743. for (i = 0; i < 256; i += 2) {
  744. block64 = this.encipher(block64[0], block64[1]);
  745. this.sBox2[i] = block64[0];
  746. this.sBox2[i + 1] = block64[1];
  747. }
  748. for (i = 0; i < 256; i += 2) {
  749. block64 = this.encipher(block64[0], block64[1]);
  750. this.sBox3[i] = block64[0];
  751. this.sBox3[i + 1] = block64[1];
  752. }
  753. }
  754. };// End Blowfish.prototype
  755.  
  756. Blowfish.pArray = [0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b],
  757. Blowfish.sBox0 = [0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a],
  758. Blowfish.sBox1 = [0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7],
  759. Blowfish.sBox2 = [0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0],
  760. Blowfish.sBox3 = [0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6];
  761.  
  762. function xhrProgress(e, userdata) {
  763. if (e.lengthComputable) {
  764. let percent = e.loaded * 100.0 / e.total;
  765. postMessage([userdata, `Downloading` + ` ` + Math.floor(percent) + `%`]);
  766. } else {
  767. postMessage([userdata, `Downloading` + ` ` + Math.round(e.loaded * 10.0 / 1024 / 1024) / 10.0 + `M`]);
  768. }
  769. }
  770.  
  771. function xhrComplete(e, key, userdata) {
  772. postMessage([userdata, `Decrypting`]);
  773. let data = new Uint8Array(e.target.response),
  774. L = data.length;
  775. if(WORKER_Debug) { console.log( `Data length`, data.length ); }
  776. for (var i = 0; i < L; i += 6144) {
  777. if (i % (6144 * 20) == 6144 * 19) // let it display state at every 120K
  778. postMessage([userdata, `Decrypting ` + Math.floor(i * 100.0 / L) + '%']);
  779. if (i + 2048 <= L) {
  780. var D = data.slice(i, i + 2048), //data.substr(i, 2048);
  781. bf = new Blowfish(key, 'cbc');
  782. if (WORKER_Debug && (i===0)) { console.log( D.toString(), D.length ); }
  783. bf.decrypt(D, [0, 1, 2, 3, 4, 5, 6, 7]),
  784. data.set(D, i); //for (var j=0; j<2048; j++) data[i+j]=DD.charCodeAt(j);
  785. //dd += DD;
  786. //if (i+6144<L) dd += data.substr(i+2048, 4096);
  787. //else dd += data.substr(i+2048);
  788. } // else { dd += data.substr(i); }
  789. }
  790. let B = new Blob([data], {
  791. type: 'audio/mpeg'
  792. }), //dd
  793. burl = URL.createObjectURL(B);
  794. postMessage([userdata, `DONE`, burl]);
  795. if(WORKER_Debug) { console.log( `Listen at`, burl ); }
  796. }
  797.  
  798. function xhrError(e, userdata) {
  799. postMessage([userdata, `ERROR`]);
  800. }
  801.  
  802. function xhrCancelled(e, userdata) {
  803. postMessage([userdata, `ABORT`]);
  804. }
  805. self.onmessage = function(m) {
  806. if(WORKER_Debug) { console.log( m.origin, m.data ); }
  807. let url = m.data[0],
  808. key = m.data[1],
  809. userdata = m.data[2],
  810. rq = new XMLHttpRequest();
  811. rq.responseType = 'arraybuffer';
  812. rq.addEventListener('progress', function(e) {
  813. xhrProgress(e, userdata);
  814. }); // xhr.onprogress = xhrProgress;
  815. rq.addEventListener('load', function(e) {
  816. xhrComplete(e, key, userdata);
  817. });
  818. rq.addEventListener('error', function(e) {
  819. xhrError(e, userdata);
  820. });
  821. rq.addEventListener('abort', function(e) {
  822. xhrCancelled(e, userdata);
  823. });
  824. rq.open('get', url);
  825. rq.send();
  826. };
  827. });//End var mainWk
  828.  
  829.  
  830. // DOWNLOADER LOGIC: URL ENCRYPTION
  831. const urlCryptor = new aesjs.ModeOfOperation.ecb(aesjs.util.convertStringToBytes('jo6aey6haid2Teih')),
  832. hex2bin = function(h) {
  833. return aesjs.util.convertStringToBytes(h, 'hex');
  834. },
  835. bin2hex = function(b) {
  836. return aesjs.util.convertBytesToString(b, 'hex');
  837. },
  838. bin2str = function(b) {
  839. return b.map(c => String.fromCharCode(c)).join('');
  840. }, //aesjs.util.convertBytesToString(b);
  841. str2bin = function(s) {
  842. return s.split('').map(c => c.charCodeAt(0));
  843. }, //aesjs.util.convertStringToBytes(s));
  844. aesBS = 16,
  845. zeroPad = function(b) {
  846. let l = b.length;
  847. if (l % aesBS !== 0) {
  848. if (typeof(b) === 'string') b += '\0'.repeat(aesBS - (l % aesBS));
  849. else b = b.concat(Array.apply(null, Array(aesBS - (l % aesBS))).map(() => 0));
  850. }
  851. return b;
  852. },
  853. zeroUnpad = (s => s.replace(/\0+$/, '')),
  854. urlsep = '\xa4';
  855.  
  856. function decryptURL(hasTrack, url, raw) {
  857. var i = url.lastIndexOf('/');
  858. if (i >= 0) url = url.substr(i + 1);
  859. let decrypted = zeroUnpad(bin2str(urlCryptor.decrypt(hex2bin(url)))).split(urlsep);
  860. if (raw) return decrypted;
  861. if (hasTrack(decrypted[3])) return getTrack(decrypted[3]);
  862. return {
  863. SNG_ID: decrypted[3],
  864. MD5_ORIGIN: decrypted[1],
  865. MEDIA_VERSION: decrypted[4],
  866. chosen_fmt: decrypted[2],
  867. h: decrypted[0],
  868. e: decrypted[5]
  869. };
  870. }
  871.  
  872. function encryptURL(track, fmt) {
  873. let str = [track.MD5_ORIGIN, fmt, track.SNG_ID, track.MEDIA_VERSION].join(urlsep);
  874. str = zeroPad([hex_md5(str), str, ''].join(urlsep));
  875. return bin2hex(urlCryptor.encrypt(str2bin(str)));
  876. }
  877.  
  878. // DOWNLOADER LOGIC: GLOBAL VARIABLES AND HELPER METHODS
  879. const fmtMisc = 0, fmtLow = 10, fmtLow32 = 11, fmtMed = 1, fmtHQ = 3, fmtFLAC = 9,
  880. flagTitle = 1, flagArt = 3, flagHash = 16, flagVer = 32, flagFmt = 64;
  881.  
  882. function generateName(track, fmt, flags) {
  883. var name = ''; //Replace track.SNG_ID with ''
  884. if ((flags & flagHash) == flagHash && (name += `[` + track.SNG_ID + `_` + track.MD5_ORIGIN + `] `),
  885. (flags & flagVer) == flagVer && (name += `[` + track.SNG_ID + `_v` + track.MEDIA_VERSION + `] `),
  886. (flags & flagArt) == flagArt && (name += track.ART_NAME.toUpperCase() + ` - `), //Having .toUpperCase() for generate Name > NAME, and invert place with '-'
  887. (flags & flagTitle) == flagTitle && (name += ` ` + track.SNG_TITLE, track.VERSION && '' !== track.VERSION && (name += ` ` + track.VERSION)), fmt == fmtFLAC) name += '.flac';
  888. else if ((flags & flagFmt) == flagFmt) {
  889. switch (fmt) {
  890. case fmtMisc:
  891. name += '.default';
  892. break;
  893. case fmtLow:
  894. name += '.lq';
  895. break;
  896. case fmtLow32:
  897. name += '.32';
  898. break;
  899. case fmtMed:
  900. name += '.sq';
  901. break;
  902. case fmtHQ:
  903. name += '.hq';
  904. break;
  905. }
  906. name += '.mp3';
  907. } else if (fmt >= 0) name += '.mp3';
  908. return name;
  909. }
  910.  
  911. let bfGK = 'g4el58wc0zvf9na1';
  912.  
  913. function bfGenKey2(h1, h2) {
  914. let l = h1.length,
  915. s = [];
  916. for (var i = 0; i < l; i++) s.push(bfGK.charCodeAt(i) ^ h1.charCodeAt(i) ^ h2.charCodeAt(i));
  917. return s;
  918. }
  919.  
  920. function bfGenKey(id, format) {
  921. let h = hex_md5(id + ''),
  922. h1 = h.substr(0, 16),
  923. h2 = h.substr(16, 16),
  924. k = bfGenKey2(h1, h2);
  925. if (!format) return k;
  926. return k.map(format == 'hex' ? (a => (a + 256).toString(16).substr(-2)) : (a => String.fromCharCode(a))).join('');
  927. }
  928.  
  929. const trackDB = {},
  930. urlDB = {};
  931.  
  932. function hasTrack(id) {
  933. return trackDB.hasOwnProperty(id + '');
  934. }
  935.  
  936. function getTrack(id) {
  937. return hasTrack(id) ? trackDB[id + ''] : null;
  938. }
  939.  
  940. function hasDownloaded(id, fmt) {
  941. if (!urlDB.hasOwnProperty(id + '')) return false;
  942. let urls = urlDB[id + ''];
  943. return urls.hasOwnProperty(fmt);
  944. }
  945.  
  946. function getDownloaded(id, fmt) {
  947. return hasDownloaded(id, fmt) ? urlDB[id + ''][fmt] : null;
  948. }
  949.  
  950. function FileConvertSize(aSize) {
  951. aSize = Math.abs(parseInt(aSize, 10));
  952. const def = [
  953. [1, 'octets'],
  954. [1024, 'ko'],
  955. [1024 * 1024, 'Mo'],
  956. [1024 * 1024 * 1024, 'Go'],
  957. [1024 * 1024 * 1024 * 1024, 'To']
  958. ];
  959. for (var i = 0; i < def.length; i++) {
  960. if (aSize < def[i][0]) return (aSize / def[i - 1][
  961. 0
  962. ]).toFixed(2) + ' ' + def[i - 1]
  963. [1];
  964. }
  965. }
  966.  
  967. // DOWNLOAD ENTRY POINT
  968. function dzDownload(obj, userdata, fmt, size) {
  969. const msg = [
  970. 'https://e-cdns-proxy-' + obj.MD5_ORIGIN.charAt(0) + '.dzcdn.net' +
  971. '/mobile/1/' + encryptURL(obj, fmt),
  972. bfGenKey(obj.SNG_ID),
  973. userdata + ',' + fmt
  974. ];
  975. if(GM_Debug) { gm_win.console.log(msg); }
  976. mainWk.postMessage(msg);
  977. }
  978.  
  979. // DOWNLOADER WORKER CALLBACK
  980. mainWk.onmessage = function(msg) {
  981. let userdata = msg.data[0].split(','),
  982. elId = userdata[0],
  983. fmt = userdata[1],
  984. trackEl = D.querySelector('#' + elId);
  985. if (trackEl === null) return;
  986. let trackId = trackEl.dataset.trackId,
  987. state = msg.data[1];
  988. if (state == 'DONE') {
  989. if (!hasTrack(trackId)) {
  990. gm_win.console.error( `On download: MISSING TRACK INFO!`, trackId ),//Required (not only in debug mode)
  991. trackEl.querySelector('.status').style.display = 'none',
  992. trackEl.querySelector('.links').style.display = 'block';
  993. return;
  994. }
  995. if (!urlDB[trackId + '']) urlDB[trackId + ''] = {};
  996. urlDB[trackId + ''][fmt] = msg.data[2];
  997. let links = trackEl.querySelectorAll('a.dl');
  998. for (var i = 0; i < links.length; i++) {
  999. if (links[i].dataset.fmt == fmt) {
  1000. links[i].download = generateName(getTrack(trackId), fmt, W.dzDL.dlFlags),
  1001. links[i].href = msg.data[2],
  1002. W.setTimeout((function(l) {
  1003. return function() {
  1004. l.click();
  1005. };
  1006. })(links[i]), 10);
  1007. break;
  1008. }
  1009. }
  1010. } else if (state == 'ABORT') {
  1011. gm_win.console.error( `Download abort:`, trackId, fmt );//Required (not only in debug mode)
  1012. } else if (state == 'ERROR') {
  1013. gm_win.console.error( `Download ERROR!`, trackId, fmt );//Required (not only in debug mode)
  1014. } else {
  1015. trackEl.querySelector('.status').innerHTML = state;
  1016. return;
  1017. }
  1018. trackEl.querySelector('.status').style.display = 'none',
  1019. trackEl.querySelector('.links').style.display = 'block';
  1020. };
  1021.  
  1022. // DOWNLOADER LOGIC: HTML GENERATOR
  1023. const rootEl = D.createElement('div');
  1024.  
  1025. function getFilesize(track, fmt_name) {
  1026. if (!track.hasOwnProperty('FILESIZE_' + fmt_name)) return 0;
  1027. if (track['FILESIZE_' + fmt_name] === '' || track['FILESIZE_' + fmt_name] === 0) return 0;
  1028. let size = parseInt(track['FILESIZE_' + fmt_name]);
  1029. return isNaN(size) ? 0 : size;
  1030. }
  1031.  
  1032. function generateDl(track, fmt_name, fmt_id, size) {
  1033. let msgSize = /*'size: ' +*/ FileConvertSize(size), //Use FileConvertSize fn() to have size 'human readable'
  1034. el = D.createElement('a');
  1035. //el.href = '#',
  1036. el.className = 'dl',
  1037. el.dataset.fmt = fmt_id,
  1038. el.dataset.filesize = size,
  1039. //el.dataset.trackUrl = '',
  1040. el.innerHTML = fmt_name,
  1041. el.title = msgSize,
  1042. el.onclick = function() {
  1043. let trackEl = this.parentElement.parentElement; // a <- links <- trackdl
  1044. if (hasDownloaded(trackEl.dataset.trackId, this.dataset.fmt)) //(this.dataset.trackUrl != "")
  1045. return true; // we already have a link. href should also be set.
  1046. else {
  1047. //this.href = '#';
  1048. this.removeAttribute('download');
  1049. }
  1050. if (!hasTrack(trackEl.dataset.trackId)) {
  1051. gm_win.console.warn(`NO INFORMATION ABOUT TRACK!`, trackEl.dataset.trackId);//Required (not only in debug mode)
  1052. return false;
  1053. }
  1054. let track = trackDB[trackEl.dataset.trackId + ''];
  1055. trackEl.querySelector('.links').style.display = 'none',
  1056. trackEl.querySelector('.status').style.display = 'inline-block',
  1057. dzDownload(track, trackEl.id, this.dataset.fmt, this.dataset.filesize);
  1058. return false;
  1059. };
  1060. return el;
  1061. }
  1062.  
  1063. function generateTrackDiv(track, index) {
  1064. trackDB[track.SNG_ID + ''] = track;
  1065. let trackEl = D.createElement('div');
  1066. trackEl.className = 'trackdl',
  1067. trackEl.id = 'trackdl' + Math.floor(Math.random() * 900000 + 100000),
  1068. trackEl.dataset.trackId = track.SNG_ID;
  1069. //trackEl.dataset.trackUrl = '';
  1070. let nameEl = D.createElement('span');
  1071. nameEl.className = 'name',
  1072. nameEl.innerHTML = ( index ? `⅊ ` + index + ` ▫ ` : `` ) + generateName( track, 0, flagTitle | flagArt ); // don't add extension here
  1073. let linksEl = D.createElement('div');
  1074. linksEl.className = 'links',
  1075. linksEl.innerHTML = '';
  1076. let statusEl = D.createElement('div');
  1077. statusEl.className = 'status',
  1078. statusEl.style.display = 'none',
  1079. statusEl.innerHTML = 'Waiting for download...';
  1080. var miscFilesize = 0;
  1081. if (track.hasOwnProperty('FILESIZE') && track.FILESIZE !== 0 && track.FILESIZE !== '') miscFilesize = parseInt(track.FILESIZE);
  1082. else miscFilesize = getFilesize(track, 'MISC');
  1083. //if (miscFilesize > 0) linksEl.appendChild(generateDl(track, 'default ', fmtMisc, miscFilesize));//REMOVE: redundancy
  1084. if (getFilesize(track, 'MP3_32') > 0) linksEl.appendChild(generateDl(track, 'low (32)', fmtLow32, getFilesize(track, 'MP3_32')));
  1085. if (getFilesize(track, 'MP3_64') > 0) linksEl.appendChild(generateDl(track, 'low (64)', fmtLow, getFilesize(track, 'MP3_64')));
  1086. if (getFilesize(track, 'MP3_128') > 0) linksEl.appendChild(generateDl(track, 'standard', fmtMed, getFilesize(track, 'MP3_128')));
  1087. if (getFilesize(track, 'MP3_320') > 0) linksEl.appendChild(generateDl(track, 'hq', fmtHQ, getFilesize(track, 'MP3_320')));
  1088. if (getFilesize(track, 'FLAC') > 0) linksEl.appendChild(generateDl(track, 'flac', fmtFLAC, getFilesize(track, 'FLAC')));
  1089. trackEl.appendChild(nameEl),
  1090. trackEl.appendChild(linksEl),
  1091. trackEl.appendChild(statusEl),
  1092. trackEl.appendChild((function() {
  1093. var el = D.createElement('div');
  1094. el.className = 'endfloat';
  1095. return el;
  1096. })());
  1097. return trackEl;
  1098. }
  1099.  
  1100. // ENTRY POINTS
  1101. function deleteElement(id) {
  1102. var trackEl = id;
  1103. if (typeof(id) === 'string') trackEl = D.querySelector('#' + id);
  1104. if (hasTrack(trackEl.dataset.trackId)) {
  1105. let urls = urlDB[trackEl.dataset.trackId + ''];
  1106. for (var fmt in urls)
  1107. if (urls.hasOwnProperty(fmt)) {
  1108. W.URL.revokeObjectURL(urls[fmt]);
  1109. }
  1110. }
  1111. trackEl.parentElement.removeChild(trackEl);
  1112. }
  1113.  
  1114. function addTrack(track) {
  1115. let el = generateTrackDiv(track),
  1116. delEl = D.createElement('a');
  1117. delEl.href = '#',
  1118. delEl.className = 'deltrack',
  1119. delEl.onclick = function() {
  1120. W.dzDL.deleteCustom(this.parentElement);
  1121. },
  1122. delEl.innerHTML = '[X]',
  1123. el.insertBefore(el.children[0], delEl),
  1124. D.querySelector('#dlcustomtracks').appendChild(el); //divCustomTracks
  1125. return el.id; //el
  1126. }
  1127.  
  1128. let divCurrentTrack = null,
  1129. divTracklist = D.createElement('div');
  1130.  
  1131. function refreshTracklist() {
  1132. // Clean up EVERYTHING
  1133. if (divCurrentTrack !== null) {
  1134. deleteElement(divCurrentTrack),
  1135. divCurrentTrack = null;
  1136. }
  1137. var tracks = divTracklist.querySelectorAll('.trackdl');
  1138. for (var i = 0; i < tracks.length; i++) deleteElement(tracks[i]);
  1139. //rootEl.appendChild(elH1);
  1140. if (W.dzPlayer) {
  1141. var sng = W.dzPlayer.getCurrentSong();
  1142. if (sng && sng.SNG_ID) {
  1143. divCurrentTrack = generateTrackDiv(sng),
  1144. rootEl //.appendChild
  1145. .insertBefore(divCurrentTrack, elH2);
  1146. }
  1147. }
  1148. //rootEl.appendChild(elH2);
  1149. if (W.dzPlayer) {
  1150. var list = W.dzPlayer.getTrackList(),
  1151. current = W.dzPlayer.getTrackListIndex();
  1152. if (list) {
  1153. var digits = list.length.toString().length;
  1154. for (var c = 0; c < list.length; c++) {
  1155. var trackDiv = generateTrackDiv(list[c], '0'.repeat(digits - (c + 1).toString().length) + (c + 1));
  1156. if (c == current) trackDiv.classList.add('current');
  1157. divTracklist.appendChild(trackDiv);
  1158. }
  1159. }
  1160. }
  1161. return false;
  1162. }
  1163.  
  1164. // DATA STYLESHEET.
  1165. function _dzAppCss_(newStyle) {
  1166. var styleElement = D.getElementById('dzApp_styles_js'),
  1167. appCss =
  1168. '.dltitle { height: 100%; position: relative; display: inline-block; font-weight: bolder; padding-top: 3px; padding-bottom: 3px; color: #000000; margin-left: 7px }' +
  1169. '#dzdownloader { position: fixed; top: 0; z-index: 500; background: #cccccc; padding: 5px; max-height: 90%; overflow-y: auto; min-width:25%; max-width:28% }' +
  1170. '#dzdltrigger { position: fixed; left: 0; top: 0; z-index: 501; background: #cccccc; padding: 3px; text-align: right }' +
  1171. '.trackdl .status { float: right } .trackdl .links { float: right } .trackdl:nth-child(even) { background: #dddddd }' +
  1172. '.trackdl a.dl { margin-left: 4px } .trackdl a.dl[download]:before { content: ""; text-decoration: none }' + // Some others 'improvements'
  1173. '#page_sidebar { z-index: 2; top: 20px !important }' + // Fix Deezer's sidebar height
  1174. '.trackdl a:before { margin-right: 5px; content: "⇪"; text-decoration: none } .trackdl.current { background: #808284; color: #e8eaed; font-weight: lighter } .dl,#dzdltrigger { letter-spacing: -1px } .dltitle:first-letter { font-size: 1.2em }' +
  1175. '.dl[download]:not(after) { text-decoration: line-through } #dlrefresh { position: relative; top: 5px; margin-left: 25px } .dltitle,.trackdl { margin-top: .12em } .dl { font-size: 1.02em }' +
  1176. '.deltrack { margin-right: 3px } #dzdownloader .endfloat { clear: both } .trackdl.current a.dl { color: #e8eaed } #dzdltrigger,#dzdownloader { font-size: 1.05em }' +
  1177. '.trackdl.current a.dl[download] { color: #595959 } #dzdltrigger:hover { cursor: pointer } select { position: relative; top: 5px; text-align: center; margin-left: 25px }' +
  1178. '.dl:after,.dl[download]:after { position: relative; display: inline-block; content: " "; text-decoration: none!important }' +
  1179. '.dllinkstitle { position: relative /*after-element(.dltitle:nth-of-type(1))*/; max-height: 100%; font-weight: bolder; color: #000000; display: block; right: 22px; text-align: right }' +
  1180. '.dltitle:nth-of-type(1) { top: 20px } .links:before { content: "☞" } ';
  1181. if ( !styleElement ) {
  1182. styleElement = D.createElement('style'),
  1183. styleElement.type = 'text/css',
  1184. styleElement.innerHTML = appCss,
  1185. styleElement.id = 'dzApp_styles_js',
  1186. D.getElementsByTagName('head')[0].appendChild(styleElement);
  1187. }
  1188. styleElement.appendChild(D.createTextNode(newStyle));
  1189. } //End _dzAppCss_ fn
  1190.  
  1191. // THE DOWNLOADER PANEL
  1192. rootEl.style.left = '220px',
  1193. rootEl.id = 'dzdownloader',
  1194. rootEl.className = 'dzdl';
  1195.  
  1196. var elCombo = D.createElement('select');var IDtxt="[ID_hash_v] ";
  1197. elCombo.title = translate(`Choose the file name`),
  1198. elCombo.innerHTML =
  1199. //ADDING translation + REMOVE redundancy
  1200. '<option value="' + (flagTitle) + '" selected>' + `⋯ ` + translate(`Choose`) + ` ⋯` + '</option>' + //Having 'selected' here
  1201. '<option value="' + (flagTitle) + '">' + translate(`Title`) + '</option>' +
  1202. '<option value="' + (flagTitle | flagArt) + '">' + translate(`Artist`).toUpperCase() + ` - ` + translate(`Title`) + '</option>' +
  1203. '<option value="' + (flagVer | flagTitle) + '">' + `[ID_v] ` + translate(`Title`) + '</option>' +
  1204. '<option value="' + (flagVer | flagArt) + '">' + `[ID_v] ` + translate(`Artist`).toUpperCase() + ` - ` + translate(`Title`) + '</option>' +
  1205. '<option value="' + (flagHash | flagVer | flagTitle) + '">' + `[ID_hash_v] ` + translate(`Title`) + '</option>' +
  1206. '<option value="' + (flagHash | flagVer | flagArt) + '">' + `[ID_hash_v] ` + translate(`Artist`).toUpperCase() + ` - ` + translate(`Title`) + '</option>',
  1207. elCombo.onclick = function() {
  1208. W.dzDL.dlFlags = parseInt(this.value);
  1209. };
  1210.  
  1211. var elRefresh = D.createElement('a');
  1212. elRefresh.id = 'dlrefresh', elRefresh.href = '', elRefresh.innerHTML = `⟳ ` + translate(`refresh track list`),
  1213. elRefresh.onclick = function() {
  1214. if(GM_Debug) { gm_win.console.info( `Refreshing the Track List.` ); }
  1215. W.dzDL.refreshPlayer();
  1216. return false;
  1217. };
  1218.  
  1219. const elH1 = D.createElement('p'), elH1b = D.createElement('p');
  1220. elH1.className = 'dltitle', elH1.innerHTML = `⇰ ` + translate(`Current track`);
  1221. elH1b.className = 'dllinkstitle', elH1b.innerHTML = translate(`Available links`);
  1222.  
  1223. var elH2 = D.createElement('p');
  1224. elH2.className = 'dltitle', elH2.innerHTML = `⛁ ` + translate(`Track list`);
  1225.  
  1226. var elH3 = D.createElement('p');
  1227. //elH3.className = 'dltitle', elH3.innerHTML = `⛂ User-added tracks`;
  1228.  
  1229. divTracklist.id = 'dltracklist',
  1230. divTracklist.className = 'dllist';
  1231.  
  1232. var divCustomTracks = D.createElement('div');
  1233. divCustomTracks.id = 'dlcustomtracks',
  1234. divCustomTracks.className = 'dllist',
  1235. rootEl.style.display = 'none',
  1236. rootEl.appendChild(elCombo),
  1237. rootEl.appendChild(elRefresh),
  1238. rootEl.appendChild(elH1),
  1239. rootEl.appendChild(elH1b),
  1240. rootEl.appendChild(elH2),
  1241. rootEl.appendChild(divTracklist),
  1242. //rootEl.appendChild(elH3),
  1243. rootEl.appendChild(divCustomTracks);
  1244.  
  1245. refreshTracklist();
  1246. W.setTimeout(refreshTracklist, 1000);
  1247.  
  1248. var triggerEl = D.createElement('div');
  1249. triggerEl.style.width = '214px', //padding is 3 => 220 total
  1250. triggerEl.id = 'dzdltrigger',
  1251. triggerEl.title = translate(`Click to open the app`),
  1252. triggerEl.innerHTML = 'D➲wnloader ☰',
  1253. triggerEl.onclick = function() {
  1254. var el = D.querySelector('#dzdownloader'); //rootEl
  1255. el.style.display=(el.style.display!=='block')? 'none' : 'block';
  1256. if (el.style && el.style.display) {
  1257. if (el.style.display == 'none') {
  1258. el.style.display = 'block',
  1259. this.innerHTML = 'D➲wnloader ⚟';
  1260. } else {
  1261. el.style.display = 'none',
  1262. this.innerHTML = 'D➲wnloader ☰';
  1263. }
  1264. }
  1265. refreshTracklist();
  1266. };
  1267.  
  1268. D.body.appendChild(triggerEl), D.body.appendChild(rootEl);
  1269.  
  1270. _dzAppCss_(); // Applying StyleSheet to the app.
  1271.  
  1272. //ESCAPING DOWNLOADER WINDOW POPUP
  1273. W.onload = D.addEventListener('keydown', e => {
  1274. if (e.which === 27) { // escape being used
  1275. if(GM_Debug) { gm_win.console.info( `key with code ` + e.which + ` (escape) was pressed. We quit the app.` ); }
  1276. var node = D.querySelector('#dzdownloader'); //rootEl
  1277. node.style.display=(node.style.display=='block')? 'none' : 'block';
  1278. if (node.style && node.style.display) {
  1279. node.style.display = 'none', triggerEl.innerHTML = 'D➲wnloader ☰';
  1280. }
  1281. e.preventDefault();
  1282. }
  1283. else { // Other keycode pressed. May be useful.
  1284. if(GM_Debug) { gm_win.console.info( `key with code ` + e.which + ` was pressed.` ); }
  1285. }
  1286. },0);
  1287.  
  1288. // https://blog.sessionstack.com/how-to-track-changes-in-the-dom-using-mutation-observer-bafdac65bca5
  1289. // console.log(`content has changed`);
  1290. // W.dzPlayer.getTrackList();refreshTracklist();
  1291.  
  1292. const dzDL = {
  1293. DEFAULT: fmtMisc, MP3_64: fmtLow, MP3_32: fmtLow32, MP3: fmtMed, MP3_128: fmtMed, MP3_320: fmtHQ, HQ: fmtHQ, FLAC: fmtFLAC, LOSSLESS: fmtFLAC,
  1294. WITH_TITLE: flagTitle, WITH_ART: flagArt, WITH_HASH: flagHash, WITH_VER: flagVer, WITH_FMT: flagFmt, NAME_DEFAULT: flagTitle | flagArt, NAME_DB: flagHash | flagVer,
  1295. // CONFIGURATION
  1296. dlFlags: flagHash | flagVer | flagTitle,
  1297. // MISCELLANEOUS FUNCTIONS
  1298. hasTrackInDB: hasTrack, getTrackFromDB: getTrack, hasDownloadedTrack: hasDownloaded, getDownloadedTrack: getDownloaded, generateFileName: generateName,
  1299. // ENTRY POINTS
  1300. getFromURL: decryptURL, makeURL: encryptURL, download: dzDownload, deleteCustom: deleteElement, addCustom: addTrack, refreshPlayer: refreshTracklist
  1301. };
  1302.  
  1303. W.dzDL = dzDL; if (!W.dzHAX) W.dzHAX = {}, W.dzHAX.dL = dzDL;
  1304.  
  1305. } //END _dzApp_ fn
  1306.  
  1307. function _dzLogoCss_(newStyle) {
  1308. var styleElement = D.getElementById('dzLogo_styles_js'),
  1309. LogoCss =
  1310. '.logo-deezer{ height: 55px }' +
  1311. '.index-header-logo { width: 227px; max-width: 100%; height: 65px }' +
  1312. '.logo-deezer.logo,.logo-deezer-hp.logo.index-header-logo { \
  1313. background-position: center; background-repeat: no-repeat; background-size: contain; \
  1314. background-image:url("https://framapic.org/5RWAy9Zz6ekd/X7hftDFqC9MY.png") \
  1315. }' +
  1316. '.nav-main.nav { margin-top: 8px!important } #menu_search { z-index: 2 } #menu_navigation { top: 135px }';
  1317. if (!styleElement) {
  1318. styleElement = D.createElement('style'),
  1319. styleElement.type = 'text/css',
  1320. styleElement.innerHTML = LogoCss,
  1321. styleElement.id = 'dzLogo_styles_js',
  1322. D.getElementsByTagName('head')[0].appendChild(styleElement);
  1323. }
  1324. styleElement.appendChild(D.createTextNode(newStyle));
  1325. } //End _dzLogoCss_ fn
  1326.  
  1327. function _gmAlertCss_(newStyle) {
  1328. var styleElement = D.getElementById('GM_styles_js'),
  1329. FIXME = `( ℹ ) Greasemonkey :     Works, but you needs to reclick a second time on the link (after the conversion is done).`, //see document.write
  1330. gmCss =
  1331. '#dlrefresh:after { \
  1332. content: " (" attr(FIXME) ")"; \
  1333. font-size: .7em; top: .8em; right: 0; text-align: left; position: absolute; ligne-height: .82em; \
  1334. letter-spacing: -0.5px; display: table; width: 30% \
  1335. }';
  1336. if (!styleElement) {
  1337. styleElement = D.createElement('style'),
  1338. styleElement.type = 'text/css',
  1339. styleElement.innerHTML = gmCss,
  1340. styleElement.id = 'GM_styles_js',
  1341. styleElement.title = FIXME,
  1342. D.getElementsByTagName('head')[0].appendChild(styleElement);
  1343. }
  1344. styleElement.appendChild(D.createTextNode(newStyle));
  1345. } //End _gmAlertCss_ fn
  1346.  
  1347.  
  1348. var Player = D.getElementsByTagName('dzPlayer');
  1349. if (dzPlayer && dzPlayer.isPlaying == false) dzPlayer.play;
  1350.  
  1351.  
  1352.  
  1353. })(window, 'script', 'css');
  1354.  
RAW Paste Data