Greasy Fork镜像 支持简体中文。

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

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

  1. // ==UserScript==
  2. // @name 学起Plus、弘成教育挂课自动连续播放
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.6
  5. // @description 一个网课挂机自动连续播放工具,仅适用于学起Plus、弘成教育 sccchina.net chinaedu.net,反馈与交流QQ群:715307684,更新日期:2022年11月27日
  6. // @author 哆哆啦啦梦
  7. // @match *://*.chinaedu.net/*
  8. // @match *://*.sccchina.net/*
  9. // @match *://*.edu.cn/*
  10. // @match *://*.bnude.cn/*
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=chinaedu.net
  12. // @grant unsafeWindow
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @grant GM_deleteValue
  16. // @run-at document-end
  17. // @license GPLv3
  18. // ==/UserScript==
  19.  
  20. const lessionRules = {
  21. "play.html": {
  22. selector: ['.page-sidebar li>a>span[class^="title"]'],
  23. },
  24. "study.do": {
  25. beforeFun: () => {
  26. const catalogDiv = document.getElementById("catalogDiv");
  27. if (catalogDiv.childElementCount === 0) {
  28. const catalog = document.getElementById("catalogA");
  29. catalog && catalog.className.indexOf("Cur") === -1 && catalog.click();
  30. }
  31. },
  32. selector: ["#catalogDiv span[onclick]", "#catalogDiv span[class^='tit']", "#catalogDiv li h3[onclick]"],
  33. },
  34. "mp4_video_index.html": {
  35. selector: [".ui-folder .ui-leaf span"],
  36. },
  37. "index.html": {
  38. selector: [".cwcOutline span[id$='_span']"],
  39. },
  40. };
  41.  
  42. const currentRules = {
  43. "videolearning.html": {
  44. selector: [".page-sidebar li.active>a>span"],
  45. },
  46. "play.html": {
  47. selector: [".page-sidebar li.active>a>span"],
  48. },
  49. "mp4_video_index.html": {
  50. selector: [".ui-folder .ui-leaf.ui-selected span"],
  51. },
  52. "study.do": {
  53. selector: [
  54. "#catalogDiv .cur span",
  55. ".study-video-title span[class$='title']",
  56. "#catalogDiv li.cur h3[onclick]",
  57. ],
  58. },
  59. "index.html": {
  60. selector: [".cwcOutline .curSelectedNode span[id$='_span']"],
  61. },
  62. };
  63.  
  64. const videoRules = {
  65. "video.html": {
  66. selector: ["#videoFrame video"],
  67. },
  68. "play.html": {
  69. selector: ["#draggable video"],
  70. },
  71. "mp4_video_index.html": {
  72. selector: [".plyr__video-wrapper video"],
  73. },
  74. "study.do": {
  75. selector: ["videobox video"],
  76. },
  77. "index.html": {
  78. selector: [".video-js video"],
  79. },
  80. };
  81.  
  82. const noNeedAutoPlayRules = ["mp4_video_index"];
  83.  
  84. function isInNoNeedAutoPlay() {
  85. return noNeedAutoPlayRules.find((e) => document.URL.indexOf(e) > 0);
  86. }
  87.  
  88. function urlIn(rules) {
  89. for (let key in rules) {
  90. if (document.URL.indexOf(key) > 0) {
  91. return true;
  92. }
  93. }
  94.  
  95. return false;
  96. }
  97.  
  98. function getDataForRules(rules) {
  99. for (let key in rules) {
  100. if (document.URL.indexOf(key) > 0) {
  101. for (let i = 0; i < rules[key].selector.length; i++) {
  102. rules[key].beforeFun && rules[key].beforeFun(rules[key].selector[i]);
  103. const res = document.querySelectorAll(rules[key].selector[i]);
  104. rules[key].afterFun && rules[key].afterFun(rules[key].selector[i], res);
  105.  
  106. if (res.length > 0) {
  107. return res;
  108. }
  109. }
  110. }
  111. }
  112.  
  113. return null;
  114. }
  115.  
  116. function getCurrentLession() {
  117. const arr = getDataForRules(currentRules);
  118.  
  119. if (arr) {
  120. GM_setValue("current", arr[arr.length - 1].innerText);
  121. }
  122. }
  123.  
  124. function getLessionsInfo() {
  125. const arr = getDataForRules(lessionRules);
  126.  
  127. if (arr) {
  128. const lessions = [];
  129. for (let i = 0; i < arr.length; i++) {
  130. const className = "api20221120-" + i;
  131. if (arr[i].className.indexOf(className) === -1) {
  132. arr[i].className += " " + className;
  133. }
  134. lessions.push({ title: arr[i].innerText, className });
  135. }
  136. GM_setValue("lessions", lessions);
  137. }
  138. }
  139.  
  140. let findVideoCount = 0;
  141. const findVideoMaxCount = 3;
  142.  
  143. function getVideo() {
  144. const status = GM_getValue("play_end");
  145. if (GM_getValue("video") || status) {
  146. return;
  147. }
  148.  
  149. if (findVideoCount >= findVideoMaxCount) {
  150. if (status !== "not found") {
  151. GM_setValue("play_end", "not found");
  152. findVideoCount = 0;
  153. }
  154. return;
  155. }
  156.  
  157. if (document.querySelector("video")) {
  158. GM_setValue("video", document.URL);
  159.  
  160. setTimeout(() => {
  161. playCheck();
  162. }, 5000);
  163. } else {
  164. findVideoCount++;
  165. }
  166. }
  167.  
  168. function playCheck() {
  169. if (GM_getValue("play_end")) {
  170. return;
  171. }
  172.  
  173. const video = document.querySelector("video");
  174.  
  175. if (video) {
  176. video.muted = true;
  177. video.playbackRate = 2;
  178.  
  179. const currentTime = video.currentTime.toFixed(1);
  180. const totalTime = video.duration.toFixed(1);
  181. const nowTime = new Date();
  182.  
  183. console.log(`${nowTime.getHours()}:${nowTime.getMinutes()}:${nowTime.getSeconds()},当前进度:${currentTime}/${totalTime} ${(currentTime / totalTime).toFixed(1)},${document.URL}`);
  184.  
  185. if (video.ended || totalTime - currentTime < 35 * video.playbackRate) {
  186. video.onpause = null;
  187.  
  188. setTimeout(() => {
  189. GM_setValue("play_end", "over");
  190. }, 5000);
  191. } else {
  192. if (video.paused) {
  193. console.log("视频被暂停,继续播放!");
  194. video.play();
  195. video.onpause = function() {
  196. document.querySelector("video").play();
  197. }
  198. }
  199.  
  200. setTimeout(() => {
  201. playCheck();
  202. }, 5000);
  203. }
  204. } else {
  205. console.log("异常:找不到视频元素了");
  206. }
  207. }
  208.  
  209. function nextCheck() {
  210. const status = GM_getValue("play_end");
  211. const lessions = GM_getValue("lessions");
  212. if (status && lessions && lessions.length) {
  213. let currentText = GM_getValue("current");
  214. const lastCurrent = GM_getValue("last_current");
  215.  
  216. if (!lastCurrent || (currentText && lastCurrent !== currentText)) {
  217. GM_setValue("last_current", currentText);
  218. } else {
  219. currentText = lastCurrent;
  220. }
  221.  
  222. let index = GM_getValue("last_pos") ?? 0;
  223.  
  224. const newIndex = lessions.findIndex((e) => e.title === currentText);
  225.  
  226. if (newIndex !== -1 && newIndex > index) {
  227. index = newIndex;
  228. }
  229.  
  230. if (status === "not found" && !currentText) {
  231. index = 0;
  232. } else if (isInNoNeedAutoPlay()) {
  233. return;
  234. } else {
  235. index += 1;
  236. }
  237.  
  238. GM_setValue("last_pos", index);
  239.  
  240. if (index < lessions.length) {
  241. console.log(lessions[index]);
  242. GM_deleteValue("play_end");
  243. GM_deleteValue("video");
  244. document.querySelector("." + lessions[index].className).click();
  245. } else {
  246. alert("课程播放结束");
  247. return;
  248. }
  249. }
  250.  
  251. setTimeout(() => {
  252. nextCheck();
  253. }, 5000);
  254. }
  255.  
  256. function getResource() {
  257. getCurrentLession();
  258. getLessionsInfo();
  259. }
  260.  
  261. function init() {
  262. GM_deleteValue("play_end");
  263. GM_deleteValue("video");
  264. GM_deleteValue("current");
  265. GM_deleteValue("last_current");
  266. GM_deleteValue("lessions");
  267. }
  268.  
  269.  
  270. function initLessions() {
  271. GM_setValue("step", 1);
  272. GM_setValue("last_pos", 0);
  273. return true;
  274. }
  275.  
  276. function popupClose() {
  277. const tips = document.querySelector(".win-content");
  278.  
  279. if (tips && tips.innerText.indexOf("继续学习") > 0) {
  280. const btn = document.querySelector(".win-content .close-win-bt");
  281. btn && btn.click();
  282. }
  283.  
  284. const pop = document.querySelector("#pop");
  285.  
  286. pop && pop.querySelector(".pop_close").click();
  287. }
  288.  
  289. function work() {
  290. init();
  291.  
  292. setTimeout(() => {
  293. urlIn(lessionRules) && initLessions() && nextCheck();
  294. }, 5000);
  295.  
  296. setInterval(() => {
  297. urlIn(videoRules) && getVideo();
  298. }, 10000);
  299.  
  300. setInterval(() => {
  301. getResource();
  302. popupClose();
  303. }, 3000);
  304.  
  305. setInterval(() => {
  306. if (document.URL.indexOf("sccchina.net/student/") >= 0) {
  307. // 定时刷新
  308. setTimeout(() => {
  309. location.reload();
  310. }, (new Date().getSeconds() + 100) * 1234);
  311. }
  312. }, 60 * 15 * 1000);
  313. }
  314.  
  315. (function () {
  316. "use strict";
  317.  
  318. work();
  319. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址