学习通下载器 Chaoxing Downloader

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

安裝腳本?
作者推薦腳本

您可能也會喜歡 学习通/MOOC等 隐藏答案 Hide Answer

安裝腳本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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