// ==UserScript==
// @name 鸽子宝宝自动学习
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 自动找到视频播放按钮并触发播放,处理自动播放限制
// @author x2in
// @match *://ols.v.zzu.edu.cn/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 配置参数 - 新增播放速度设置
const config = {
// 课程项选择器
dirItemSelector: '.dir-item',
// 状态元素选择器
statusSelector: '.item-status',
// 完成状态文本
completedText: '已完成',
// 视频播放相关选择器
playButtonSelectors: [
'.prism-big-play-btn',
'.prism-play-btn',
'#J_prismPlayer_component_A506AD5A-D221-4218-B5E1-9E3BE9AEFBB7'
],
videoSelector: '.prism-player video',
// 倍速控制相关选择器
speedSelector: '.prism-speed-selector', // 倍速选择器容器
speedOptionSelector: '.prism-speed-selector .selector-list li', // 倍速选项
// 播放速度设置
targetPlaybackRate: 2, // 默认2倍速
// 重试与检查间隔配置
maxRetries: 20,
checkInterval: 1000,
statusCheckInterval: 5000,
speedCheckInterval: 3000, // 倍速检查间隔
allowUnmuteAfterInteraction: true
};
// 全局状态变量
let currentItemIndex = 0;
let dirItems = [];
let videoElement = null;
let isPlaying = false;
let userInteracted = false;
let statusCheckTimer = null;
let speedCheckTimer = null;
// 初始化
function init() {
console.log('自动课程学习脚本初始化');
handleUserInteraction();
processDirItems();
}
// 获取并处理所有课程项
function processDirItems() {
dirItems = Array.from(document.querySelectorAll(config.dirItemSelector));
if (dirItems.length === 0) {
console.log('未找到课程项('.concat(config.dirItemSelector,'),将在',config.checkInterval,'ms后重试'));
setTimeout(processDirItems, config.checkInterval);
return;
}
console.log('找到'.concat(dirItems.length,'个课程项,开始处理'));
processCurrentDirItem();
}
// 处理当前课程项
function processCurrentDirItem() {
// 清除之前的定时器
if (speedCheckTimer) {
clearInterval(speedCheckTimer);
}
// 检查是否所有课程都已完成
if (currentItemIndex >= dirItems.length) {
checkAllCompleted();
return;
}
const currentItem = dirItems[currentItemIndex];
console.log('正在处理第'.concat(currentItemIndex + 1,'个课程项'));
// 查找状态元素
const statusElement = currentItem.querySelector(config.statusSelector);
if (!statusElement) {
console.log('未找到状态元素,跳过当前项');
currentItemIndex++;
setTimeout(processCurrentDirItem, config.checkInterval);
return;
}
const statusText = statusElement.textContent.trim();
console.log('当前课程状态: '.concat(statusText));
// 检查是否已完成
if (statusText === config.completedText) {
console.log('当前课程已完成,处理下一个');
currentItemIndex++;
setTimeout(processCurrentDirItem, config.checkInterval);
} else {
console.log('当前课程未完成,准备学习');
// 点击当前课程项
clickElement(currentItem);
// 等待课程加载
setTimeout(startVideoPlayback, 2000);
}
}
// 开始视频播放
function startVideoPlayback() {
// 清除之前的状态检查定时器
if (statusCheckTimer) {
clearInterval(statusCheckTimer);
}
// 尝试播放视频
const playInterval = setInterval(async () => {
const success = await tryPlayVideo();
if (success) {
clearInterval(playInterval);
console.log('视频开始播放,开始监控状态和设置倍速');
// 设置2倍速
setPlaybackSpeed();
// 定期检查倍速是否保持
startSpeedMonitoring();
// 开始监控课程状态
startStatusMonitoring();
}
}, config.checkInterval);
}
// 设置播放速度为2倍速
function setPlaybackSpeed() {
// 方法1: 直接设置video元素的playbackRate(最直接有效)
if (videoElement) {
videoElement.playbackRate = config.targetPlaybackRate;
console.log('已直接设置视频播放速度为'.concat(config.targetPlaybackRate,'倍'));
}
// 方法2: 尝试通过页面的倍速控制UI设置(作为备份)
setTimeout(() => {
try {
// 先点击倍速按钮打开选项列表
const speedButton = document.querySelector(config.speedSelector);
if (speedButton) {
speedButton.click();
console.log('已点击倍速按钮');
// 查找并点击2倍速选项
setTimeout(() => {
const speedOptions = document.querySelectorAll(config.speedOptionSelector);
let targetOption = null;
// 查找包含"2x"或"2.0x"或"2倍"的选项
speedOptions.forEach(option => {
const text = option.textContent.trim().toLowerCase();
if (text.includes('2x') || text.includes('2.0x') || text.includes('2倍')) {
targetOption = option;
}
});
if (targetOption) {
targetOption.click();
console.log('已通过UI设置2倍速');
// 关闭倍速选择面板
if (speedButton) speedButton.click();
} else {
console.log('未找到2倍速选项,将继续使用直接设置方式');
if (speedButton) speedButton.click(); // 关闭面板
}
}, 500);
}
} catch (err) {
console.log('通过UI设置倍速失败,将继续使用直接设置方式:', err);
}
}, 1000);
}
// 监控播放速度,确保保持2倍速
function startSpeedMonitoring() {
speedCheckTimer = setInterval(() => {
if (videoElement && Math.abs(videoElement.playbackRate - config.targetPlaybackRate) > 0.1) {
console.log('检测到播放速度变化,重新设置为2倍速');
videoElement.playbackRate = config.targetPlaybackRate;
}
}, config.speedCheckInterval);
}
// 监控课程状态,判断是否完成
function startStatusMonitoring() {
statusCheckTimer = setInterval(() => {
if (currentItemIndex >= dirItems.length) {
clearInterval(statusCheckTimer);
return;
}
const currentItem = dirItems[currentItemIndex];
const statusElement = currentItem.querySelector(config.statusSelector);
if (statusElement && statusElement.textContent.trim() === config.completedText) {
console.log('当前课程已完成');
clearInterval(statusCheckTimer);
// 停止当前视频
stopVideo();
// 处理下一个课程
currentItemIndex++;
setTimeout(processCurrentDirItem, 2000);
} else if (videoElement && videoElement.ended) {
console.log('视频播放结束,等待状态更新');
}
}, config.statusCheckInterval);
}
// 检查是否所有课程都已完成
function checkAllCompleted() {
const allCompleted = dirItems.every(item => {
const statusElement = item.querySelector(config.statusSelector);
return statusElement && statusElement.textContent.trim() === config.completedText;
});
if (allCompleted) {
console.log('所有课程已学习完成!');
showCompletionMessage();
} else {
console.log('检测到有课程未完成,重新开始处理');
currentItemIndex = 0;
setTimeout(processDirItems, config.checkInterval);
}
}
// 显示学习完成提示
function showCompletionMessage() {
// 创建提示元素
const notification = document.createElement('div');
notification.style.position = 'fixed';
notification.style.top = '50%';
notification.style.left = '50%';
notification.style.transform = 'translate(-50%, -50%)';
notification.style.padding = '20px 40px';
notification.style.backgroundColor = '#4CAF50';
notification.style.color = 'white';
notification.style.fontSize = '24px';
notification.style.borderRadius = '8px';
notification.style.zIndex = '999999';
notification.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
notification.textContent = '所有课程学习完成!';
document.body.appendChild(notification);
// 5秒后自动移除提示
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transition = 'opacity 0.5s';
setTimeout(() => notification.remove(), 500);
}, 5000);
}
// 尝试播放视频
async function tryPlayVideo() {
// 先尝试直接播放视频元素
if (await playVideoElement()) {
return true;
}
// 再尝试点击播放按钮
if (clickPlayButtons()) {
setTimeout(playVideoElement, 500);
return true;
}
return false;
}
// 直接播放视频元素
async function playVideoElement() {
videoElement = document.querySelector(config.videoSelector);
if (!videoElement) return false;
try {
// 检查是否已经在播放
if (!videoElement.paused) {
isPlaying = true;
return true;
}
// 强制保持静音,直到用户交互
if (!videoElement.muted) {
videoElement.muted = true;
console.log('已强制静音以符合浏览器自动播放政策');
}
// 尝试播放
const promise = videoElement.play();
if (promise !== undefined) {
await promise;
console.log('视频已静音播放');
isPlaying = true;
return true;
}
} catch (err) {
console.log('播放尝试失败:', err.message);
return false;
}
}
// 点击播放按钮
function clickPlayButtons() {
for (const selector of config.playButtonSelectors) {
const button = document.querySelector(selector);
if (button) {
console.log('找到播放按钮并点击: '.concat(selector));
return simulateClick(button);
}
}
return false;
}
// 停止视频播放
function stopVideo() {
if (videoElement && !videoElement.paused) {
videoElement.pause();
isPlaying = false;
console.log('已停止当前视频播放');
}
}
// 模拟点击元素
function simulateClick(element) {
if (!element) return false;
try {
const clickEvent = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
button: 0
});
element.dispatchEvent(clickEvent);
console.log('已模拟点击元素');
return true;
} catch (err) {
console.error('模拟点击失败:', err);
return false;
}
}
// 点击元素(直接点击)
function clickElement(element) {
if (element.click) {
try {
element.click();
console.log('已点击元素');
return true;
} catch (err) {
console.error('点击元素失败:', err);
return simulateClick(element);
}
}
return simulateClick(element);
}
// 监听用户交互
function handleUserInteraction() {
const markAsInteracted = () => {
if (!userInteracted) {
userInteracted = true;
console.log('检测到用户交互');
}
};
// 监听所有可能的用户交互
window.addEventListener('click', markAsInteracted);
window.addEventListener('keydown', markAsInteracted);
window.addEventListener('touchstart', markAsInteracted);
}
// 监控页面动态变化
const observer = new MutationObserver((mutations) => {
if (dirItems.length === 0) {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.matches(config.dirItemSelector)) {
console.log('检测到新的课程项,重新处理');
processDirItems();
}
});
});
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// 页面加载完成后初始化
if (document.readyState === 'complete') {
init();
} else {
window.addEventListener('load', init);
}
})();