学起Plus、弘成教育挂课自动连续播放

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

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

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

const lessionRules = {
  "play.html": {
    selector: ['.page-sidebar li>a>span[class="title"]'],
  },
  "study.do": {
    beforeFun: () => {
      const catalogDiv = document.getElementById("catalogDiv");
      if (catalogDiv.childElementCount === 0) {
        const catalog = document.getElementById("catalogA");
        catalog && catalog.className.indexOf("Cur") === -1 && catalog.click();
      }
    },
    selector: ["#catalogDiv span[onclick]", "#catalogDiv span[class='tit']"],
  },
  "mp4_video_index.html": {
    selector: [".ui-folder .ui-leaf span"],
  },
};

const currentRules = {
  "videolearning.html": {
    selector: [".page-sidebar li.active>a>span"],
  },
  "play.html": {
    selector: [".page-breadcrumb li>a", ".page-sidebar li.active>a>span"],
  },
  "mp4_video_index.html": {
    selector: [".ui-folder .ui-leaf.ui-selected span"],
  },
  "study.do": {
    selector: [
      "#catalogDiv .cur span",
      '.study-video-title span[class$="title"]',
    ],
  },
};

const videoRules = {
  "video.html": {
    selector: ["#videoFrame video"],
  },
  "play.html": {
    selector: ["#draggable video"],
  },
  "mp4_video_index.html": {
    selector: [".plyr__video-wrapper video"],
  },
  "study.do": {
    selector: ["videobox video"],
  },
};

const noNeedAutoPlayRules = ["mp4_video_index"];

function isInNoNeedAutoPlay() {
  return noNeedAutoPlayRules.find((e) => document.URL.indexOf(e) > 0);
}

function urlIn(rules) {
  for (let key in rules) {
    if (document.URL.indexOf(key) > 0) {
      return true;
    }
  }

  return false;
}

function getDataForRules(rules) {
  for (let key in rules) {
    if (document.URL.indexOf(key) > 0) {
      for (let i = 0; i < rules[key].selector.length; i++) {
        rules[key].beforeFun && rules[key].beforeFun(rules[key].selector[i]);
        const res = document.querySelectorAll(rules[key].selector[i]);
        rules[key].afterFun && rules[key].afterFun(rules[key].selector[i], res);

        if (res.length > 0) {
          return res;
        }
      }
    }
  }

  return null;
}

function getCurrentLession() {
  const arr = getDataForRules(currentRules);

  if (arr) {
    GM_setValue("current", arr[arr.length - 1].innerText);
  }
}

function getLessionsInfo() {
  const arr = getDataForRules(lessionRules);

  if (arr) {
    const lessions = [];
    for (let i = 0; i < arr.length; i++) {
      const className = "api20221120-" + i;
      if (arr[i].className.indexOf(className) === -1) {
        arr[i].className += " " + className;
      }
      lessions.push({ title: arr[i].innerText, className });
    }
    GM_setValue("lessions", lessions);
  }
}

let findVideoCount = 0;
const findVideoMaxCount = 3;

function getVideo() {
  const status = GM_getValue("play_end");
  if (GM_getValue("video") || status) {
    return;
  }

  if (findVideoCount >= findVideoMaxCount) {
    if (status !== "not found") {
      GM_setValue("play_end", "not found");
      findVideoCount = 0;
    }
    return;
  }

  if (document.querySelector("video")) {
    GM_setValue("video", document.URL);

    setTimeout(() => {
      playCheck();
    }, 5000);
  } else {
    findVideoCount++;
  }
}

function playCheck() {
  if (GM_getValue("play_end")) {
    return;
  }

  const video = document.querySelector("video");

  if (video) {
    video.muted = true;
    video.playbackRate = 1;

    const currentTime = video.currentTime.toFixed(1);
    const totalTime = video.duration.toFixed(1);

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

    if (video.ended) {
      setTimeout(() => {
        GM_setValue("play_end", "over");
      }, 5000);
    } else {
      if (video.paused) {
        console.log("视频被暂停,继续播放!");
        video.play();
      }

      setTimeout(() => {
        playCheck();
      }, 5000);
    }
  } else {
    console.log("异常:找不到视频元素了");
  }
}

function nextCheck() {
  const status = GM_getValue("play_end");
  const lessions = GM_getValue("lessions");
  if (status && lessions && lessions.length) {
    let currentText = GM_getValue("current");
    const lastCurrent = GM_getValue("last_current");
    let step = GM_getValue("step");

    if (!lastCurrent || (currentText && lastCurrent !== currentText)) {
      GM_setValue("last_current", currentText);
      step = 1;
    } else {
      currentText = lastCurrent;
      step += 1;
    }

    GM_setValue("step", step);

    let index = GM_getValue("last_pos") ?? 0;

    if (status === "not found" && !currentText) {
      step = 0;
    } else if (isInNoNeedAutoPlay()) {
      return;
    }

    const newIndex = lessions.findIndex((e) => e.title === currentText);

    if (newIndex !== -1) {
      index = newIndex;
    }

    GM_setValue("last_pos", index);

    if (index + step < lessions.length) {
      document.querySelector("." + lessions[index + step].className).click();
      GM_deleteValue("play_end");
      GM_deleteValue("video");
    } else {
      alert("课程播放结束");
      return;
    }
  }

  setTimeout(() => {
    nextCheck();
  }, 5000);
}

function getResource() {
  getCurrentLession();
  getLessionsInfo();
}

function init() {
  GM_deleteValue("play_end");
  GM_deleteValue("video");
  GM_deleteValue("current");
  GM_deleteValue("last_current");
  GM_deleteValue("lessions");
  GM_setValue("step", 1);
  GM_setValue("last_pos", 0);
}

function popupClose() {
  const tips = document.querySelector(".win-content");

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

  const pop = document.querySelector("#pop");

  pop && pop.querySelector(".pop_close").click();
}

function work() {
  init();

  setTimeout(() => {
    urlIn(lessionRules) && nextCheck();
  }, 5000);

  setInterval(() => {
    urlIn(videoRules) && getVideo();
  }, 10000);

  setInterval(() => {
    getResource();
    popupClose();
  }, 3000);
}

(function () {
  "use strict";

  work();
})();