VGMLoaderX

Automatically downloads albums from KHInsider without an account.

  1. // ==UserScript==
  2. // @name VGMLoaderX
  3. // @name:de VGMLoaderX
  4. // @name:en VGMLoaderX
  5. // @namespace sun/userscripts
  6. // @version 1.0.26
  7. // @description Automatically downloads albums from KHInsider without an account.
  8. // @description:de Lädt Alben von KHInsider automatisch und ohne Account herunter.
  9. // @description:en Automatically downloads albums from KHInsider without an account.
  10. // @compatible chrome
  11. // @compatible edge
  12. // @compatible firefox
  13. // @compatible opera
  14. // @compatible safari
  15. // @homepageURL https://forgejo.sny.sh/sun/userscripts
  16. // @supportURL https://forgejo.sny.sh/sun/userscripts/issues
  17. // @contributionURL https://liberapay.com/sun
  18. // @contributionAmount €1.00
  19. // @author Sunny <sunny@sny.sh>
  20. // @include https://downloads.khinsider.com/game-soundtracks/album/*
  21. // @match https://downloads.khinsider.com/game-soundtracks/album/*
  22. // @connect vgmdownloads.com
  23. // @connect vgmsite.com
  24. // @connect vgmtreasurechest.com
  25. // @run-at document-end
  26. // @inject-into page
  27. // @grant GM.xmlHttpRequest
  28. // @grant GM_xmlhttpRequest
  29. // @noframes
  30. // @require https://unpkg.com/@zip.js/zip.js/dist/zip.js
  31. // @require https://unpkg.com/file-saver
  32. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
  33. // @icon https://forgejo.sny.sh/sun/userscripts/raw/branch/main/icons/VGMLoaderX.ico
  34. // @copyright 2021-present, Sunny (https://sny.sh/)
  35. // @license Hippocratic License; https://forgejo.sny.sh/sun/userscripts/src/branch/main/LICENSE.md
  36. // ==/UserScript==
  37.  
  38. (() => {
  39. for (const x of document.querySelectorAll('a[href^="/cp/add_album/"]')) {
  40. x.addEventListener("click", (e) => {
  41. e.preventDefault();
  42.  
  43. let format = Array(
  44. ...document.querySelectorAll("#songlist_header th[align=right]"),
  45. ).map((x) => x.textContent);
  46. if (format.length === 1) {
  47. format = format[0];
  48. } else {
  49. const input = prompt(
  50. `Please enter your desired format (one of ${format.join(", ")}):`,
  51. format[0],
  52. );
  53.  
  54. if (!input) return;
  55. if (!format.includes(input.toUpperCase())) {
  56. format = format[0];
  57. alert(`Invalid format supplied. Using ${format} instead.`);
  58. } else {
  59. format = input;
  60. }
  61. }
  62.  
  63. const element = document.getElementsByClassName("albumMassDownload")[0];
  64. element.style.height = "auto";
  65. element.style.marginBottom = "2em";
  66.  
  67. const input = eval(
  68. document
  69. .querySelector("#pageContent script")
  70. .textContent.slice(5, -3)
  71. .replace("function", "function x")
  72. .replace("return p}", "return p}x"),
  73. );
  74.  
  75. const mediaPath = input.match(/mediaPath='(.+?)'/)[1];
  76. const tracks = JSON.parse(
  77. input.match(/tracks=(\[.+?,\])/)[1].replace(",]", "]"),
  78. );
  79. const output = tracks.map(
  80. (x) =>
  81. `${
  82. mediaPath + x.file.split(".").slice(0, -1).join(".")
  83. }.${format.toLowerCase()}`,
  84. );
  85. const names = tracks.map((x) => x.name);
  86.  
  87. const blobWriter = new zip.BlobWriter("application/zip");
  88. const writer = new zip.ZipWriter(blobWriter);
  89.  
  90. function forSync(i) {
  91. element.innerHTML = `Downloading track ${i + 1} of ${output.length} (${names[i] || `Track ${i + 1}`})...`;
  92. GM.xmlHttpRequest({
  93. method: "GET",
  94. url: output[i],
  95. responseType: "blob",
  96. onload: async (response) => {
  97. await writer.add(
  98. decodeURIComponent(output[i].split("/").pop()),
  99. new zip.BlobReader(response.response),
  100. );
  101.  
  102. if (output[i + 1]) {
  103. forSync(i + 1);
  104. } else {
  105. await writer.close();
  106. const blob = await blobWriter.getData();
  107. saveAs(
  108. blob,
  109. `${document.getElementsByTagName("h2")[0].textContent}.zip`,
  110. );
  111. element.innerHTML =
  112. "Album successfully downloaded. ZIP file has been passed to the browser.";
  113. }
  114. },
  115. });
  116. }
  117. forSync(0);
  118. });
  119. }
  120. })();