// ==UserScript==
// @name 雨课堂刷课助手
// @namespace http://tampermonkey.net/
// @version 1.0.2
// @description 针对雨课堂视频进行自动播放
// @author 风之子
// @license MIT
// @match *://*.yuketang.cn/*
// @icon http://niuwh.cn/favicon.ico
// @grant GM_addStyle
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js
// @downloadURL none
// ==/UserScript==
// 雨课堂刷课脚本
// 添加用户交互窗口
function addWindow() {
// 插入的交互HTML窗口
const outerHTML = `
- ⭐ 脚本支持:雨课堂官方版本,二倍速,自动播放
- 📢 手动点击进入要刷的课程目录,点击开始刷课,即可自动运行,如页面不动请联系作者
- ⚠️ 运行后请不要随意点击刷课窗口,可新开窗口,可最小化浏览器
- 💡 拖动上方标题栏可以进行拖拽哦!
`;
$('body').append(outerHTML);
// 添加css样式
function addStyle() {
let css = `
ul,
li,
p {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
}
.n_outer {
margin: 0;
padding: 0;
position: fixed;
top: 0;
left: 0;
min-width: 500px;
height: 250px;
background-color: #fff;
z-index: 99999;
box-shadow: 6px 4px 17px 2px #000000;
border-radius: 10px;
border: 1px solid #a3a3a3;
font-family: Avenir, Helvetica, Arial, sans-serif;
color: #636363;
}
.n_header {
text-align: center;
height: 40px;
background-color: #f7f7f7;
color: #000;
font-size: 18px;
line-height: 40px;
cursor: move;
border-radius: 10px 10px 0 0;
border-bottom: 2px solid #eee;
}
.n_body {
font-weight: bold;
font-size: 13px;
line-height: 26px;
height: 183px;
}
.n_body .n_infoAlert {
overflow-y: scroll;
height: 100%;
}
/* 滚动条整体 */
.n_body .n_infoAlert::-webkit-scrollbar {
height: 20px;
width: 7px;
}
/* 滚动条轨道 */
.n_body .n_infoAlert::-webkit-scrollbar-track {
--webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
border-radius: 10px;
background: #ffffff;
}
/* 滚动条滑块 */
.n_body .n_infoAlert::-webkit-scrollbar-thumb {
border-radius: 10px;
--webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: rgb(20, 19, 19, 0.6);
}
.n_footer {
position: absolute;
bottom: 0;
left: 0;
text-align: right;
height: 25px;
width: 100%;
background-color: #f7f7f7;
color: #c5c5c5;
font-size: 13px;
line-height: 25px;
border-radius: 0 0 10px 10px;
border-bottom: 2px solid #eee;
display: flex;
justify-content: space-between;
}
.n_footer button {
border-radius: 6px;
border: 0;
background-color: blue;
color: #fff;
cursor: pointer;
}
.n_footer button:hover {
background-color: yellow;
color: #000;
}
.n_footer #n_zanshang {
cursor: pointer;
position: relative;
color: red;
}
.n_footer #n_zanshang img {
position: absolute;
top: 30px;
left: -130px;
display: none;
width: 300px;
}
.n_footer #n_zanshang:hover img {
display: block;
}
`;
GM_addStyle(css);
}
addStyle();
// 窗口的拽拖逻辑
$('.n_header').mousedown(function (e) {
let innerLeft = e.offsetX,
innerTop = e.offsetY
$('body').mousemove(function (e) {
let left = e.clientX - innerLeft,
top = e.clientY - innerTop;
//获取body的页面可视宽高
var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
// 通过判断是否溢出屏幕
if (left <= 0) {
left = 0;
} else if (left >= clientWidth - $('.n_outer')[0].offsetWidth) {
left = clientWidth - $('.n_outer')[0].offsetWidth
}
if (top <= 0) {
top = 0
} else if (top >= clientHeight - $('.n_outer')[0].offsetHeight) {
top = clientHeight - $('.n_outer')[0].offsetHeight
}
$('.n_outer').css({
left: () => {
return left + 'px';
},
top: () => {
return top + 'px';
}
})
})
})
$('.n_header').mouseup(function (e) {
$('body').off();
})
$('#n_button').click(function () {
main();
$('#n_button').text('刷课中~');
})
$('#n_clear').click(function () {
localStorage.removeItem(location.href);
localStorage.removeItem('userCount')
})
}
// 脚本运行核心逻辑
function main() {
start();
// 视频播放速率,可选值 [1,1.25,1.5,2],默认为二倍速
const rate = 2;
// 向弹窗里追加信息
function alertMessage(message) {
$('.n_infoAlert').append(`${message}`);
}
setInterval(function () {
document.querySelector('.n_infoAlert').lastElementChild.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
}, 500)
// 视频自动加速逻辑
function speed() {
let keyt = '';
if (rate === 2 || rate === 1) {
keyt = "[keyt='" + rate + ".00']"
} else {
keyt = "[keyt='" + rate + "']"
}
function fun(className, selector) {
var mousemove = document.createEvent("MouseEvent");
mousemove.initMouseEvent("mousemove", true, true, unsafeWindow, 0, 10, 10, 10, 10, 0, 0, 0, 0, 0, null);
console.log(document.getElementsByClassName(className)[0]);
document.getElementsByClassName(className)[0].dispatchEvent(mousemove);
document.querySelector(selector).click();
alertMessage('已开始两倍速播放');
}
fun('xt_video_player_speed', keyt)
}
// 判断页面类型执行不同的操作
function start() {
const url = location.host;
let type = url.split('.')[0];
alertMessage(`正在为您匹配${url}的处理逻辑...`);
if (type === 'www') {
www();
} else {
changjiang(type);
}
}
// www.yuketang.cn页面的处理逻辑
function www() {
alertMessage('已匹配到www.yuketang.cn,正在处理...');
// 用于判断不同的课程
let baseUrl = location.href;
// 根据客户端记录的URL判别刷到那一集了,不影响第一批用户的刷课进度。
if (localStorage.getItem('classIndex')) {
localStorage.setItem(baseUrl, +localStorage.getItem('classIndex'));
localStorage.removeItem('classIndex');
}
let count = +localStorage.getItem(baseUrl) || 0;
alertMessage(`检测到已经播放到${count}集...`);
let classList = [];
// 用于标记视频是否播放完毕
let play = true;
// 主函数
function main() {
autoSlide(count).then(() => {
let list = document.querySelector('.logs-list').childNodes;
alertMessage('刷课状态:第' + (count + 1) + '个/' + list.length + '个');
classList[count] = list[count]?.querySelector('.content-box')?.querySelector('section');
let classInfo = classList[count]?.querySelector('.tag')?.querySelector('use')?.getAttribute('xlink:href');
if (classInfo?.includes('shipin') && play === true) { // 视频处理
play = false;
classList[count].click();
setTimeout(() => {
alertMessage('第' + (count + 1) + '个:进入了视频区');
speed();
let progress = document.querySelector('.progress-wrap').querySelector('.text');
let timer1 = setInterval(() => {
if (progress.innerHTML.includes('100%') || progress.innerHTML.includes('99%') || progress.innerHTML.includes('98%')) {
count++;
localStorage.setItem(baseUrl, count);
play = true;
history.back();
main();
clearInterval(timer1);
}
}, 10000);
}, 3000)
// 批量处理
} else if (classInfo?.includes('piliang') && play === true) { // 批量处理
let zhankai = classList[count].querySelector('.sub-info').querySelector('.gray').querySelector('span');
sync();
async function sync() {
await zhankai.click();
setTimeout(() => {
alertMessage('第' + (count + 1) + '个:进入了批量区');
localStorage.getItem('userCount') ? localStorage.getItem('userCount') : localStorage.setItem('userCount', 0);
// 保存所有视频
let a = list[count].querySelector('.leaf_list__wrap').querySelectorAll('.activity__wrap');
let count1 = localStorage.getItem('userCount');
bofang();
function bofang() {
let classInfo1 = a[count1]?.querySelector('.tag').querySelector('use').getAttribute('xlink:href');
let play = true;
if (classInfo1?.includes('shipin') && play === true) {
play = false;
a[count1].click();
alertMessage(`开始播放视频`);
// 延迟3秒后加速
setTimeout(() => {
speed();
}, 3000);
let timer = setInterval(() => {
let progress = document.querySelector('.progress-wrap').querySelector('.text');
if (progress.innerHTML.includes('100%') || progress.innerHTML.includes('99%') || progress.innerHTML.includes('98%')) {
count1++;
localStorage.setItem('userCount', count1);
clearInterval(timer);
alertMessage(`视频播放完毕`);
history.back();
setTimeout(() => {
bofang();
}, 2000);
}
}, 3000)
} else if (classInfo1 && !classInfo1.includes('shipin') && play === true) {
alertMessage('不是视频');
count1++;
localStorage.setItem('userCount', count1);
bofang();
} else if (count1 === a.length && play === true) {
alertMessage('合集播放完毕');
count++;
localStorage.setItem('userCount', count1);
localStorage.setItem(baseUrl, count);
main();
}
}
}, 2000)
}
} else if (classInfo?.includes('ketang') && play === true) {
alertMessage('第' + (count + 1) + '个:进入了课堂区');
play = false;
classList[count].click();
setTimeout(() => {
let playBack = document.querySelector('.playback');
if (playBack) { // 存在回放按钮时进入详情页
playBack.click();
setTimeout(() => {
// 内容为视频的逻辑
if (document.querySelector('video')) {
function isComplate() {
let videoTime = document.querySelector('.video__time').innerHTML.toString();
let currentTime = videoTime.split('/')[0];
let totalTime = videoTime.split('/')[1];
if (currentTime == totalTime || currentTime == '00:00' || currentTime == '00:00:00') {
count++;
localStorage.setItem(baseUrl, count);
play = true;
history.go(-2);
main();
clearInterval(timer);
}
}
let timer = setInterval(() => {
isComplate();
}, 10000)
}
// 内容为音频的逻辑
if (document.querySelector('audio')) {
function isComplate() {
let mainArea = document.querySelector('.mainArea');
let currentTime = mainArea.querySelectorAll('span')[0].innerHTML.toString();
let totalTime = mainArea.querySelectorAll('span')[1].innerHTML.toString();
if (currentTime == totalTime || currentTime == '00:00' || currentTime == '00:00:00') {
count++;
localStorage.setItem(baseUrl, count);
play = true;
history.go(-2);
main();
clearInterval(timer);
}
}
let timer = setInterval(() => {
isComplate();
}, 10000)
}
}, 3000)
} else { // 不存在回放按钮时退出
count++;
localStorage.setItem(baseUrl, count);
play = true;
history.go(-1);
main();
}
}, 3000)
} else if (count === list.length && play === true) {
alertMessage('课程刷完了');
$('#n_buttoni').text('刷完了~');
localStorage.setItem(baseUrl, 0);
return;
} else if (!(classInfo.includes('shipin') || classInfo.includes('piliang')) && play === true) {
// 视频和批量都不是的时候跳过,此处可以优化
alertMessage('第' + (count + 1) + '个:不是视频,已跳过');
count++;
localStorage.setItem(baseUrl, count);
main();
}
})
}
// 根据视频集数,自动下拉刷新集数
async function autoSlide(count) {
let frequency = parseInt((count + 1) / 20) + 1;
for (let i = 0; i < frequency; i++) {
await new Promise((resolve, reject) => {
setTimeout(() => {
document.querySelector('.viewContainer').scrollTop = document.querySelector('.el-tab-pane').scrollHeight;
resolve();
}, 1000)
})
}
}
main();
}
// 长江雨课堂页面的处理逻辑
function changjiang(userType) {
alertMessage(`已匹配到${userType}.yuketang.cn,正在处理...`);
"use strict";
// 多长时间刷新一下页面,单位 分钟
const reloadTime = 10;
// 视频播放速率,可选值 [1,1.25,1.5,2],默认为二倍速
const rate = 2;
window.onload = function () {
// 网课页面跳转
function getElTooltipItemList() {
return document.getElementsByClassName("el-tooltip leaf-detail");
}
function getElTooltipList() {
return document.getElementsByClassName("el-tooltip f12 item");
}
// 静音
function claim() {
$(
"#video-box > div > xt-wrap > xt-controls > xt-inner > xt-volumebutton > xt-icon"
).click();
}
function fun(className, selector) {
var mousemove = document.createEvent("MouseEvent");
mousemove.initMouseEvent("mousemove", true, true, unsafeWindow, 0, 10, 10, 10, 10, 0, 0, 0, 0, 0, null);
document.getElementsByClassName(className)[0].dispatchEvent(mousemove);
document.querySelector(selector).click();
}
// 加速
function speed() {
let keyt = '';
if (rate === 2 || rate === 1) {
keyt = "[keyt='" + rate + ".00']"
} else {
keyt = "[keyt='" + rate + "']"
}
fun("xt_video_player_speed", keyt);
}
const getElementInterval = setInterval(function () {
const elTooltipList = getElTooltipList();
const elTooltipItemList = getElTooltipItemList();
if (elTooltipList) {
for (let index = 0; index < elTooltipList.length; index++) {
const element = elTooltipList[index];
const textContent = element.textContent;
//const textContent = ''
if (textContent === "未开始" || textContent === "未读") {
// 判断是否是习题
if (elTooltipItemList[index].innerText.indexOf('习题') != -1) {
continue;
}
// 判断是否已过学习时间
if (elTooltipItemList[index].children[1].children[0].innerText.indexOf("已过") != -1) {
continue;
}
window.clearInterval(getElementInterval);
GM_setValue("rowUrl", window.location.href.toString());
// 网课页面跳转
elTooltipItemList[index].click();
window.close();
break;
}
}
}
}, 1000);
let video;
const videoPlay = setInterval(function () {
// 获取播放器
video = document.getElementsByClassName("xt_video_player")[0];
if (!video) {
return;
}
setTimeout(function () {
// 视频开始5s之后再开启倍速
speed()
}, 5000);
claim();
window.clearInterval(videoPlay);
}, 500);
// 是否播放完成的检测
const playTimeOut = setInterval(function () {
if (!video) {
return;
}
video.play();
// 没有静音
if (video.volume != 0) {
claim();
}
const completeness = $(
"#app > div.app-wrapper > div.wrap > div.viewContainer.heightAbsolutely > div > div.video-wrap > div > div > section.title > div.title-fr > div > div > span"
);
if (!completeness) {
return;
}
if (typeof completeness[0] == "undefined") {
return;
}
const videoText = completeness[0].innerHTML
if (videoText) {
let str = videoText.toString();
const succ = str.substring(4, str.length - 1);
const succNum = parseInt(succ);
if (succ >= 95) {
const url = GM_getValue("rowUrl");
if (url) {
window.clearInterval(playTimeOut);
window.location.replace(url);
}
}
}
}, 1000);
// 是否为阅读类型
const readInterval = setInterval(function () {
const read = $(
"#app > div.app-wrapper > div.wrap > div.viewContainer.heightAbsolutely > div > div.graph-wrap > div > div > section.title > div.title-fr > div > div"
);
if (!read) {
return
}
if (typeof read[0] == "undefined") {
return;
}
const readText = read[0].innerHTML
if (readText) {
if (readText.toString() === '已读') {
window.clearInterval(readInterval);
window.location.replace(GM_getValue("rowUrl"));
}
}
}, 1000);
// 为了防止页面假死,定时刷新一下页面
setTimeout(function () {
// 如果保存了课程列表路径就回退的课程列表页面
if (GM_getValue("rowUrl")) {
window.location.replace(GM_getValue("rowUrl"));
}
location.reload()
}, reloadTime * 60 * 1000);
};
}
// 其他不同类型的DOM页面的处理逻辑,待完善...
}
// 油猴执行文件
(function () {
'use strict';
addWindow();
})();