Greasy Fork

Greasy Fork is available in English.

学起Plus挂课自动连续播放

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

目前为 2022-11-17 提交的版本。查看 最新版本

// ==UserScript==
// @name         学起Plus挂课自动连续播放
// @namespace    http://tampermonkey.net/
// @version      0.15
// @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();
    }

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

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

    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;
    }

    // 提取课程信息
    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");

    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("未知页面");
  }
})();