学习通下载器 Chaoxing Downloader

一键下载资料文件,无视系统限制。

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         学习通下载器 Chaoxing Downloader
// @namespace    https://github.com/lcandy2/user.js/tree/main/websites/chaoxing.com/chaoxing-downloader
// @version      1.2
// @author       甜檸Cirtron (lcandy2)
// @description  一键下载资料文件,无视系统限制。
// @license      AGPL-3.0-or-later
// @copyright    lcandy2 All Rights Reserved
// @homepage     https://greasyfork.org/scripts/499214
// @homepageURL  https://greasyfork.org/scripts/499214
// @source       https://github.com/lcandy2/user.js/tree/main/websites/chaoxing.com/chaoxing-downloader
// @match        *://*.chaoxing.com/mooc2-ans/coursedata/stu-datalist*
// @require      https://registry.npmmirror.com/vue/3.4.27/files/dist/vue.global.prod.js
// @require      data:application/javascript,%3Bwindow.Vue%3DVue%3B
// @require      https://registry.npmmirror.com/vuetify/3.6.6/files/dist/vuetify.min.js
// @require      data:application/javascript,%3B
// @resource     VuetifyStyle  https://registry.npmmirror.com/vuetify/3.6.6/files/dist/vuetify.min.css
// @connect      pan-yz.chaoxing.com
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

(function (vue, vuetify) {
  'use strict';

  var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  const backgroundFetch = async (url) => {
    return new Promise((resolve, reject) => {
      _GM_xmlhttpRequest({
        method: "GET",
        url,
        onload: (response) => {
          resolve(response.responseText);
        },
        onerror: (error) => {
          reject(error);
        }
      });
    });
  };
  const _sfc_main = /* @__PURE__ */ vue.defineComponent({
    __name: "App",
    setup(__props) {
      const buttonRef = vue.ref(null);
      const downloadLink = vue.ref("");
      const isDownloading = vue.ref(false);
      const isFinished = vue.ref(false);
      const isErrored = vue.ref(false);
      const fileInfo = vue.ref({
        fileName: "",
        objectId: "",
        dataId: "",
        t: "",
        courseId: "",
        clazzId: "",
        cpi: "",
        puid: ""
      });
      const handleClick = async (event) => {
        !isFinished.value && event.preventDefault();
        isDownloading.value = true;
        if (buttonRef.value && !isFinished.value) {
          try {
            const tokenReq = await backgroundFetch("https://pan-yz.chaoxing.com/api/token/uservalid");
            const tokenJson = JSON.parse(tokenReq);
            const token = tokenJson._token;
            const fileInfoApi = new URL(
              "https://pan-yz.chaoxing.com/api/share/downloadUrl"
            );
            fileInfoApi.searchParams.set("puid", fileInfo.value.puid);
            fileInfoApi.searchParams.set("_token", token);
            fileInfoApi.searchParams.set("sarepuid", fileInfo.value.puid);
            fileInfoApi.searchParams.set("objectid", fileInfo.value.objectId);
            const fileInfoReq = await backgroundFetch(fileInfoApi.toString());
            const fileInfoJson = JSON.parse(fileInfoReq);
            const fileInfoUrl = fileInfoJson.url;
            downloadLink.value = fileInfoUrl.toString();
            isFinished.value = true;
            vue.nextTick(() => {
              buttonRef.value.$el.click();
            });
          } catch (e) {
            isErrored.value = true;
            console.error(e);
          }
        } else {
          isDownloading.value = false;
        }
      };
      vue.onMounted(() => {
        var _a;
        if (buttonRef.value) {
          try {
            const fileElement = (_a = buttonRef.value.$el.parentElement) == null ? void 0 : _a.parentElement.parentElement.parentElement;
            fileInfo.value.objectId = fileElement == null ? void 0 : fileElement.getAttribute("objectid");
            fileInfo.value.fileName = fileElement == null ? void 0 : fileElement.getAttribute("dataname");
            fileInfo.value.dataId = (fileElement == null ? void 0 : fileElement.getAttribute("id")) || (fileElement == null ? void 0 : fileElement.getAttribute("order"));
            fileInfo.value.t = fileElement == null ? void 0 : fileElement.getAttribute("t");
            const userIdElement = document.querySelector("input#userId");
            if (userIdElement) {
              fileInfo.value.puid = userIdElement.getAttribute("value") || "";
            }
            const href = new URL(window.location.href);
            fileInfo.value.courseId = href.searchParams.get("courseid") || "";
            fileInfo.value.clazzId = href.searchParams.get("clazzid") || "";
            fileInfo.value.cpi = href.searchParams.get("cpi") || "";
            const downloadUrl = new URL(
              "https://mooc1.chaoxing.com/coursedata/downloadData?ut=s"
            );
            downloadUrl.searchParams.set("dataId", fileInfo.value.dataId);
            downloadUrl.searchParams.set("classId", fileInfo.value.clazzId);
            downloadUrl.searchParams.set("cpi", fileInfo.value.cpi);
            downloadUrl.searchParams.set("courseId", fileInfo.value.courseId);
            downloadLink.value = downloadUrl.toString();
          } catch (e) {
            isErrored.value = true;
            console.error(e);
          }
        }
      });
      return (_ctx, _cache) => {
        const _component_v_btn = vue.resolveComponent("v-btn");
        return vue.openBlock(), vue.createBlock(_component_v_btn, {
          ref_key: "buttonRef",
          ref: buttonRef,
          disabled: isErrored.value,
          loading: isDownloading.value && !isErrored.value,
          onClick: handleClick,
          href: downloadLink.value,
          density: "compact",
          variant: "plain",
          color: "primary"
        }, {
          default: vue.withCtx(() => [
            vue.createTextVNode(vue.toDisplayString(isErrored.value ? "下载器错误,请刷新重试" : isDownloading.value ? "正在下载中" : isFinished.value ? "下载完成" : "下载器下载"), 1)
          ]),
          _: 1
        }, 8, ["disabled", "loading", "href"]);
      };
    }
  });
  const appendApp = (element) => {
    const vuetify$1 = vuetify.createVuetify({});
    const app = vue.createApp(_sfc_main);
    app.use(vuetify$1);
    app.mount(element);
  };
  const cssLoader = (e) => {
    const t = GM_getResourceText(e);
    return GM_addStyle(t), t;
  };
  cssLoader("VuetifyStyle");
  function addElement() {
    const uls = document.querySelectorAll("ul.operate");
    uls.forEach((ul) => {
      var _a;
      const file = (_a = ul.parentElement) == null ? void 0 : _a.parentElement;
      if (!file)
        return;
      const isBook = file.getAttribute("type") === "book";
      const isAFolder = file.getAttribute("type") === "afolder";
      const hasObjectID = file.getAttribute("objectid") !== "";
      if (isBook || isAFolder || !hasObjectID)
        return;
      if (!ul.querySelector("[chaoxing-downloader]")) {
        const li = document.createElement("li");
        li.className = "operate_down";
        li.setAttribute("chaoxing-downloader", "");
        ul.appendChild(li);
        appendApp(li);
      }
    });
  }
  addElement();
  const observer = new MutationObserver((mutationsList, observer2) => {
    for (let mutation of mutationsList) {
      if (mutation.type === "childList") {
        addElement();
      }
    }
  });
  const config = { childList: true, subtree: true };
  observer.observe(document, config);

})(Vue, Vuetify);