Curseforge 直接下载

跳过下载时的 5 秒等待,适用于新旧 Curseforge。

// ==UserScript==
// @name        Curseforge 直接下载
// @namespace   su226
// @include     https://www.curseforge.com/*
// @include     https://legacy.curseforge.com/*
// @grant       none
// @version     1.0
// @author      su226
// @description 跳过下载时的 5 秒等待,适用于新旧 Curseforge。
// @license     MIT
// ==/UserScript==

function observeSelector(selector, callback) {
  for (const el of document.querySelectorAll(selector)) {
    callback(el);
  }
  new MutationObserver((mutations, observer) => {
    for (const mutation of mutations) {
      for (const node of mutation.addedNodes) {
        if (node.nodeType != Node.ELEMENT_NODE) {
          continue;
        }
        if (node.matches(selector)) {
          callback(node);
        }
        for (const el of node.querySelectorAll(selector)) {
          callback(el);
        }
      }
    }
  }).observe(document, { childList: true, subtree: true });
}

async function downloadOld(href) {
  if (href.match(/\/\d+$/)) {
    location.href = href + "/file";
    return;
  }
  const response = await fetch(href);
  const content = await response.text();
  const parser = new DOMParser();
  const doc = parser.parseFromString(content, "text/html");
  const downloadLink = doc.querySelector(".alink").href;
  location.href = downloadLink;
}

async function downloadNew(href) {
  const response = await fetch(href);
  const content = await response.text();
  const projectId = content.match(/\\"project\\":{\\"id\\":(\d+)/)[1];
  const fileId = href.match(/\d+$/);
  location.href = `https://www.curseforge.com/api/v1/mods/${projectId}/files/${fileId}/download`;
}

if (location.host.indexOf("legacy") !== -1) {
  observeSelector("a", el => {
    if (el.href.indexOf("/download") === -1 || el.href.indexOf("?client=y") !== -1) {
      return;
    }
    console.log(el);
    el.addEventListener("click", e => {
      e.preventDefault();
      downloadOld(el.href);
    });
  });
} else {
  observeSelector(".download-cta", el => {
    if (el.tagName !== "A") {
      return;
    }
    el.addEventListener("click", e => {
      e.preventDefault();
      downloadNew(el.href);
    })
  });
  observeSelector(".modal", el => {
    const fileCard = el.querySelector(".file-card");
    if (!fileCard) {
      return;
    }
    const button = el.querySelector(".btn-primary");
    const newButton = button.cloneNode(true);
    newButton.addEventListener("click", () => {
      // 确保是最新的 fileCard
      const fileCard = el.querySelector(".file-card");
      downloadNew(fileCard.href.replace("/files/", "/download/"));
    });
    button.insertAdjacentElement("afterend", newButton);
    button.remove();
  });
}