NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name SCU eCourse 自动连续播放
// @namespace scu-ecourse-autoplay
// @version 1.0.0
// @description 四川大学 eCourse:自动播放当前视频,播放结束后自动进入下一学习内容
// @author ChatGPT
// @license MIT
// @match https://ecourse.scu.edu.cn/learn/course/detail/mooc/courseWare/*
// @match https://ecourse.scu.edu.cn/learn/course/mooc/*
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
let enabled = GM_getValue('scu_autoplay_enabled', true);
let currentVideo = null;
let observerTimer = null;
let nextClickLock = false;
console.log('[SCU自动连播] 脚本已加载');
console.log('[SCU自动连播] 当前地址:', location.href);
// ===============================
// 后台播放辅助
// ===============================
function enableBackgroundPlayback() {
try {
Object.defineProperty(document, 'hidden', {
configurable: true,
get: () => false
});
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: () => 'visible'
});
console.log('[SCU自动连播] 后台播放辅助已启用');
} catch (e) {
console.log('[SCU自动连播] 无法修改页面可见状态:', e);
}
}
// ===============================
// 查找视频
// ===============================
function findVideo() {
const videos = Array.from(document.querySelectorAll('video'));
if (!videos.length) {
return null;
}
// 优先寻找当前可见的视频
const visibleVideo = videos.find(video => {
const rect = video.getBoundingClientRect();
return (
rect.width > 0 &&
rect.height > 0
);
});
return visibleVideo || videos[0];
}
// ===============================
// 自动播放
// ===============================
async function tryPlay(video) {
if (!enabled || !video) {
return;
}
if (!video.paused) {
updateStatus('正在播放');
return;
}
try {
// 首先尝试正常播放,尽量保留声音
await video.play();
console.log('[SCU自动连播] 自动播放成功');
updateStatus('正在播放');
} catch (e) {
console.log(
'[SCU自动连播] 浏览器阻止有声自动播放,尝试静音播放'
);
try {
video.muted = true;
await video.play();
console.log(
'[SCU自动连播] 静音自动播放成功'
);
updateStatus('播放中(静音)');
} catch (e2) {
console.warn(
'[SCU自动连播] 自动播放失败,需要手动点击一次播放',
e2
);
updateStatus('请手动点一次播放');
}
}
}
// ===============================
// 查找“下一节”
// ===============================
function findNextButton() {
// 四川大学平台原脚本使用的按钮
const selectors = [
'.video_btn.next_video_btn',
'.next_video_btn',
'button.next_video_btn',
'[class*="next_video"]',
'[title*="下一个"]',
'[title*="下一节"]',
'[aria-label*="下一个"]',
'[aria-label*="下一节"]'
];
for (const selector of selectors) {
const elements =
document.querySelectorAll(selector);
for (const el of elements) {
if (isUsableButton(el)) {
return el;
}
}
}
// 如果平台修改了 class,
// 再通过按钮文字寻找
const candidates =
document.querySelectorAll(
'button, a, [role="button"], .el-button'
);
for (const el of candidates) {
const text =
(el.innerText || el.textContent || '')
.trim()
.replace(/\s+/g, '');
if (
text === '下一个' ||
text === '下一节' ||
text.includes('下一个学习内容') ||
text.includes('下一节')
) {
if (isUsableButton(el)) {
return el;
}
}
}
return null;
}
function isUsableButton(el) {
if (!el) return false;
const style =
window.getComputedStyle(el);
if (
style.display === 'none' ||
style.visibility === 'hidden'
) {
return false;
}
if (
el.disabled ||
el.getAttribute('aria-disabled') === 'true' ||
el.classList.contains('disabled') ||
el.classList.contains('is-disabled')
) {
return false;
}
return true;
}
// ===============================
// 视频结束
// ===============================
function onVideoEnded() {
if (!enabled || nextClickLock) {
return;
}
console.log(
'[SCU自动连播] 视频播放结束'
);
updateStatus('视频结束,寻找下一节');
const button =
findNextButton();
if (!button) {
console.log(
'[SCU自动连播] 没找到下一节按钮'
);
updateStatus('未找到下一节');
return;
}
nextClickLock = true;
console.log(
'[SCU自动连播] 找到下一节按钮:',
button
);
updateStatus('正在进入下一节');
setTimeout(() => {
button.click();
console.log(
'[SCU自动连播] 已点击下一节'
);
// 防止连续点击
setTimeout(() => {
nextClickLock = false;
scanPage();
}, 2000);
}, 800);
}
// ===============================
// 绑定视频
// ===============================
function bindVideo(video) {
if (!video) {
return;
}
if (currentVideo !== video) {
if (currentVideo) {
currentVideo.removeEventListener(
'ended',
onVideoEnded
);
}
currentVideo = video;
video.addEventListener(
'ended',
onVideoEnded
);
console.log(
'[SCU自动连播] 已识别新播放器',
video
);
}
tryPlay(video);
}
// ===============================
// 页面扫描
// ===============================
function scanPage() {
if (!enabled) {
updateStatus('已关闭');
return;
}
const video =
findVideo();
if (!video) {
console.log(
'[SCU自动连播] 当前页面暂未发现 video'
);
updateStatus('寻找视频…');
return;
}
bindVideo(video);
}
// ===============================
// 监听页面动态变化
// ===============================
function startObserver() {
const observer =
new MutationObserver(() => {
clearTimeout(observerTimer);
observerTimer =
setTimeout(() => {
scanPage();
}, 700);
});
observer.observe(
document.documentElement,
{
childList: true,
subtree: true
}
);
}
// ===============================
// UI
// ===============================
let statusText;
let toggleButton;
function createUI() {
const box =
document.createElement('div');
box.style.cssText = `
position: fixed;
right: 20px;
bottom: 20px;
z-index: 2147483647;
background: rgba(30,30,30,.90);
color: white;
padding: 10px 12px;
border-radius: 8px;
font-size: 13px;
font-family: Arial, sans-serif;
box-shadow: 0 3px 12px rgba(0,0,0,.25);
`;
toggleButton =
document.createElement('button');
toggleButton.style.cssText = `
border: none;
padding: 6px 10px;
border-radius: 5px;
cursor: pointer;
margin-bottom: 5px;
`;
statusText =
document.createElement('div');
statusText.style.cssText = `
font-size: 12px;
opacity: .85;
min-width: 120px;
`;
toggleButton.onclick = () => {
enabled = !enabled;
GM_setValue(
'scu_autoplay_enabled',
enabled
);
updateToggle();
if (enabled) {
scanPage();
} else {
updateStatus('已关闭');
}
};
box.appendChild(toggleButton);
box.appendChild(statusText);
document.body.appendChild(box);
updateToggle();
}
function updateToggle() {
if (!toggleButton) return;
if (enabled) {
toggleButton.innerText =
'SCU 连播:开';
toggleButton.style.background =
'#38a169';
toggleButton.style.color =
'white';
} else {
toggleButton.innerText =
'SCU 连播:关';
toggleButton.style.background =
'#e53e3e';
toggleButton.style.color =
'white';
}
}
function updateStatus(text) {
if (statusText) {
statusText.innerText =
'状态:' + text;
}
}
// ===============================
// 启动
// ===============================
function main() {
console.log(
'[SCU自动连播] 初始化'
);
createUI();
enableBackgroundPlayback();
startObserver();
// 页面刚打开时多检测几次
scanPage();
setTimeout(scanPage, 1000);
setTimeout(scanPage, 3000);
setTimeout(scanPage, 6000);
}
if (
document.readyState === 'loading'
) {
document.addEventListener(
'DOMContentLoaded',
main,
{ once: true }
);
} else {
main();
}
})();