// ==UserScript==
// @name 安徽省高等教育自学考试助学服务平台,视频自动播放|连续播放|后台运行
// @namespace http://tampermonkey.net/
// @author 花开半夏
// @version 0.0.1
// @description 适配于安徽省高等教育自学考试助学服务平台的一个脚本,主要是实现视频自动播放、连续播放、后台运行,
// @match https://*.ahjxjy.cn/*
// @grant none
// @license Apache Licence 2.0
// @icon https://www.ahjxjy.cn/images/favicon.ico
// ==/UserScript==
(function() {
'use strict';
let isAutoPlaying = false;
let currentVideoElement = null;
let autoLearningSession = false;
let hasStartedAuto = false;
let lastPlayAttempt = 0;
let lastProgressLog = 0;
let backgroundPlayInterval = null; // 后台播放保护定时器
// 增强的后台播放保护
function preventVideoPause() {
console.log('🛡️ 启用增强的后台播放保护...');
// 强制设置页面可见性
Object.defineProperty(document, 'hidden', {
get: () => false,
configurable: false
});
Object.defineProperty(document, 'visibilityState', {
get: () => 'visible',
configurable: false
});
Object.defineProperty(document, 'webkitHidden', {
get: () => false,
configurable: false
});
Object.defineProperty(document, 'webkitVisibilityState', {
get: () => 'visible',
configurable: false
});
// 阻止所有可见性变化事件
['visibilitychange', 'webkitvisibilitychange', 'mozvisibilitychange', 'msvisibilitychange'].forEach(event => {
document.addEventListener(event, function(e) {
if (isAutoPlaying) {
e.stopImmediatePropagation();
e.preventDefault();
console.log(`🛡️ 阻止了${event}事件`);
}
}, true);
});
// 阻止窗口失焦
window.addEventListener('blur', function(e) {
if (isAutoPlaying) {
console.log('🛡️ 窗口失焦被阻止');
e.stopImmediatePropagation();
e.preventDefault();
// 立即恢复焦点
setTimeout(() => {
window.focus();
}, 100);
// 确保视频继续播放
setTimeout(() => {
forceVideoPlay();
}, 200);
}
}, true);
// 监听页面焦点变化
window.addEventListener('focus', function(e) {
if (isAutoPlaying) {
console.log('🛡️ 页面重新获得焦点');
setTimeout(() => {
forceVideoPlay();
}, 300);
}
});
// 监听视频暂停事件
document.addEventListener('pause', function(e) {
if (isAutoPlaying && e.target.tagName === 'VIDEO') {
const now = Date.now();
console.log('🛡️ 检测到视频暂停,立即恢复');
// 立即尝试恢复
setTimeout(() => {
if (e.target.paused && !e.target.ended && isAutoPlaying) {
console.log('🔄 强制恢复视频播放');
e.target.play().catch(err => {
console.log('恢复播放失败:', err);
});
}
}, 100);
// 再次确认恢复
setTimeout(() => {
if (e.target.paused && !e.target.ended && isAutoPlaying) {
console.log('🔄 二次确认恢复播放');
e.target.play().catch(err => {
console.log('二次恢复失败:', err);
});
}
}, 1000);
}
}, true);
// 阻止页面休眠和节能模式
document.addEventListener('beforeunload', function(e) {
if (isAutoPlaying) {
e.preventDefault();
e.returnValue = '';
console.log('🛡️ 阻止页面卸载');
}
});
// 启动后台播放保护定时器
startBackgroundPlayProtection();
}
// 启动后台播放保护定时器
function startBackgroundPlayProtection() {
if (backgroundPlayInterval) {
clearInterval(backgroundPlayInterval);
}
backgroundPlayInterval = setInterval(() => {
if (isAutoPlaying && currentVideoElement) {
forceVideoPlay();
}
}, 2000); // 每2秒检查一次
console.log('🛡️ 后台播放保护定时器已启动');
}
// 停止后台播放保护定时器
function stopBackgroundPlayProtection() {
if (backgroundPlayInterval) {
clearInterval(backgroundPlayInterval);
backgroundPlayInterval = null;
console.log('🛡️ 后台播放保护定时器已停止');
}
}
// 强制视频播放
function forceVideoPlay() {
if (!currentVideoElement || !isAutoPlaying) return;
try {
if (currentVideoElement.paused && !currentVideoElement.ended) {
console.log('🔄 强制视频继续播放');
// 确保视频处于可播放状态
currentVideoElement.muted = true;
currentVideoElement.volume = 0;
// 尝试播放
const playPromise = currentVideoElement.play();
if (playPromise !== undefined) {
playPromise.then(() => {
console.log('✅ 强制播放成功');
}).catch(err => {
console.log('强制播放失败:', err);
// 如果播放失败,尝试点击播放按钮
setTimeout(() => {
clickPlayButton();
}, 500);
});
}
}
// 确保静音状态
if (!currentVideoElement.muted) {
currentVideoElement.muted = true;
currentVideoElement.volume = 0;
}
} catch (e) {
console.log('强制播放时出错:', e);
}
}
// 温和的确保视频播放(已被forceVideoPlay替代,但保留以防万一)
function ensureVideoPlaying() {
if (!currentVideoElement || !isAutoPlaying) return;
const now = Date.now();
if (now - lastPlayAttempt < 3000) {
return; // 缩短到3秒
}
if (currentVideoElement.paused && !currentVideoElement.ended) {
lastPlayAttempt = now;
console.log('🔄 温和地恢复视频播放');
currentVideoElement.play().then(() => {
console.log('✅ 视频继续播放');
}).catch(err => {
console.log('播放失败:', err);
});
}
if (!currentVideoElement.muted) {
currentVideoElement.muted = true;
currentVideoElement.volume = 0;
}
}
// 保存和恢复自动学习状态
function saveAutoLearningState() {
try {
localStorage.setItem('videoAutoLearning_active', 'true');
localStorage.setItem('videoAutoLearning_timestamp', Date.now().toString());
} catch (e) {
console.log('保存状态失败:', e);
}
}
function clearAutoLearningState() {
try {
localStorage.removeItem('videoAutoLearning_active');
localStorage.removeItem('videoAutoLearning_timestamp');
} catch (e) {
console.log('清除状态失败:', e);
}
}
function isInAutoLearningSession() {
try {
const isActive = localStorage.getItem('videoAutoLearning_active');
const timestamp = localStorage.getItem('videoAutoLearning_timestamp');
if (isActive === 'true' && timestamp) {
const elapsed = Date.now() - parseInt(timestamp);
return elapsed < 30 * 60 * 1000;
}
} catch (e) {
console.log('检查状态失败:', e);
}
return false;
}
// 彻底静默所有数据库相关错误
function handleApiErrors() {
console.log('🔇 设置彻底的错误静默处理...');
const originalFetch = window.fetch;
window.fetch = function(...args) {
return originalFetch.apply(this, args)
.then(response => {
if (!response.ok && response.status !== 404) {
console.log(`静默处理Fetch错误: ${response.status}`);
return new Response('{"success":true,"message":"ok"}', {
status: 200,
statusText: 'OK',
headers: { 'Content-Type': 'application/json' }
});
}
return response;
})
.catch(error => {
console.log('Fetch错误被静默处理:', error.message || error);
return new Response('{"success":true,"message":"ok"}', {
status: 200,
statusText: 'OK',
headers: { 'Content-Type': 'application/json' }
});
});
};
window.addEventListener('unhandledrejection', function(event) {
console.log('Promise拒绝被静默处理');
event.preventDefault();
});
window.addEventListener('error', function(event) {
console.log('全局错误被静默处理');
event.preventDefault();
});
const originalConsoleError = console.error;
console.error = function(...args) {
const message = args.join(' ');
if (!message.includes('Database operation expected') &&
!message.includes('optimistic concurrency')) {
originalConsoleError.apply(console, args);
}
};
}
// 停止自动学习
function stopAutoLearning() {
isAutoPlaying = false;
autoLearningSession = false;
currentVideoElement = null;
hasStartedAuto = false;
lastPlayAttempt = 0;
lastProgressLog = 0;
stopBackgroundPlayProtection(); // 停止后台保护
clearAutoLearningState();
updateStatus('已停止自动播放');
}
// 更新状态显示
function updateStatus(text) {
const statusElement = document.getElementById('status');
if (statusElement) {
statusElement.textContent = text;
}
console.log('📊 状态:', text);
}
// 获取所有视频项
function getAllVideoItems() {
const dots = document.querySelectorAll('div[data-v-2f21b36e].dot');
const videoItems = [];
dots.forEach((dot, index) => {
const hasBlueBackground = dot.style.background && dot.style.background.includes('rgb(45, 140, 240)');
const parentLi = dot.closest('li');
if (parentLi) {
let title = '';
const titleElement = parentLi.querySelector('.title-nnx');
if (titleElement) {
title = titleElement.textContent.trim();
} else {
const allText = parentLi.textContent.trim();
const lines = allText.split('\n').map(line => line.trim()).filter(line => line);
title = lines.find(line => line.includes('视频:') || line.includes('.mp4')) || `视频${index + 1}`;
}
const isCurrent = parentLi.querySelector('.state span[style*="rgb(45, 140, 240)"]') !== null;
videoItems.push({
index: index,
element: parentLi,
dot: dot,
isCompleted: hasBlueBackground,
isCurrent: isCurrent,
title: title
});
}
});
return videoItems;
}
// 检查当前页面是否有视频播放器
function hasVideoPlayer() {
return document.querySelector('video') !== null;
}
// 查找下一个未完成的视频
function findNextUncompletedVideo() {
const videoItems = getAllVideoItems();
let currentIndex = -1;
for (let i = 0; i < videoItems.length; i++) {
if (videoItems[i].isCurrent) {
currentIndex = i;
break;
}
}
for (let i = currentIndex + 1; i < videoItems.length; i++) {
if (!videoItems[i].isCompleted) {
return videoItems[i];
}
}
for (let i = 0; i <= currentIndex; i++) {
if (!videoItems[i].isCompleted) {
return videoItems[i];
}
}
return null;
}
// 增强的视频设置
function setupVideo() {
const video = document.querySelector('video');
if (video && video !== currentVideoElement) {
currentVideoElement = video;
// 立即设置静音和播放属性
video.muted = true;
video.volume = 0;
video.autoplay = true;
video.loop = false;
// 添加视频事件监听
video.addEventListener('pause', function() {
if (isAutoPlaying) {
console.log('🔄 视频暂停事件,准备恢复');
setTimeout(() => {
forceVideoPlay();
}, 100);
}
});
video.addEventListener('ended', function() {
console.log('📹 视频播放结束');
});
console.log(`✅ 视频设置完成: 已静音,已启用自动播放`);
return true;
}
return currentVideoElement !== null;
}
// 增强的点击播放按钮
function clickPlayButton() {
console.log('🔍 查找播放按钮...');
const playSelectors = [
'.dplayer-big-play',
'.dplayer-play-icon',
'.dplayer-icon.dplayer-play-icon',
'button.dplayer-play-icon',
'.dplayer-controller .dplayer-play-icon',
'.dplayer-video',
];
for (let selector of playSelectors) {
const button = document.querySelector(selector);
if (button && button.offsetParent !== null) {
console.log(`✅ 找到播放按钮,点击:`, selector);
try {
button.click();
setTimeout(() => setupVideo(), 500);
setTimeout(() => setupVideo(), 1000);
setTimeout(() => forceVideoPlay(), 1500);
return true;
} catch (e) {
console.log('点击播放按钮失败:', e);
}
}
}
if (setupVideo()) {
console.log('✅ 直接设置视频成功');
setTimeout(() => forceVideoPlay(), 500);
return true;
}
console.log('❌ 未找到播放按钮');
return false;
}
// 检查视频是否播放完成
function isVideoCompleted() {
if (!currentVideoElement) return false;
try {
if (currentVideoElement.ended) {
console.log('✅ 视频已结束(ended=true)');
return true;
}
if (currentVideoElement.duration > 0 && currentVideoElement.currentTime > 0) {
const progress = currentVideoElement.currentTime / currentVideoElement.duration;
const currentMinutes = Math.floor(currentVideoElement.currentTime / 60);
const totalMinutes = Math.floor(currentVideoElement.duration / 60);
const now = Date.now();
if (!lastProgressLog || now - lastProgressLog > 30000) {
console.log(`📹 视频播放详情:`);
console.log(` 当前时间: ${currentMinutes}:${Math.floor(currentVideoElement.currentTime % 60).toString().padStart(2, '0')}`);
console.log(` 总时长: ${totalMinutes}:${Math.floor(currentVideoElement.duration % 60).toString().padStart(2, '0')}`);
console.log(` 播放进度: ${Math.round(progress * 100)}%`);
console.log(` 视频状态: ${currentVideoElement.paused ? '⏸️暂停' : '▶️播放中'}`);
console.log(` 是否结束: ${currentVideoElement.ended ? '✅是' : '❌否'}`);
console.log(` 后台保护: ${backgroundPlayInterval ? '✅活跃' : '❌停止'}`);
lastProgressLog = now;
}
const remainingTime = currentVideoElement.duration - currentVideoElement.currentTime;
if (progress > 0.995 || remainingTime < 5) {
console.log(`✅ 视频即将完成 - 进度: ${Math.round(progress * 100)}%, 剩余: ${Math.round(remainingTime)}秒`);
return true;
}
if (remainingTime < 10 && currentVideoElement.paused) {
console.log(`✅ 视频在结尾暂停,认为完成`);
return true;
}
}
} catch (e) {
console.log('检查视频完成状态失败:', e);
}
return false;
}
// 智能点击
function smartClick(element) {
if (!element) return false;
try {
const linkElement = element.querySelector('a') || element.closest('a');
if (linkElement) {
linkElement.click();
return true;
}
element.click();
return true;
} catch (e) {
console.log('智能点击失败:', e);
return false;
}
}
// 处理视频完成后的逻辑
async function handleVideoCompleted() {
console.log('🎉 视频播放完成!');
try {
currentVideoElement = null;
lastProgressLog = 0;
stopBackgroundPlayProtection(); // 停止当前视频的后台保护
updateStatus('视频完成,准备跳转...');
console.log('⏳ 等待系统更新完成状态...');
await new Promise(resolve => setTimeout(resolve, 8000));
const nextVideo = findNextUncompletedVideo();
if (nextVideo) {
console.log(`🔄 跳转到下一个视频: "${nextVideo.title}"`);
updateStatus(`跳转: ${nextVideo.title}`);
if (smartClick(nextVideo.element)) {
console.log('✅ 已点击下一个视频');
// 重新启动后台保护将在新视频开始播放时进行
} else {
console.log('❌ 跳转失败,停止自动播放');
stopAutoLearning();
}
} else {
console.log('🎉 所有视频已完成!');
stopAutoLearning();
alert('🎉 所有视频已学习完成!');
}
} catch (e) {
console.log('处理视频完成时出错:', e);
stopAutoLearning();
}
}
// 开始播放当前页面的视频
function startCurrentVideoPlayback() {
console.log('🚀 === 开始自动播放 ===');
if (hasStartedAuto) {
console.log('⚠️ 已经启动,跳过');
return;
}
try {
hasStartedAuto = true;
autoLearningSession = true;
isAutoPlaying = true;
saveAutoLearningState();
startBackgroundPlayProtection(); // 启动后台播放保护
updateStatus('正在启动播放...');
let attempts = 0;
const maxAttempts = 3;
const waitForVideo = setInterval(() => {
attempts++;
console.log(`🔄 尝试播放第${attempts}次...`);
if (clickPlayButton()) {
clearInterval(waitForVideo);
console.log('✅ 播放启动成功');
updateStatus('🎬 视频播放中...');
// 主要的检查完成状态的间隔
const checkInterval = setInterval(() => {
if (!isAutoPlaying || !autoLearningSession) {
clearInterval(checkInterval);
return;
}
if (isVideoCompleted()) {
console.log('🎯 检测到视频播放完成');
clearInterval(checkInterval);
handleVideoCompleted();
}
}, 3000);
setTimeout(() => {
if (isAutoPlaying && autoLearningSession) {
clearInterval(checkInterval);
console.log('⏰ 视频播放超时(20分钟),跳转下一个');
handleVideoCompleted();
}
}, 1200000);
} else if (attempts >= maxAttempts) {
clearInterval(waitForVideo);
console.log('❌ 启动播放失败');
hasStartedAuto = false;
updateStatus('❌ 启动失败');
}
}, 3000);
} catch (e) {
console.log('启动播放时出错:', e);
hasStartedAuto = false;
stopAutoLearning();
}
}
// 检测并自动播放
function detectAndAutoPlay() {
try {
hasStartedAuto = false;
console.log('🔍 检测视频播放器...');
if (hasVideoPlayer()) {
console.log('✅ 发现视频播放器');
updateStatus('发现视频,准备播放...');
setTimeout(() => {
if (!hasStartedAuto) {
startCurrentVideoPlayback();
}
}, 3000);
} else {
console.log('❌ 未发现视频播放器');
if (isInAutoLearningSession()) {
console.log('🔄 自动查找下一个视频');
const videoItems = getAllVideoItems();
for (let i = 0; i < videoItems.length; i++) {
if (!videoItems[i].isCompleted) {
console.log(`🔄 自动跳转: "${videoItems[i].title}"`);
smartClick(videoItems[i].element);
break;
}
}
}
}
} catch (e) {
console.log('检测播放时出错:', e);
}
}
// 页面监听
function observePageChanges() {
try {
const observer = new MutationObserver((mutations) => {
let hasVideoAdded = false;
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.tagName === 'VIDEO' || node.querySelector('video')) {
hasVideoAdded = true;
}
}
});
}
});
if (hasVideoAdded && !hasStartedAuto) {
console.log('🆕 检测到新视频');
setTimeout(() => {
detectAndAutoPlay();
}, 2000);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
} catch (e) {
console.log('设置页面监听失败:', e);
}
}
// 添加控制面板
function addControlPanel() {
try {
const panel = document.createElement('div');
panel.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
z-index: 9999;
background: white;
border: 2px solid #2d8cf0;
border-radius: 8px;
padding: 15px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
font-family: Arial, sans-serif;
min-width: 220px;
`;
panel.innerHTML = `
<div style="margin-bottom: 10px; font-weight: bold; color: #333;">🎥 强化后台播放</div>
<button id="stopAuto" style="background: #f56c6c; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; width: 100%; margin-bottom: 8px;">⏹️ 停止播放</button>
<button id="forcePlay" style="background: #409eff; color: white; border: none; padding: 6px 10px; border-radius: 4px; cursor: pointer; width: 100%; margin-bottom: 8px; font-size: 12px;">▶️ 强制播放</button>
<button id="debugBtn" style="background: #67c23a; color: white; border: none; padding: 6px 10px; border-radius: 4px; cursor: pointer; width: 100%; margin-bottom: 8px; font-size: 12px;">🔍 状态</button>
<div id="status" style="font-size: 12px; color: #666; margin-top: 8px;">强化模式 • 后台播放保护</div>
`;
document.body.appendChild(panel);
document.getElementById('stopAuto').onclick = () => {
stopAutoLearning();
};
document.getElementById('forcePlay').onclick = () => {
console.log('🔄 手动强制播放');
forceVideoPlay();
};
document.getElementById('debugBtn').onclick = () => {
console.log('=== 详细状态 ===');
console.log('🎬 自动播放:', isAutoPlaying);
console.log('📚 学习会话:', autoLearningSession);
console.log('🚀 已启动:', hasStartedAuto);
console.log('📹 有视频:', hasVideoPlayer());
console.log('🛡️ 后台保护:', backgroundPlayInterval ? '活跃' : '停止');
console.log('📺 视频元素:', currentVideoElement);
if (currentVideoElement) {
const progress = (currentVideoElement.currentTime / currentVideoElement.duration) * 100;
const currentMin = Math.floor(currentVideoElement.currentTime / 60);
const currentSec = Math.floor(currentVideoElement.currentTime % 60);
const totalMin = Math.floor(currentVideoElement.duration / 60);
const totalSec = Math.floor(currentVideoElement.duration % 60);
console.log(`⏰ 播放时间: ${currentMin}:${currentSec.toString().padStart(2, '0')} / ${totalMin}:${totalSec.toString().padStart(2, '0')}`);
console.log(`📊 播放进度: ${Math.round(progress)}%`);
console.log(`🎮 视频状态: ${currentVideoElement.paused ? '⏸️暂停' : '▶️播放中'}`);
console.log(`🏁 是否结束: ${currentVideoElement.ended ? '✅是' : '❌否'}`);
}
};
} catch (e) {
console.log('添加面板失败:', e);
}
}
// 增强的自动静音
function initAutoMute() {
try {
document.addEventListener('play', function(e) {
if (e.target.tagName === 'VIDEO') {
console.log('🔇 视频开始播放,立即静音');
e.target.muted = true;
e.target.volume = 0;
// 延迟再次确认静音
setTimeout(() => {
if (e.target) {
e.target.muted = true;
e.target.volume = 0;
}
}, 100);
}
}, true);
} catch (e) {
console.log('设置静音失败:', e);
}
}
// 初始化
function initialize() {
try {
handleApiErrors();
addControlPanel();
initAutoMute();
preventVideoPause();
observePageChanges();
setTimeout(() => {
detectAndAutoPlay();
}, 2000);
} catch (e) {
console.log('初始化失败:', e);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
setTimeout(initialize, 2000);
});
} else {
setTimeout(initialize, 2000);
}
console.log('🎯 强化后台播放脚本已加载');
})();