网课后台自动连播
浏览器扩展
https://www.tampermonkey.net/
使用说明
- 安装浏览器扩展 Tampermonkey。
- 新建脚本,复制粘贴下述代码并保存。
- 打开网课页面,脚本会自动运行。
- 脚本会清除本地播放记录,强制从头播放视频,并在视频结束后自动跳转到下一个单元。
- 如果视频未能在30秒内加载,脚本会自动跳转到下一个单元,多半是因为进入了答题或讨论页面。
- 脚本会尝试在视频暂停时自动恢复播放,确保视频持续播放,适合后台学习。
脚本代码
学堂在线
// ==UserScript==
// @name 网课后台自动连播
// @namespace http://tampermonkey.net/
// @version 3.0
// @description 清除本地播放记录, 后台自动恢复播放, 结束后通过URL递增跳转。
// @author Chehil
// @match https://*.xuetangx.com/pro/lms/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
const SCRIPT_PREFIX = '[自动连播脚本]';
const PROGRESS_STORAGE_KEY = 'nhd';
let observer;
let timeoutId;
function log(message) {
console.log(SCRIPT_PREFIX, message);
}
function clearPlaybackProgress() {
try {
if (localStorage.getItem(PROGRESS_STORAGE_KEY)) {
localStorage.removeItem(PROGRESS_STORAGE_KEY);
log(`已清除本地播放记录 (Key: ${PROGRESS_STORAGE_KEY}),强制从头播放。`);
}
} catch (e) {
console.error(SCRIPT_PREFIX, '清除播放记录时出错:', e);
}
}
function jumpToNextVideo() {
try {
const url = new URL(window.location.href);
const match = url.pathname.match(/\/video\/(\d+)/);
if (match && match[1]) {
const currentId = parseInt(match[1], 10);
const nextId = currentId + 1;
url.pathname = url.pathname.replace(/\/video\/\d+/, `/video/${nextId}`);
log(`跳转到下一单元: ${url.href}`);
window.location.href = url.href;
} else {
console.error(SCRIPT_PREFIX, '无法从URL中解析出视频ID,跳转失败。');
}
} catch (e) {
console.error(SCRIPT_PREFIX, '构建下一个URL时发生错误:', e);
}
}
function setupVideo(video) {
if (timeoutId) {
clearTimeout(timeoutId);
log('已找到视频,超时跳转已取消。');
}
log('开始设置视频...');
video.addEventListener('canplay', () => {
log('视频准备就绪,强制从头开始并播放。');
video.muted = true;
video.currentTime = 0;
video.addEventListener('pause', () => {
if (!video.ended) {
log('检测到暂停,自动恢复播放...');
video.play().catch(e => log('恢复播放失败:', e.message));
} else {
log('视频正常结束,不恢复播放。');
}
});
video.play().catch(e => log('自动播放需要用户与页面首次交互,请点击页面。'));
video.addEventListener('ended', jumpToNextVideo);
}, { once: true });
if (observer) {
observer.disconnect();
log('任务完成,已停止DOM观察。');
}
}
function findAndSetupVideo() {
const videoElement = document.querySelector('video');
if (videoElement) {
setupVideo(videoElement);
return true;
}
return false;
}
clearPlaybackProgress();
window.addEventListener('DOMContentLoaded', () => {
if (findAndSetupVideo()) {
log('脚本初始化成功,已在页面加载时找到视频。');
} else {
log('页面加载时未发现视频,启动观察器等待视频加载...');
timeoutId = setTimeout(() => {
log('等待视频超时,自动跳转到下一个页面。');
if (observer) {
observer.disconnect();
log('因超时,已停止DOM观察。');
}
jumpToNextVideo();
}, 30000);
observer = new MutationObserver(() => {
findAndSetupVideo();
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
});
})();
雨课堂
// ==UserScript==
// @name 雨课堂看课助手 V2.0
// @namespace http://tampermonkey.net/
// @version 2.0
// @description 完美实现后台自动播放与防暂停功能,稳定挂机。
// @author Gemini
// @match https://www.yuketang.cn/v2/web/xcloud/video-student/*
// @match https://www.yuketang.cn/v2/web/studentLog/*
// @grant none
// @run-at document-documentElement
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let currentMode = null;
/**
* 视频播放页的全部逻辑
*/
function handleVideoPage() {
if (currentMode === 'video') return;
currentMode = 'video';
console.log('雨课堂助手(V2.0): 已进入视频播放模式,启动视频观察器...');
const observer = new MutationObserver((mutationsList, obs) => {
const videoElement = document.querySelector('video');
if (videoElement) {
console.log('观察器检测到视频元素,应用设置...');
const setupVideoPlayer = (video) => {
if (video.dataset.helperAttached) return;
video.dataset.helperAttached = 'true';
// 1. 立即尝试自动播放
video.muted = true;
video.play().catch(err => {});
// 2. 延迟“防暂停”功能的介入
setTimeout(() => {
console.log('冷静期结束,接管“防暂停”功能。');
// 只有在视频没有因为播放结束而暂停时,才强制播放
if (!video.ended && video.paused) {
video.play().catch(e => {});
}
// 附加上我们的“防暂停”卫士
video.addEventListener('pause', () => {
if (!video.ended) {
video.play().catch(e => {});
}
});
}, 2000); // 给予2秒的初始化冷静期
// 3. 播放结束后跳转
video.addEventListener('ended', () => {
const match = window.location.href.match(/video-student\/(\d+)/);
if (match && match[1]) {
window.location.href = `https://www.yuketang.cn/v2/web/studentLog/${match[1]}`;
}
});
};
setupVideoPlayer(videoElement);
obs.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
/**
* 课程目录页的全部逻辑
*/
function handleCatalogPage() {
if (currentMode === 'catalog') return;
currentMode = 'catalog';
console.log('雨课堂助手(V2.0): 已进入目录模式,启动混合引擎。');
let state = 'IDLE';
let observer;
const findAndClickNextVideo = (iframeDoc) => {
const searchContext = iframeDoc || document;
const contextName = iframeDoc ? 'iFrame' : '主页面';
console.log(`在 ${contextName} 中查找下一个未完成视频...`);
const allItems = searchContext.querySelectorAll('.leaf-detail');
if (allItems.length === 0) {
console.error("错误: 找不到任何课程条目 ('.leaf-detail')!");
return;
}
let nextVideoFound = false;
for (const item of allItems) {
if (item.querySelector('.icon--shipin')) {
const progressElement = item.querySelector('.progress-wrap .item');
const statusText = progressElement ? progressElement.textContent.trim() : '[无状态]';
if (statusText !== '已完成') {
const title = item.querySelector('.title') ? item.querySelector('.title').textContent.trim() : '未知标题';
console.log(`✅ 找到目标视频: "${title}" (状态: ${statusText})。准备点击...`);
item.click();
nextVideoFound = true;
break;
}
}
}
if (!nextVideoFound) {
console.log('🎉 所有视频均已完成。');
}
};
const pollForContent = () => {
console.log('启动内容轮询器,等待内容就绪...');
let checks = 0;
const maxChecks = 60;
const interval = setInterval(() => {
checks++;
try {
const iframe = document.querySelector('iframe');
if (iframe && iframe.contentWindow && iframe.contentWindow.document) {
const iframeDocument = iframe.contentWindow.document;
const loader = iframeDocument.querySelector('.loading-view');
if (!loader) {
clearInterval(interval);
console.log('轮询成功:加载动画已消失。额外等待2秒以同步最新进度...');
setTimeout(() => {
findAndClickNextVideo(iframeDocument);
}, 2000);
return;
}
}
} catch(e) { /* iFrame可能暂时无法访问 */ }
if (checks > maxChecks) {
console.error('轮询超时:加载动画一直存在。');
clearInterval(interval);
}
}, 500);
};
const mainTick = () => {
try {
if (state === 'IDLE' || state === 'WAITING_FOR_TAB') {
const tab = document.querySelector('#tab-content');
if (!tab) return;
if (tab.classList.contains('is-active')) {
console.log('状态机:任务完成,控制权移交至轮询器。');
state = 'POLLING';
observer.disconnect();
pollForContent();
} else if (state === 'IDLE') {
console.log('状态机:正在点击“学习内容”标签页...');
state = 'WAITING_FOR_TAB';
tab.click();
}
}
} catch (e) {
console.error('状态机出错:', e);
if(observer) observer.disconnect();
}
};
observer = new MutationObserver(mainTick);
observer.observe(document.documentElement, { childList: true, subtree: true });
mainTick();
}
/**
* 主路由:持续监控URL变化,决定运行哪个模式
*/
const mainRouter = () => {
const url = window.location.href;
if (url.includes('/video-student/') && currentMode !== 'video') {
handleVideoPage();
} else if (url.includes('/studentLog/') && currentMode !== 'catalog') {
handleCatalogPage();
}
};
new MutationObserver(mainRouter).observe(document.documentElement, { attributes: true, childList: true, subtree: true });
window.addEventListener('load', mainRouter);
})();