VGMLoaderX

Automatically downloads albums from KHInsider without an account.

目前为 2022-10-28 提交的版本。查看 最新版本

  1. // @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT
  2. /* eslint-env browser, greasemonkey */
  3. /* jshint asi: true, esversion: 11 */
  4. /* globals zip, saveAs */
  5.  
  6. // ==UserScript==
  7. // @name VGMLoaderX
  8. // @name:de VGMLoaderX
  9. // @name:en VGMLoaderX
  10. // @namespace sun/userscripts
  11. // @version 1.0.13
  12. // @description Automatically downloads albums from KHInsider without an account.
  13. // @description:de Lädt Alben von KHInsider automatisch und ohne Account herunter.
  14. // @description:en Automatically downloads albums from KHInsider without an account.
  15. // @compatible chrome
  16. // @compatible edge
  17. // @compatible firefox
  18. // @compatible opera
  19. // @compatible safari
  20. // @homepageURL https://codeberg.org/sun/userscripts
  21. // @supportURL https://codeberg.org/sun/userscripts/issues/new
  22. // @contributionURL https://ko-fi.com/rcrsch
  23. // @contributionAmount €1.00
  24. // @author Sunny <roesch.eric+userscripts@protonmail.com>
  25. // @include https://downloads.khinsider.com/game-soundtracks/album/*
  26. // @match https://downloads.khinsider.com/game-soundtracks/album/*
  27. // @connect vgmsite.com
  28. // @run-at document-end
  29. // @inject-into auto
  30. // @grant GM.xmlHttpRequest
  31. // @grant GM_xmlhttpRequest
  32. // @noframes
  33. // @require https://cdn.jsdelivr.net/gh/gildas-lormeau/zip.js@0595a47d24598c6cbb32f7ce9bb2974cc82af2da/dist/zip.min.js
  34. // @require https://cdn.jsdelivr.net/gh/eligrey/FileSaver.js@5bb701bd6ea05a02836daf8ef88ec350a1dd4d83/dist/FileSaver.min.js
  35. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
  36. // @icon https://codeberg.org/sun/userscripts/raw/branch/main/icons/VGMLoaderX.ico
  37. // @copyright 2021-2022, Sunny
  38. // @license MIT; https://codeberg.org/sun/userscripts/src/branch/main/LICENSE
  39. // ==/UserScript==
  40.  
  41. // ==OpenUserJS==
  42. // @author TheLastZombie
  43. // ==/OpenUserJS==
  44.  
  45. (function () {
  46. "use strict";
  47.  
  48. document.querySelectorAll('a[href^="/cp/add_album/"]').forEach((x) => {
  49. x.addEventListener("click", (e) => {
  50. e.preventDefault();
  51.  
  52. let format = Array(
  53. ...document.querySelectorAll("#songlist_header th[align=right]")
  54. ).map((x) => x.textContent);
  55. if (format.length === 1) {
  56. format = format[0];
  57. } else {
  58. const input = prompt(
  59. "Please enter your desired format (one of " +
  60. format.join(", ") +
  61. "):",
  62. format[0]
  63. );
  64.  
  65. if (!input) return;
  66. if (!format.includes(input.toUpperCase())) {
  67. format = format[0];
  68. alert("Invalid format supplied. Using " + format + " instead.");
  69. } else {
  70. format = input;
  71. }
  72. }
  73.  
  74. const element = document.getElementsByClassName("albumMassDownload")[0];
  75. element.style.height = "auto";
  76. element.style.marginBottom = "2em";
  77.  
  78. /* jshint ignore:start */
  79. // eslint-disable-next-line no-eval
  80. const input = eval(
  81. document
  82. .querySelector("#pageContent script")
  83. .textContent.slice(5, -3)
  84. .replace("function", "function x")
  85. .replace("return p}", "return p}x")
  86. );
  87. /* jshint ignore:end */
  88.  
  89. const mediaPath = input.match(/mediaPath='(.+?)'/)[1];
  90. const tracks = JSON.parse(
  91. input.match(/tracks=(\[.+?,\])/)[1].replace(",]", "]")
  92. );
  93. const output = tracks.map(
  94. (x) =>
  95. mediaPath +
  96. x.file.split(".").slice(0, -1).join(".") +
  97. "." +
  98. format.toLowerCase()
  99. );
  100. const names = tracks.map((x) => x.name);
  101.  
  102. const blobWriter = new zip.BlobWriter("application/zip");
  103. const writer = new zip.ZipWriter(blobWriter);
  104.  
  105. function forSync(i) {
  106. element.innerHTML =
  107. "Downloading track " +
  108. (i + 1) +
  109. " of " +
  110. output.length +
  111. " (" +
  112. (names[i] || "Track " + (i + 1)) +
  113. ")…";
  114. GM.xmlHttpRequest({
  115. method: "GET",
  116. url: output[i],
  117. responseType: "blob",
  118. onload: async (response) => {
  119. await writer.add(
  120. decodeURIComponent(output[i].split("/").pop()),
  121. new zip.BlobReader(response.response)
  122. );
  123.  
  124. if (output[i + 1]) {
  125. forSync(i + 1);
  126. } else {
  127. await writer.close();
  128. const blob = await blobWriter.getData();
  129. saveAs(
  130. blob,
  131. document.getElementsByTagName("h2")[0].textContent + ".zip"
  132. );
  133. element.innerHTML =
  134. "Album successfully downloaded. ZIP file has been passed to the browser.";
  135. }
  136. },
  137. });
  138. }
  139. forSync(0);
  140. });
  141. });
  142. })();
  143.  
  144. // @license-end