Toggle Features

Enable or disable features which may or may not be experimental/web version only.

  1. // ==UserScript==
  2. // @name Toggle Features
  3. // @description Enable or disable features which may or may not be experimental/web version only.
  4. // @author bertigert
  5. // @version 1.0.2
  6. // @icon https://www.google.com/s2/favicons?sz=64&domain=deezer.com
  7. // @namespace Violentmonkey Scripts
  8. // @match https://www.deezer.com/*
  9. // @grant none
  10. // @run-at document-start
  11. // ==/UserScript==
  12.  
  13.  
  14. (function() {
  15. "use strict";
  16. // ======= Settings START =======
  17.  
  18. const LOG_ALL_FEATURES_DEBUG = true; // useful to see all features (gets logged in the (dev tools) console, use https://github.com/bertigert/DeezMod/blob/main/plugins/enable_dev_mode.js to view)
  19.  
  20. const DEEZER_CUSTOM_FEATURES = {
  21. // gapless_playback: true,
  22. deeztools: true, // simple way to toggle most of the custom features
  23. }
  24.  
  25. const SPECIAL_FEATURES = {
  26. spoof_family: false, // Spoof your account to be the head of a family plan if you are a child account of a family account, opening up more features for you. (e.g. linking to last.fm)
  27. }
  28.  
  29. // ======= Settings END =======
  30.  
  31.  
  32.  
  33. function log(...args) {
  34. console.log("[Toggle Features]", ...args);
  35. }
  36. function error(...args) {
  37. console.error("[Toggle Features]", ...args);
  38. }
  39. function debug(...args) {
  40. console.debug("[Toggle Features]", ...args);
  41. }
  42.  
  43. const original_fetch = window.fetch;
  44.  
  45. log("Hooking fetch");
  46. window.fetch = async function (...args) {
  47. try {
  48. const url = new URL(args[0]);
  49.  
  50. if (url.pathname !== "/ajax/gw-light.php" ||
  51. url.searchParams.get("method") !== "deezer.getUserData" ||
  52. url.searchParams.get("api_token") !== "" ||
  53. !url.searchParams.has("cid") ||
  54. typeof args[1].body !== "string"
  55. ) {
  56. return original_fetch.apply(window, args);
  57. }
  58.  
  59. debug('Catched user data fetch call');
  60.  
  61. const response = await original_fetch.apply(window, args);
  62. const resp_json = await response.json();
  63.  
  64. if (resp_json.results) {
  65. // Special features
  66. if (SPECIAL_FEATURES.spoof_family) {
  67. resp_json.results.USER.MULTI_ACCOUNT = {"ENABLED": true,"ACTIVE": true,"CHILD_COUNT": 0,"MAX_CHILDREN": 0,"PARENT": null,"IS_KID": false,"IS_SUB_ACCOUNT": false}
  68. }
  69.  
  70.  
  71. // Deezer custom features
  72. const features = resp_json.results.__DZR_GATEKEEPS__;
  73.  
  74. if (LOG_ALL_FEATURES_DEBUG) {
  75. log('All Features:', features, "Special Features:", SPECIAL_FEATURES);
  76. }
  77.  
  78. for (let feature of Object.entries(DEEZER_CUSTOM_FEATURES)) {
  79. features[feature[0]] = feature[1];
  80. log(feature[1] ? 'Enabled' : 'Disabled', feature[0]);
  81. }
  82. }
  83.  
  84. // since this request is only made once, we can unhook now
  85. log("Unhooking fetch");
  86. window.fetch = original_fetch;
  87.  
  88. return new Response(JSON.stringify(resp_json), {
  89. status: response.status,
  90. statusText: response.statusText,
  91. headers: response.headers,
  92. });
  93. } catch (e) {
  94. error("Error in fetch hook:", e);
  95. return original_fetch.apply(window, args);
  96. }
  97. }
  98. })();