VGMLoaderX

Automatically downloads albums from KHInsider without an account.

Устаревшая версия за 28.07.2025. Перейдите к последней версии.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==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);
    });
  }
})();