Greasy Fork

Greasy Fork is available in English.

学起Plus挂课自动连续播放

一个网课挂机自动连续播放工具,仅适用于学起Plus sccchina.net chinaedu.net,反馈与交流QQ群:715307684,更新日期:2022年11月14日

当前为 2022-11-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         学起Plus挂课自动连续播放
// @namespace    http://tampermonkey.net/
// @version      0.15.1
// @description  一个网课挂机自动连续播放工具,仅适用于学起Plus sccchina.net chinaedu.net,反馈与交流QQ群:715307684,更新日期:2022年11月14日
// @author       哆哆啦啦梦
// @match        *://*.chinaedu.net/*
// @match        *://*.sccchina.net/*
// @match        *://rspcourse.chinaedu.net/*
// @match        *://*.edu.cn/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chinaedu.net
// @grant        unsafeWindow
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @license      GPLv3
// ==/UserScript==

const newSupport_20221106 = {
  lessionInfos: [],
  currLessionId: "",
  currLessionType: 0,
  isSupport: false,
  getLessions() {
    // 所有的播放节点并获取其课程名称和id
    document.querySelectorAll("#catalogDiv h3[onclick]").forEach((e) =>
      this.lessionInfos.push({
        ele: e,
        title: e.innerText,
        callStr: e.onclick.toString(),
      })
    );
    return this.lessionInfos.length;
  },
  getCurr() {
    // 获取当前播放课程ID
    this.currLessionId = document.querySelector("#courseActivityId").value;
    // 查找当前课程ID对应的节点
    const index = this.lessionInfos.findIndex(
      (e) => e.callStr.indexOf(this.currLessionId) > 0
    );
    // 正则提取类型
    const res = this.lessionInfos[index].callStr.match(/\'(\d)\'/);
    if (res && res.length === 2) {
      this.currLessionType = res[1];
      console.log(
        `当前播放课程:${this.lessionInfos[index].title},课程类型:${this.currLessionType}`
      );
      return true;
    }
  },
  playNext() {
    // 查找当前课程ID对应的节点
    const index = this.lessionInfos.findIndex(
      (e) => e.callStr.indexOf(this.currLessionId) > 0
    );
    // 判断是否存在下一个课程
    if (index + 1 < this.lessionInfos.length) {
      console.log("下一个播放的课程:", this.lessionInfos[index + 1].title);
      // 调用播放下一个
      this.lessionInfos[index + 1].ele.onclick();
    } else {
      console.log("课程播放结束");
      alert("课程播放结束");
    }
  },
  playCheck() {
    // 设置支持
    this.isSupport = true;

    // 挂课弹窗解除
    const tips = document.querySelector(".win-content");

    if (tips && tips.innerText.indexOf("继续学习") > 0) {
      const btn = document.querySelector(".win-content .close-win-bt");
      btn && btn.click();
    }

    if (this.currLessionType !== "1") {
      setTimeout(() => {
        this.playNext();
      }, 5000);
    } else {
      // 获取视频元素
      const video = document.querySelector("video");

      // 设置静音
      video.muted = true;
      // 播放速率,可自行修改0.5,1,1.25,1.5,2
      video.playbackRate = 1;

      // 获取当前播放进度和视频长度 保留一位小数
      const currentTime = video.currentTime.toFixed(1);
      const totalTime = video.duration.toFixed(1);

      console.log(`当前进度:${currentTime}/${totalTime}`);

      if (currentTime < totalTime - 5) {
        // 视频没有播放完
        if (video.paused) {
          console.log("视频被暂停,继续播放!");
          video.play();
        }

        setTimeout(() => {
          this.playCheck();
        }, 5000);
      } else {
        setTimeout(() => {
          this.playNext();
        }, 5000);
      }
    }
  },
  work() {
    setTimeout(() => {
      this.getLessions() && this.getCurr() && this.playCheck();
    }, 5000);
  },
};

const newSupport_20221116 = {
  videoModuleInfo: null,
  lessionsInfo: null,
  currLessionId: "",
  stepOne() {
    setTimeout(() => {
      this.findLession();
      this.checkStatus();

      GM_deleteValue("newSupport_20221116.isVideoInPaly");
      if (document.querySelector("video")) {
        GM_setValue("newSupport_20221116.isVideoInPaly", true);

        this.playCheck();
      }
    }, 5000);
  },
  async checkStatus() {
    await this.getCurr();

    const isGoto = await this.gotoNext();

    if (isGoto && GM_getValue("newSupport_20221116.isVideoInPaly")) {
      this.playCheck();
    }

    setTimeout(() => {
      this.checkStatus();
    }, 5000);
  },
  getLessionsInfo(arr, path = []) {
    const infos = [];
    arr.forEach((e) => {
      if (e.child) {
        infos.push(...this.getLessionsInfo(e.child, [...path, e.id]));
      } else {
        // 添加到数组
        infos.push({ ...e, path: [...path, e.id] });
      }
    });

    return infos;
  },
  getLessions() {
    // 处理弹窗
    const pop = document.querySelector("#pop");

    // 存在就点击关闭按钮
    pop && pop.querySelector(".pop_close").click();

    // 处理课程信息
    if (!courseInfo) {
      return false;
    }

    // 提取课程信息
    const lessionInfo = {
      id: courseInfo.id,
      name: courseInfo.name,
    };

    console.log(`当前课程:${lessionInfo.name},ID:${lessionInfo.id}`);

    // 提取课程模块
    const module = courseInfo.child.$model;

    console.log("模块信息:", module);

    // 查找视频模块索引
    const index = module.findIndex(
      (e) => e.name === "视频学习" || (e.code && e.code === "course")
    );

    // 提取视频模块信息
    this.videoModuleInfo = {
      id: module[index].id,
      name: module[index].name,
    };

    console.log("视频模块信息:", this.videoModuleInfo);

    this.lessionsInfo = this.getLessionsInfo(module[index].child);

    // 提取课程Res
    console.log("课程章节信息:", this.lessionsInfo);

    GM_setValue("newSupport_20221116_lessionsInfo", this.lessionsInfo);

    return true;
  },
  checkCoursePage() {
    if (this.lessionsInfo && this.lessionsInfo.length) {
      // 取出第一个
      const firstInfo = this.lessionsInfo[0];
      // 查询是否元素存在
      if (document.querySelector(`li[nid='${firstInfo.id}']`) === null) {
        // 不存在,代表非视频页
        // 获取当前选中的模块
        const active = document.querySelector("li.fl.cur");

        if (!active || active.getAttribute("nid") !== this.videoModuleInfo.id) {
          console.log(
            `当前课程模块不是视频模块!即将转到视频模块,ID:${this.videoModuleInfo.id}`
          );

          // 查找视频模块标签并点击
          document
            .querySelector(`li[nid='${this.videoModuleInfo.id}']`)
            .click();
        }

        return false;
      }

      return true;
    }

    return false;
  },
  findLession() {
    if (this.getLessions()) {
      if (!this.checkCoursePage()) {
        // 延迟执行
        setTimeout(() => this.findLession, 5000);
      } else {
        return true;
      }
    }

    return false;
  },
  stepTwo() {
    setTimeout(() => {
      this.playCheck();
    }, 10000);
  },
  async getCurr() {
    return new Promise((reslove) => {
      // 获取课程内容容器
      const courseCon = document.querySelector(".courseCon iframe");

      if (courseCon) {
        // 获取加载的地址
        const iframeSrc = courseCon.getAttribute("src");

        // 根据src获取对应课程信息
        let currInfo = null;
        this.lessionsInfo.find((item) =>
          item.res.find((e) => {
            if (decodeURI(iframeSrc).indexOf(decodeURI(e.url)) >= 0) {
              currInfo = e;
            }

            return currInfo;
          })
        );

        GM_setValue("newSupport_20221116_currInfo", currInfo);
      }

      reslove();
    });
  },
  randomRange(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  },
  clickAutomatic(element) {
    var _offset = $(element).offset();
    var x = 0,
      y = 0;
    if (typeof _offset == "undefined") {
      return;
    }
    x = this.randomRange(_offset.left, _offset.left + $(element).width());
    y = this.randomRange(_offset.top, _offset.top + $(element).height());
    if (x <= 0 || y <= 0 || x >= $(window).width() || y >= $(window).height()) {
      return;
    }
    document.elementFromPoint(x, y).click();
  },
  async sleep(time = 3) {
    return new Promise((reslove) => {
      setTimeout(() => {
        reslove();
      }, time * 1000);
    });
  },
  async openMenu(path) {
    return new Promise(async (reslove) => {
      for (let i = 0; i < path.length; i++) {
        const ele = document.querySelector(`li[nid='${path[i]}']`);
        if (ele && ele.className.indexOf("open") === -1) {
          document.querySelector(".ps-container").scrollTo(0, ele.offsetTop);
          await this.sleep(1);
          this.clickAutomatic(ele);
        }

        await this.sleep(3);
      }

      reslove();
    });
  },
  async gotoNext() {
    return new Promise(async (reslove) => {
      const nextData = GM_getValue("newSupport_20221116_next");

      if (nextData) {
        const { info, pos } = nextData;
        const nextEle = document.querySelector(`li[nid='${info.id}']`);

        if (nextEle && nextEle.className.indexOf("open active") === -1) {
          await this.openMenu(info.path);

          await this.sleep(3);

          const subEle = document.querySelector(
            `.pcCourseList li[nid='${info.res[pos].id}']`
          );

          subEle &&
            subEle.getAttribute("ms-id") === null &&
            this.clickAutomatic(subEle);
        }

        await this.sleep(5);

        await this.getCurr();

        GM_deleteValue("newSupport_20221116_next");

        return reslove(true);
      }

      reslove(false);
    });
  },
  playNext() {
    if (GM_getValue("newSupport_20221116_next")) {
      return;
    }

    const currInfo = GM_getValue("newSupport_20221116_currInfo");

    // 查找当前课程ID对应的节点
    let index = this.lessionsInfo.findIndex((item) =>
      item.res.find((e) => e.id === currInfo.id)
    );

    // 获取当前index所指向
    const currIndexInfo = this.lessionsInfo[index];

    // 获取当前信息在index所指向中的位置
    let pos = currIndexInfo.res.findIndex((e) => e.id === currInfo.id);

    let nextId = null;

    // 判断是否为最后一个
    if (pos === currIndexInfo.res.length - 1) {
      // 那就index + 1,pos 归零
      index = index + 1;
      pos = 0;
    } else {
      // 那就pos + 1, index 不变
      pos = pos + 1;
    }

    // 判断是否存在下一个课程
    if (index < this.lessionsInfo.length) {
      console.log(
        `下一个播放的课程信息:`,
        this.lessionsInfo[index],
        `pos: ${pos}`
      );
      // 设置播放下一个ID
      GM_setValue("newSupport_20221116_next", {
        info: this.lessionsInfo[index],
        pos,
      });
    } else {
      console.log("课程播放结束");
      alert("课程播放结束");
    }
  },
  playCheck() {
    const info = GM_getValue("newSupport_20221116_currInfo");

    // 挂课弹窗解除
    const tips = document.querySelector(".win-content");

    if (tips && tips.innerText.indexOf("继续学习") > 0) {
      const btn = document.querySelector(".win-content .close-win-bt");
      btn && btn.click();
    }

    if (info) {
      if (info.type === "sfp" || info.type === "video") {
        // 获取视频元素
        const video = document.querySelector("video");

        if (video) {
          // 设置静音
          video.muted = true;
          // 播放速率,可自行修改0.5,1,1.25,1.5,2
          video.playbackRate = 1;

          // 获取当前播放进度和视频长度 保留一位小数
          const currentTime = video.currentTime.toFixed(1);
          const totalTime = video.duration.toFixed(1);

          console.log(`当前进度:${currentTime}/${totalTime}`);

          if (currentTime < totalTime - 5) {
            // 视频没有播放完
            if (video.paused) {
              console.log("视频被暂停,继续播放!");
              video.play();
            }

            setTimeout(() => {
              this.playCheck();
            }, 10000);

            return true;
          }
        }

        setTimeout(() => {
          this.playNext();
        }, 100);
      } else {
        console.log("未知内容类型:", info.type);
      }
    } else {
      console.log("无法获取内容类型");
    }
  },
};

(function () {
  "use strict";

  const url = document.URL;

  if (
    url.indexOf("//rspcourse.chinaedu.net/") !== -1 ||
    url.indexOf(".sccchina.net/") !== -1 ||
    url.indexOf("study.do?userCourseId") !== -1
  ) {
    if (url.indexOf("/play.html") >= 0) {
      // 删除之前的信息
      GM_deleteValue("newSupport_20221116_lessionsInfo");
      GM_deleteValue("newSupport_20221116_currInfo");
      // 课件页面,查找课程
      newSupport_20221116.stepOne();
    } else {
      setTimeout(() => {
        newSupport_20221116.lessionsInfo = GM_getValue(
          "newSupport_20221116_lessionsInfo"
        );
        if (
          newSupport_20221116.lessionsInfo &&
          !GM_getValue("newSupport_20221116.isVideoInPaly")
        ) {
          newSupport_20221116.stepTwo();
        } else {
          // 新支持
          newSupport_20221106.work();
        }
      }, 8000);
    }
  } else {
    console.log("未知页面");
  }
})();