VGMLoaderX

Automatically downloads albums from KHInsider without an account.

Per 28-07-2025. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name               VGMLoaderX
// @name:de            VGMLoaderX
// @name:en            VGMLoaderX
// @namespace          sun/userscripts
// @version            1.0.27
// @description        Automatically downloads albums from KHInsider without an account.
// @description:de     Lädt Alben von KHInsider automatisch und ohne Account herunter.
// @description:en     Automatically downloads albums from KHInsider without an account.
// @compatible         chrome
// @compatible         edge
// @compatible         firefox
// @compatible         opera
// @compatible         safari
// @homepageURL        https://forgejo.sny.sh/sun/userscripts
// @supportURL         https://forgejo.sny.sh/sun/userscripts/issues
// @contributionURL    https://liberapay.com/sun
// @contributionAmount €1.00
// @author             Sunny <[email protected]>
// @include            https://downloads.khinsider.com/game-soundtracks/album/*
// @match              https://downloads.khinsider.com/game-soundtracks/album/*
// @connect            vgmdownloads.com
// @connect            vgmsite.com
// @connect            vgmtreasurechest.com
// @run-at             document-end
// @inject-into        page
// @grant              GM.xmlHttpRequest
// @grant              GM_xmlhttpRequest
// @noframes
// @require            https://unpkg.com/@zip.js/zip.js/dist/zip.js
// @require            https://unpkg.com/file-saver
// @require            https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @icon               https://forgejo.sny.sh/sun/userscripts/raw/branch/main/icons/VGMLoaderX.ico
// @copyright          2021-present, Sunny (https://sny.sh/)
// @license            Hippocratic License; https://forgejo.sny.sh/sun/userscripts/src/branch/main/LICENSE.md
// ==/UserScript==

(() => {
  for (const x of document.querySelectorAll('a[href^="/cp/add_album/"]')) {
    x.addEventListener("click", (e) => {
      e.preventDefault();

      let format = [
        ...document.querySelectorAll("#songlist_header th[align=right]"),
      ].map((x) => x.textContent);
      if (format.length === 1) {
        format = format[0];
      } else {
        const input = prompt(
          `Please enter your desired format (one of ${format.join(", ")}):`,
          format[0],
        );

        if (!input) return;
        if (!format.includes(input.toUpperCase())) {
          format = format[0];
          alert(`Invalid format supplied. Using ${format} instead.`);
        } else {
          format = input;
        }
      }

      const element = document.getElementsByClassName("albumMassDownload")[0];
      element.style.height = "auto";
      element.style.marginBottom = "2em";

      const input = eval(
        document
          .querySelector("#pageContent script")
          .textContent.slice(5, -3)
          .replace("function", "function x")
          .replace("return p}", "return p}x"),
      );

      const mediaPath = input.match(/mediaPath='(.+?)'/)[1];
      const tracks = JSON.parse(
        input.match(/tracks=(\[.+?,\])/)[1].replace(",]", "]"),
      );
      const output = tracks.map(
        (x) =>
          `${
            mediaPath + x.file.split(".").slice(0, -1).join(".")
          }.${format.toLowerCase()}`,
      );
      const names = tracks.map((x) => x.name);

      const blobWriter = new zip.BlobWriter("application/zip");
      const writer = new zip.ZipWriter(blobWriter);

      function forSync(i) {
        element.innerHTML = `Downloading track ${i + 1} of ${output.length} (${names[i] || `Track ${i + 1}`})...`;
        GM.xmlHttpRequest({
          method: "GET",
          url: output[i],
          responseType: "blob",
          onload: async (response) => {
            await writer.add(
              decodeURIComponent(output[i].split("/").pop()),
              new zip.BlobReader(response.response),
            );

            if (output[i + 1]) {
              forSync(i + 1);
            } else {
              await writer.close();
              const blob = await blobWriter.getData();
              saveAs(
                blob,
                `${document.getElementsByTagName("h2")[0].textContent}.zip`,
              );
              element.innerHTML =
                "Album successfully downloaded. ZIP file has been passed to the browser.";
            }
          },
        });
      }
      forSync(0);
    });
  }
})();