// ==UserScript==
// @name 湖南开放大学自动刷课(解决自动播放问题版)
// @namespace http://tampermonkey.net/
// @version 2.0
// @description 解决自动播放被阻止问题,确保视频持续播放
// @author 唐跃翔
// @match *.hnsydwpx.cn/*
// @grant GM_addStyle
// @grant GM_log
// @grant GM_notification
// @run-at document-idle
// @license MIT
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
// 配置参数
const config = {
checkInterval: 5000, // 状态检查间隔(ms)
interactionWait: 3000, // 等待用户交互时间(ms)
maxRetry: 300, // 最大重试次数
debugMode: true // 调试模式
};
// 添加UI指示器
GM_addStyle(`
.script-indicator {
position: fixed;
top: 20px;
right: 20px;
background: #4CAF50;
color: white;
padding: 8px 15px;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
z-index: 9999;
font-size: 30px;
}
.script-indicator.error {
background: #F44336;
}
`);
const indicator = document.createElement('div');
indicator.className = 'script-indicator';
indicator.textContent = '阿唐的刷课脚本已经启动啦';
document.body.appendChild(indicator);
// 解决自动播放问题的视频控制器
class VideoController {
constructor() {
this.player = null;
this.retryCount = 0;
this.isWaitingInteraction = false;
this.init();
}
async init() {
try {
this.player = await this.waitForElement('#coursePlayer video');
this.addFakeInteractionLayer();
this.startMonitoring();
this.notify('视频控制器已启动');
} catch (error) {
this.notify(`初始化失败: ${error.message}`, 'error');
indicator.classList.add('error');
indicator.textContent = '脚本初始化失败';
}
}
// 添加伪交互层解决自动播放限制
addFakeInteractionLayer() {
GM_addStyle(`
.interaction-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: transparent;
z-index: 9998;
cursor: pointer;
}
.interaction-notice {
position: fixed;
bottom: 80px;
right: 20px;
background: rgba(0,0,0,0.7);
color: white;
padding: 10px 15px;
border-radius: 4px;
z-index: 9999;
max-width: 300px;
font-size: 14px;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.5);
}
`);
// 创建覆盖层
const overlay = document.createElement('div');
overlay.className = 'interaction-overlay';
overlay.onclick = () => this.handleUserInteraction();
document.body.appendChild(overlay);
// 添加提示
const notice = document.createElement('div');
notice.className = 'interaction-notice';
notice.innerHTML = '点击页面任意位置激活自动播放功能
3秒后自动尝试播放';
document.body.appendChild(notice);
this.isWaitingInteraction = true;
setTimeout(() => {
if (this.isWaitingInteraction) {
this.handleUserInteraction();
notice.innerHTML = '已自动激活播放功能';
setTimeout(() => notice.remove(), 2000);
}
}, config.interactionWait);
}
// 处理用户交互
handleUserInteraction() {
if (!this.isWaitingInteraction) return;
this.isWaitingInteraction = false;
document.querySelector('.interaction-overlay')?.remove();
document.querySelector('.interaction-notice')?.remove();
// 首次播放需要用户触发
this.playVideo().then(() => {
this.notify('用户交互后自动播放已启动');
}).catch(error => {
this.notify(`交互后播放失败: ${error}`, 'error');
});
}
// 开始监控
startMonitoring() {
this.monitorInterval = setInterval(() => {
if (!this.isWaitingInteraction && this.player.paused && !this.player.ended) {
this.playVideo();
}
}, config.checkInterval);
// 监听视频事件
this.player.addEventListener('pause', () => {
if (!this.isWaitingInteraction) {
this.notify('检测到视频暂停,尝试恢复');
this.playVideo();
}
});
this.player.addEventListener('ended', () => {
this.notify('当前视频播放完毕');
this.nextChapter();
});
}
// 播放视频(处理自动播放限制)
async playVideo() {
if (this.retryCount >= config.maxRetry) {
this.notify('达到最大重试次数,请手动点击播放', 'error');
GM_notification({
title: '自动播放被阻止',
text: '请手动点击播放按钮',
timeout: 5000
});
indicator.classList.add('error');
indicator.textContent = '自动播放被阻止';
return;
}
try {
const playPromise = this.player.play();
if (playPromise !== undefined) {
await playPromise;
this.retryCount = 0;
this.notify('不用慌,视频已经播放成功啦!哈哈ヾ(≧▽≦*)o');
indicator.classList.remove('error');
indicator.textContent = '阿唐的自动刷课脚本运行中';
}
} catch (error) {
this.retryCount++;
this.notify(`播放失败 (${this.retryCount}/${config.maxRetry}): ${error}`, 'error');
// 尝试通过点击按钮播放
const playBtn = await this.waitForElement('.xgplayer-play', document, 1000).catch(() => null);
if (playBtn) {
playBtn.click();
this.notify('已尝试点击播放按钮');
}
// 直接尝试静音播放
if (this.retryCount >= 1) {
this.player.muted = true;
this.player.play().catch(e => this.notify(`静音播放也失败: ${e}`, 'error'));
}
}
}
// 切换到下一章节
nextChapter() {
const items = document.querySelectorAll('li[data-v-469a21ef]');
for (let item of items) {
const progress = item.querySelector('.progress')?.textContent.trim();
if (progress != '100%'&&progress != '99%'&&progress != '98%'&&progress != '97%'&&progress != '96%'&&progress != '95%'&&progress != '94%') {
item.click();
this.notify(`切换到未完成章节: ${item.querySelector('.name').textContent}`);
// 点击后等待视频加载并开始播放
setTimeout(() => this.playVideo(), 2000);
return;
}
}
this.notify('所有章节已完成,返回课程中心');
window.location.href = 'https://www.hnsydwpx.cn/mineCourse';
}
// 等待元素出现
waitForElement(selector, parent = document, timeout = 10000) {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const check = () => {
const el = parent.querySelector(selector);
if (el) {
resolve(el);
} else if (Date.now() - startTime < timeout) {
setTimeout(check, 500);
} else {
reject(new Error(`元素未找到: ${selector}`));
}
};
check();
});
}
// 弹窗通知
notify(message, type = 'info') {
if (config.debugMode) {
GM_notification({
title: `[唐跃翔的视频控制]`,
text: message,
timeout: 5000
});
}
}
}
// 页面初始化
function check2425(items,index) {
items[index].click();
setTimeout(() => {
//先检查元素是否存在
const button = document.querySelector('.el-tab-pane .el-row .el-button');
if (button) {
if(index)console.log('2024年章节未完成,程序优先学习2024年章节!');
try {
button.click();
} catch (clickError) {
console.log('点击操作失败,执行备用方案:', clickError);
}
} else {
console.log('2024已经完成,学习2025年课程!');
if(!index)console.log('全部完成啦!');
else check2425(items,0);
}
new VideoController();
},2000);
}
function init() {
// 只在对视频页面启用
if (location.pathname.includes('/videoPlayback') ||
location.pathname.includes('/getcourseDetails')) {
new VideoController();
}
if(location.pathname.includes('/mineCourse')){
setTimeout(() => {
console.log('go to my course')
const items = document.querySelectorAll('.years div[data-v-fa6b87b5]');
check2425(items,1);
}, 2000);
}
}
// 启动脚本
if (document.readyState === 'complete') {
init();
} else {
window.addEventListener('load', init);
}
})();