// ==UserScript==
// @name 人工智能2026观看脚本V1.4 -仅供学习
// @namespace http://tampermonkey.net/
// @version 1.4
// @description 修复点击开始没反应+智慧教育平台最新适配+终极防切屏+防暂停
// @author nuoya
// @match *://*.smartedu.cn/*
// @match https://higher.smartedu.cn/*
// @match https://service.icourses.cn/*
// @grant GM_addStyle
// @grant GM_openInTab
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// ==================== 【终极防切屏】彻底解决最小化/切屏停止问题 ====================
try {
// 1. 拦截所有切屏相关事件
const blockEvents = ['visibilitychange', 'webkitvisibilitychange', 'blur', 'pagehide', 'focusout'];
blockEvents.forEach(eventName => {
window.addEventListener(eventName, e => e.stopImmediatePropagation(), true);
document.addEventListener(eventName, e => e.stopImmediatePropagation(), true);
});
// 2. 重写页面可见性属性(最关键!99%的网站通过这个检测切屏)
Object.defineProperty(document, 'visibilityState', {
get: () => 'visible',
configurable: true
});
Object.defineProperty(document, 'hidden', {
get: () => false,
configurable: true
});
// 3. 重写窗口焦点属性
Object.defineProperty(window, 'hasFocus', {
value: () => true,
configurable: true
});
console.log("🚀 终极挂课脚本 V5.4.1 已启动!切屏/最小化检测已完全拦截");
} catch (e) {
console.warn("⚠️ 部分防切屏功能受限,但核心播放不受影响");
}
// ==================== 样式定义 ====================
GM_addStyle(`
#auto-study-panel {
position: fixed;
top: 20px;
right: 20px;
width: 340px;
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
border-radius: 16px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
z-index: 99999999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
color: #ffffff;
overflow: hidden;
transition: all 0.3s ease;
user-select: none;
border: 1px solid rgba(255, 255, 255, 0.1);
}
#auto-study-panel.minimized {
width: 60px;
height: 60px;
border-radius: 50%;
}
#auto-study-panel.minimized .panel-content {
display: none;
}
#auto-study-panel.minimized .panel-header {
height: 60px;
padding: 0;
justify-content: center;
}
#auto-study-panel.minimized .panel-title {
display: none;
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
background: rgba(255, 255, 255, 0.05);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
cursor: move;
}
.panel-title {
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.panel-title::before {
content: '';
width: 8px;
height: 8px;
background: #10b981;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.panel-controls {
display: flex;
gap: 8px;
}
.panel-btn {
width: 28px;
height: 28px;
border: none;
border-radius: 8px;
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
font-size: 14px;
}
.panel-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.panel-btn.minimize-btn {
font-size: 18px;
}
.panel-content {
padding: 20px;
}
.status-section {
margin-bottom: 20px;
}
.status-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.status-label {
color: #94a3b8;
}
.status-value {
font-weight: 500;
color: #ffffff;
}
.status-value.running {
color: #10b981;
}
.status-value.paused {
color: #f59e0b;
}
.status-value.stopped {
color: #ef4444;
}
.progress-bar {
width: 100%;
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
overflow: hidden;
margin-top: 8px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #10b981, #06b6d4);
border-radius: 4px;
transition: width 0.3s ease;
}
.settings-section {
margin-bottom: 20px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.settings-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: #94a3b8;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.setting-label {
font-size: 13px;
color: #cbd5e1;
}
.setting-input {
width: 70px;
padding: 6px 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
background: rgba(255, 255, 255, 0.05);
color: #ffffff;
font-size: 13px;
text-align: center;
}
.setting-input:focus {
outline: none;
border-color: #10b981;
}
.action-buttons {
display: flex;
gap: 10px;
margin-bottom: 16px;
}
.action-btn {
flex: 1;
padding: 12px 0;
border: none;
border-radius: 10px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.action-btn.expand-btn {
background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%);
color: #ffffff;
}
.action-btn.expand-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
}
.action-btn.start-btn {
background: linear-gradient(135deg, #10b981 0%, #06b6d4 100%);
color: #0a0a0a;
}
.action-btn.start-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
}
.action-btn.pause-btn {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: #0a0a0a;
}
.action-btn.pause-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);
}
.action-btn.stop-btn {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
color: #ffffff;
}
.action-btn.stop-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
/* 推荐课程按钮样式 */
.recommend-section {
margin-bottom: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.recommend-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: #94a3b8;
}
.recommend-buttons {
display: flex;
flex-direction: column;
gap: 8px;
}
.recommend-btn {
width: 100%;
padding: 10px 0;
border: none;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
}
.recommend-btn.general {
background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%);
color: #ffffff;
}
.recommend-btn.ai-x {
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
color: #ffffff;
}
.recommend-btn.llm {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: #0a0a0a;
}
.recommend-btn:hover {
transform: translateY(-1px);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
}
.log-section {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.log-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 10px;
color: #94a3b8;
display: flex;
justify-content: space-between;
align-items: center;
}
.clear-log-btn {
font-size: 12px;
color: #94a3b8;
background: none;
border: none;
cursor: pointer;
}
.clear-log-btn:hover {
color: #ffffff;
}
.log-container {
height: 140px;
overflow-y: auto;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 10px;
font-size: 12px;
font-family: 'Consolas', monospace;
color: #e2e8f0;
}
.log-container::-webkit-scrollbar {
width: 4px;
}
.log-container::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
}
.log-item {
margin-bottom: 4px;
line-height: 1.4;
}
.log-item.info {
color: #06b6d4;
}
.log-item.success {
color: #10b981;
}
.log-item.warning {
color: #f59e0b;
}
.log-item.error {
color: #ef4444;
}
.platform-badge {
display: inline-block;
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
margin-left: 6px;
background: #10b981;
color: #0a0a0a;
}
`);
// ==================== 全局变量 ====================
let isRunning = false;
let isPaused = false;
let currentIndex = 0;
let totalVideos = 0;
let videoItems = [];
let pauseResolve = null;
let stopRequested = false;
let isAllExpanded = false;
let isSmartEduPlatform = false; // 高等教育智慧教育平台标识
// 默认配置(通用模式使用,智慧教育平台强制使用你指定的参数)
const config = {
scrollDelay: 1500,
loadDelay: 5000,
closeDelay: 3000,
checkInterval: 2000,
maxTimeout: 900
};
// 推荐课程配置
const recommendCourses = [
{
name: "人工智能应用导论(通识课)",
url: "https://higher.smartedu.cn/course/agc3/69ba53600976b58e126c4d8e?type=training",
class: "general"
},
{
name: "自动驾驶场景设计(AI+X)",
url: "https://higher.smartedu.cn/course/agc3/69b56a1ff74ce762ce352749?type=training",
class: "ai-x"
},
{
name: "DeepSeek大模型前沿进展",
url: "https://higher.smartedu.cn/course/lmc/67d7cf98625fca5f6cf9076e",
class: "llm"
}
];
// ==================== UI创建 ====================
function createPanel() {
// 防止重复创建面板
if (document.getElementById('auto-study-panel')) {
console.log("⚠️ 面板已存在,跳过创建");
return;
}
const panel = document.createElement('div');
panel.id = 'auto-study-panel';
panel.innerHTML = `
🔥 推荐课程快速跳转
${recommendCourses.map(course =>
``
).join('')}
`;
document.body.appendChild(panel);
bindEvents();
makeDraggable(panel);
log('info', '✅ 终极挂课脚本 V5.4.1 已加载完成');
log('success', '🛡️ 切屏/最小化检测已完全拦截,后台挂机零中断');
// 平台检测提示
if (isSmartEduPlatform) {
log('success', '🎯 检测到高等教育智慧教育平台,已强制启用figure标签专属脚本');
log('info', 'ℹ️ 完全按照你提供的脚本逻辑执行,无需展开章节');
} else {
log('info', 'ℹ️ 通用模式已启用,请先点击"展开全部"再开始学习');
}
console.log("✅ UI面板创建成功,开始按钮已绑定事件");
}
// ==================== 事件绑定(修复:确保事件正确绑定) ====================
function bindEvents() {
// 先移除所有可能存在的旧事件
const startBtn = document.getElementById('start-btn');
const newStartBtn = startBtn.cloneNode(true);
startBtn.parentNode.replaceChild(newStartBtn, startBtn);
// 重新绑定事件
newStartBtn.addEventListener('click', () => {
console.log("🔘 开始学习按钮被点击!");
log('info', '🔘 开始学习按钮被点击');
startAutoStudy();
});
document.getElementById('expand-btn').addEventListener('click', expandAllChapters);
document.getElementById('pause-btn').addEventListener('click', togglePause);
document.getElementById('stop-btn').addEventListener('click', stopAutoStudy);
document.getElementById('minimize-btn').addEventListener('click', toggleMinimize);
document.getElementById('clear-log-btn').addEventListener('click', clearLog);
// 绑定推荐课程按钮事件
document.querySelectorAll('.recommend-btn').forEach(btn => {
btn.addEventListener('click', () => {
const url = btn.getAttribute('data-url');
GM_openInTab(url, { active: true });
log('info', `🔗 正在打开推荐课程: ${btn.textContent}`);
});
});
// 配置输入框事件(仅通用模式生效)
document.getElementById('scroll-delay').addEventListener('change', (e) => {
if (!isSmartEduPlatform) config.scrollDelay = parseInt(e.target.value) || 1500;
});
document.getElementById('load-delay').addEventListener('change', (e) => {
if (!isSmartEduPlatform) config.loadDelay = parseInt(e.target.value) || 5000;
});
document.getElementById('close-delay').addEventListener('change', (e) => {
if (!isSmartEduPlatform) config.closeDelay = parseInt(e.target.value) || 3000;
});
console.log("✅ 所有事件绑定完成");
}
// ==================== 拖拽功能 ====================
function makeDraggable(element) {
const header = document.getElementById('panel-header');
let isDragging = false;
let offsetX, offsetY;
header.addEventListener('mousedown', (e) => {
if (e.target.closest('.panel-controls')) return;
isDragging = true;
offsetX = e.clientX - element.offsetLeft;
offsetY = e.clientY - element.offsetTop;
element.style.zIndex = '99999999';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
element.style.left = `${x}px`;
element.style.top = `${y}px`;
element.style.right = 'auto';
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
}
// ==================== 核心功能:自动展开所有章节(通用模式) ====================
async function expandAllChapters() {
if (isSmartEduPlatform) {
log('warning', '⚠️ 智慧教育平台专属模式无需展开章节');
return;
}
log('info', '🔍 正在扫描所有章节...');
try {
// 第一步:展开所有大章节(li.list-item)
const mainChapters = document.querySelectorAll('li.list-item');
log('info', `📚 找到 ${mainChapters.length} 个大章节`);
for (let i = 0; i < mainChapters.length; i++) {
if (stopRequested) break;
const chapter = mainChapters[i];
const leftItem = chapter.querySelector('.left-item');
const icon = leftItem?.querySelector('.icon');
// 通过icon的is-expanded类判断是否已经展开
if (leftItem && icon && !icon.classList.contains('is-expanded')) {
log('info', `📖 展开大章节 ${i + 1}/${mainChapters.length}`);
safeClick(leftItem);
await randomSleep(400, 800);
}
}
// 第二步:展开所有小节(ant-collapse-item)
const subChapters = document.querySelectorAll('.ant-collapse-item');
log('info', `📑 找到 ${subChapters.length} 个小节`);
for (let i = 0; i < subChapters.length; i++) {
if (stopRequested) break;
const chapter = subChapters[i];
const header = chapter.querySelector('.ant-collapse-header');
if (header && header.getAttribute('aria-expanded') !== 'true') {
log('info', `📖 展开小节 ${i + 1}/${subChapters.length}`);
safeClick(header);
await randomSleep(400, 800);
}
}
// 等待所有内容渲染完成
await sleep(1500);
// 第三步:收集所有可见的视频项(同时支持.name和.child-item)
videoItems = Array.from(document.querySelectorAll('.name, .child-item')).filter(item => {
return item.offsetParent !== null;
});
totalVideos = videoItems.length;
if (totalVideos === 0) {
log('warning', '⚠️ 未找到任何可见的视频项');
} else {
log('success', `🎉 所有章节已成功展开,共找到 ${totalVideos} 个可见视频`);
}
isAllExpanded = true;
updateUI();
} catch (error) {
log('error', `❌ 展开章节时出错: ${error.message}`);
console.error(error);
}
}
// ==================== 核心功能:自动学习(自动切换模式) ====================
async function startAutoStudy() {
console.log("🚀 startAutoStudy函数被调用");
if (isRunning) {
log('warning', '⚠️ 脚本已经在运行中');
console.log("⚠️ 脚本已经在运行中,isRunning =", isRunning);
return;
}
try {
if (isSmartEduPlatform) {
// 高等教育智慧教育平台:强制使用你提供的figure脚本逻辑
await startSmartEduExclusiveAutoStudy();
} else {
// 通用模式(保留原V5.3逻辑)
await startIntegratedAutoStudy();
}
} catch (error) {
log('error', `❌ 学习过程中出错: ${error.message}`);
console.error("❌ 学习过程中出错:", error);
}
isRunning = false;
isPaused = false;
updateUI();
console.log("🏁 学习流程结束,isRunning已重置为false");
}
// ==================== 【完全按照你要求+修复】智慧教育平台专属脚本逻辑 ====================
async function startSmartEduExclusiveAutoStudy() {
isRunning = true;
isPaused = false;
stopRequested = false;
currentIndex = 0;
updateUI();
log('info', '🚀 启动高等教育智慧教育平台专属自动学习(figure标签版)');
console.log("🚀 启动智慧教育平台专属自动学习");
// 【修复1:增加视频查询重试机制】最多重试3次,每次等待2秒
let videoItems = null;
for (let retry = 0; retry < 3; retry++) {
videoItems = document.querySelectorAll('figure[class*="cursor-pointer"]');
if (videoItems.length > 0) break;
log('warning', `⚠️ 第 ${retry + 1} 次未找到视频,等待2秒后重试...`);
console.log(`⚠️ 第 ${retry + 1} 次未找到视频,等待2秒后重试...`);
await sleep(2000);
}
if (!videoItems || videoItems.length === 0) {
log('error', '❌ 经过3次重试仍未找到视频封面,请确认:1. 页面是否完全加载 2. 是否在课程播放页面');
console.error("❌ 经过3次重试仍未找到视频封面");
isRunning = false;
updateUI();
return;
}
totalVideos = videoItems.length;
log('info', `共识别到 ${totalVideos} 个视频,准备开始按顺序播放...`);
console.log(`共识别到 ${videoItems.length} 个视频,准备开始无脑按顺序播放...`);
updateUI();
for (let i = 0; i < videoItems.length; i++) {
if (stopRequested) break;
currentIndex = i + 1;
updateUI();
const currentItem = videoItems[i];
// 等待暂停(保留原V5控制功能)
if (isPaused) {
log('info', '⏸️ 已暂停,等待继续...');
await new Promise(resolve => { pauseResolve = resolve; });
if (stopRequested) break;
log('info', '▶️ 继续学习');
}
// 2. 滚动到当前视频并点击(完全按照你指定的参数)
log('info', `▶️ 正在打开第 ${currentIndex} 个视频...`);
console.log(`▶️ 正在打开第 ${i + 1} 个视频...`);
currentItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(1500); // 你指定的1500ms
safeClick(currentItem);
// 给弹窗和视频加载留出足够的时间(你指定的4000ms)
log('info', "⏳ 等待弹窗和视频加载...");
console.log("⏳ 等待 4 秒钟让弹窗和播放器完全渲染...");
await sleep(4000);
// 保留原V5带节奏强攻播放(增强稳定性)
log('info', '🎬 正在激活视频播放...');
await forcePlayVideo();
// 3. 等待视频播放完毕(完全按照你指定的逻辑)
log('info', "⏳ 正在等待视频播放完毕,请挂机...");
console.log("⏳ 正在等待视频播放完毕,请挂机...");
await watchSmartEduVideoUntilEnd(i + 1);
// 4. 视频看完了,寻找并点击关闭按钮(完全按照你指定的优先顺序+修复)
log('info', `✅ 第 ${currentIndex} 个视频结束,尝试关闭弹窗...`);
console.log(`✅ 第 ${i + 1} 个视频结束,尝试关闭弹窗...`);
// 【修复2:增加更多关闭按钮备选】
let closeBtn = document.querySelector('button[class*="bg-white"][class*="h-10"]');
if (!closeBtn) closeBtn = document.querySelector('button[class*="close"]');
if (!closeBtn) closeBtn = document.querySelector('.anticon-close');
if (!closeBtn) closeBtn = document.querySelector('.video-modal-close');
if (closeBtn) {
log('info', "🎯 找到关闭按钮,点击关闭");
console.log("找到关闭按钮,点击关闭...");
safeClick(closeBtn);
} else {
// 如果没找到特征按钮,尝试模拟按 ESC 键退出弹窗
log('info', "🎯 未找到任何关闭按钮,尝试按 ESC 关闭...");
console.log("未找到任何关闭按钮,尝试按 ESC 关闭...");
document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true}));
document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true}));
}
// 关掉后休息几秒(你指定的3000ms)
await sleep(3000);
}
if (stopRequested) {
log('info', '⏹️ 用户手动停止了学习');
} else {
log('success', '🎉 所有界面的视频已全部轮询播放完毕!');
console.log("🎉 所有界面的视频已全部轮询播放完毕!");
}
}
// ==================== 通用模式自动学习(保留原V5.3逻辑) ====================
async function startIntegratedAutoStudy() {
isRunning = true;
isPaused = false;
stopRequested = false;
currentIndex = 0;
updateUI();
console.log("🚀 终极挂课脚本 V5.4.1 通用模式已启动!");
// 只有在未展开过的情况下才自动展开
if (!isAllExpanded) {
log('info', '🔄 检测到未展开章节,正在自动展开...');
await expandAllChapters();
}
if (totalVideos === 0) {
log('error', '❌ 没有找到任何视频,请先点击"展开全部"按钮!');
isRunning = false;
updateUI();
return;
}
log('info', `✅ 共扫描到 ${totalVideos} 个视频节,准备开始自动过滤和播放...`);
for (let i = 0; i < totalVideos; i++) {
if (stopRequested) break;
currentIndex = i + 1;
updateUI();
const currentItem = videoItems[i];
// 双重进度检查,更准确
const processElement = currentItem.querySelector('.process');
const processText = processElement ? processElement.innerText : '';
const parentText = currentItem.parentElement ? currentItem.parentElement.innerText : currentItem.innerText;
if (processText.includes('100%') || processText.includes('已完成') ||
parentText.includes('100%') || parentText.includes('已完成')) {
log('info', `⏭️ 第 ${i + 1} 个视频进度为 [${processText || '100%'}],自动跳过!`);
continue;
}
// 等待暂停
if (isPaused) {
log('info', '⏸️ 已暂停,等待继续...');
await new Promise(resolve => { pauseResolve = resolve; });
if (stopRequested) break;
log('info', '▶️ 继续学习');
}
// 滚动并打开视频
log('info', `▶️ 正在滚动并打开第 ${i + 1} 个未完成的视频...`);
currentItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(config.scrollDelay);
safeClick(currentItem);
await sleep(config.loadDelay);
// 带节奏强攻播放
log('info', "🎬 正在激活视频播放...");
await forcePlayVideo();
// 双重监听进度
log('info', "⏳ 正在监测视频播放状态...");
await waitForProcessToEnd(currentItem, i + 1);
// 关闭弹窗
log('info', `☑️ 第 ${i + 1} 个视频流程结束,尝试强制关闭弹窗...`);
forceCloseModal();
await sleep(config.closeDelay);
}
if (stopRequested) {
log('info', '⏹️ 用户手动停止了学习');
} else {
log('success', '🎉 太棒了,所有任务脚本运行结束!');
}
}
// ==================== 带节奏强攻播放函数(保留原V5核心) ====================
async function forcePlayVideo() {
const video = document.querySelector('video');
if (video) {
video.muted = true; // 强制静音解锁自动播放
await sleep(500);
}
// 多层级套娃点击,点亮即停
const targets = [
document.querySelector('.video-play-img img'), // 最里面的图片
document.querySelector('.video-play-img'), // 中间层
document.querySelector('.video-play'), // 外层
document.querySelector('.vjs-big-play-button'), // 原生大按钮
document.querySelector('button[class*="play"]'), // 通用播放按钮
video // 直接点击视频本身
];
for (let target of targets) {
if (target && target.offsetParent !== null) {
log('info', `👉 尝试物理点击播放按钮...`);
safeClick(target);
// 给框架800毫秒处理时间
await sleep(800);
// 播放成功就不再点击后面的元素,防止误点暂停
if (video && !video.paused) {
log('success', '✅ 播放成功激活!');
return;
}
}
}
// 终极兜底:直接调用视频API
if (video && video.paused) {
log('info', '👉 物理点击无效,强制调用底层播放API...');
video.play().catch((err) => {
log('warning', `⚠️ 底层API被拦截: ${err.message},将继续轮询恢复`);
console.log("底层API被拦截:", err);
});
}
}
// ==================== 控制功能 ====================
function togglePause() {
if (!isRunning) return;
isPaused = !isPaused;
updateUI();
if (!isPaused && pauseResolve) {
pauseResolve();
pauseResolve = null;
}
}
function stopAutoStudy() {
if (!isRunning) return;
stopRequested = true;
if (isPaused && pauseResolve) {
pauseResolve();
}
}
function toggleMinimize() {
const panel = document.getElementById('auto-study-panel');
panel.classList.toggle('minimized');
}
// ==================== 辅助函数 ====================
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 随机延时(反检测核心)
function randomSleep(min, max) {
const ms = Math.floor(Math.random() * (max - min + 1)) + min;
return sleep(ms);
}
// 统一安全点击函数(兼容所有浏览器)
function safeClick(element) {
if (!element) return;
try {
// 创建真实的人类鼠标点击事件
const realClick = new MouseEvent('click', {
bubbles: true,
cancelable: true,
clientX: element.getBoundingClientRect().left + element.offsetWidth / 2,
clientY: element.getBoundingClientRect().top + element.offsetHeight / 2
});
element.dispatchEvent(realClick);
console.log("✅ 安全点击成功:", element);
} catch (error) {
log('warning', `⚠️ 点击时出现小问题,已自动处理`);
console.error("点击错误:", error);
// 兜底:直接调用click方法
try { element.click(); } catch (e) {}
}
}
// ==================== 通用模式强制关闭弹窗 ====================
function forceCloseModal() {
const realClick = new MouseEvent('click', { bubbles: true, cancelable: true });
// 传统关闭图标
const spanIcon = document.querySelector('.anticon-close');
if (spanIcon && spanIcon.offsetParent !== null) {
log('info', "🎯 使用真实鼠标事件点击了关闭图标!");
spanIcon.dispatchEvent(realClick);
return;
}
// 视频弹窗关闭按钮
const divContainer = document.querySelector('.video-modal-close');
if (divContainer && divContainer.offsetParent !== null) {
log('info', "🎯 使用真实鼠标事件点击了关闭容器!");
divContainer.dispatchEvent(realClick);
return;
}
// 遮罩层
const modalMask = document.querySelector('.video-modal-mask');
if (modalMask) {
log('info', "🎯 使用真实鼠标事件点击了遮罩层!");
modalMask.dispatchEvent(realClick);
return;
}
// 终极备选:键盘ESC
log('info', "🎯 尝试模拟按 ESC 键!");
document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true}));
document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true}));
}
// ==================== 通用模式视频监测 ====================
function waitForProcessToEnd(itemNode, index) {
return new Promise((resolve) => {
let maxTimeout = 0;
let mainInterval = null;
let hasVideoEnded = false;
// 监听平台强制暂停事件,立即恢复
const videoEl = document.querySelector('video');
if (videoEl) {
videoEl.muted = true;
videoEl.addEventListener('pause', async function() {
if (videoEl.currentTime < videoEl.duration - 1 && !isPaused && !stopRequested) {
log('warning', "⚡ 平台尝试暂停视频!自动重新激活播放...");
await forcePlayVideo();
}
});
}
// 清理所有定时器的函数
function cleanupAndResolve() {
if (mainInterval) clearInterval(mainInterval);
resolve();
}
// 主检查循环
mainInterval = setInterval(async () => {
if (stopRequested) {
cleanupAndResolve();
return;
}
if (isPaused) return;
maxTimeout++;
// 【条件1】系统进度达到100%(最可靠)
const processNode = itemNode.querySelector('.process');
if (processNode && (processNode.innerText.includes('100%') || processNode.innerText.includes('已完成'))) {
log('success', `✅ 第 ${index} 个视频系统进度已更新为完成!`);
cleanupAndResolve();
return;
}
// 【条件2】视频底层确实播放完毕了
const currentVideo = document.querySelector('video');
if (currentVideo && !hasVideoEnded) {
currentVideo.muted = true;
// 【终极防暂停】每次检查都强制播放一次
if (currentVideo.paused && currentVideo.currentTime < currentVideo.duration) {
log('warning', '⚡ 检测到视频暂停,强制恢复播放...');
await forcePlayVideo();
}
currentVideo.play().catch(() => {}); // 额外强制播放一次
if (currentVideo.ended || (currentVideo.duration > 0 && currentVideo.duration - currentVideo.currentTime <= 1)) {
hasVideoEnded = true;
log('warning', `⚠️ 第 ${index} 个视频本体已播完,跳过平台服务器响应判定。`);
cleanupAndResolve();
return;
}
}
// 【条件3】45分钟超时保护(防504假死)
if (maxTimeout > config.maxTimeout) {
log('warning', `🚨 第 ${index} 个视频监测超时(可能服务器504),强行跳过。`);
cleanupAndResolve();
}
}, config.checkInterval);
});
}
// ==================== 【完全按照你要求+修复】智慧教育平台视频监测逻辑 ====================
function watchSmartEduVideoUntilEnd(index) {
return new Promise((resolve) => {
let notFoundCount = 0;
const checkInterval = setInterval(() => {
if (stopRequested) {
clearInterval(checkInterval);
resolve();
return;
}
if (isPaused) return;
const videoEl = document.querySelector('video');
if (videoEl) {
notFoundCount = 0; // 找到了就重置计数
// 强制静音,防止吵到你
videoEl.muted = true;
// 【保留原V5防暂停增强】如果视频暂停了且没播完,强行让它继续播
if (videoEl.paused && videoEl.currentTime < videoEl.duration) {
log('warning', '⚡ 检测到视频暂停,强制恢复播放...');
videoEl.play().catch(() => console.log("尝试自动播放被拦截"));
}
// 判断视频是否已经播到了最后(你指定留1秒容错)
if (videoEl.ended || (videoEl.duration > 0 && videoEl.currentTime >= videoEl.duration - 1)) {
clearInterval(checkInterval);
resolve();
}
} else {
notFoundCount++;
// 如果连续 15 秒 (5次 * 3秒) 都找不到 video 标签,可能加载卡死了(你指定)
if (notFoundCount >= 5) {
log('warning', `⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`);
console.warn(`⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`);
clearInterval(checkInterval);
resolve(); // 放行,去点下一个
}
}
}, 3000); // 你指定的每隔3秒检查一次
});
}
// 日志功能
function log(type, message) {
try {
const container = document.getElementById('log-container');
const item = document.createElement('div');
item.className = `log-item ${type}`;
item.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
container.appendChild(item);
container.scrollTop = container.scrollHeight;
} catch (error) {
console.log(message);
}
}
function clearLog() {
document.getElementById('log-container').innerHTML = '';
}
// 更新UI状态
function updateUI() {
try {
const statusText = document.getElementById('status-text');
const progressText = document.getElementById('progress-text');
const progressFill = document.getElementById('progress-fill');
const expandBtn = document.getElementById('expand-btn');
const startBtn = document.getElementById('start-btn');
const pauseBtn = document.getElementById('pause-btn');
const stopBtn = document.getElementById('stop-btn');
// 更新状态
if (isRunning) {
if (isPaused) {
statusText.textContent = '已暂停';
statusText.className = 'status-value paused';
pauseBtn.textContent = '继续';
} else {
statusText.textContent = '学习中';
statusText.className = 'status-value running';
pauseBtn.textContent = '暂停';
}
} else {
statusText.textContent = '已停止';
statusText.className = 'status-value stopped';
}
// 更新进度
progressText.textContent = `${currentIndex} / ${totalVideos}`;
const progress = totalVideos > 0 ? (currentIndex / totalVideos) * 100 : 0;
progressFill.style.width = `${progress}%`;
// 更新按钮状态
expandBtn.disabled = isRunning || isSmartEduPlatform;
startBtn.disabled = isRunning;
pauseBtn.disabled = !isRunning;
stopBtn.disabled = !isRunning;
} catch (error) {
console.error("更新UI错误:", error);
}
}
// ==================== 平台检测与初始化(修复:确保DOM完全加载) ====================
function init() {
console.log("🔧 开始初始化脚本...");
// 检测是否为高等教育智慧教育平台
isSmartEduPlatform = window.location.href.includes('higher.smartedu.cn/course/');
console.log("📱 平台检测结果:", isSmartEduPlatform ? "智慧教育平台" : "通用平台");
// 等待页面完全加载后再创建面板
if (document.readyState === 'complete') {
createPanel();
} else {
window.addEventListener('load', createPanel);
}
}
// 修复:将run-at改为document-end,并增加额外的加载等待
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(init, 1000); // 延迟1秒初始化,确保页面完全渲染
} else {
window.addEventListener('DOMContentLoaded', () => {
setTimeout(init, 1000);
});
}
})();