// ==UserScript==
// @name 灯塔-学习助手
// @namespace https://gbwlxy.dtdjzx.gov.cn/
// @version 1.3.1
// @description 灯塔-山东干部网络学院学习自动化助手,支持倍速播放和智能控制
// @author duocaikun
// @match https://gbwlxy.dtdjzx.gov.cn/content*
// ==/UserScript==
(function() {
'use strict';
console.log("学习助手已启动");
// 控制状态 - 所有功能默认开启
let controlState = {
masterSwitch: true, // 总开关
autoPlay: true, // 自动播放
autoSkipTest: true, // 自动跳过答题
autoNext: true, // 自动跳转下一个视频
playbackRate: 2 // 播放速度 (0.5, 1, 1.5, 2)
};
let interval1;
let interval2;
let testShow = false;
let floatingWindow = null;
let controlPanel = null;
let playbackRateInterval = null;
let videoCheckInterval = null;
// 创建悬浮窗和控制面板
function createFloatingUI() {
// 主悬浮按钮
floatingWindow = document.createElement('div');
floatingWindow.id = 'study-assistant-float';
floatingWindow.innerHTML = `
`;
// 添加样式
const style = document.createElement('style');
style.textContent = `
#study-assistant-float {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 10000;
font-family: 'HarmonyOS Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
opacity: 0.7;
transition: opacity 0.3s ease;
}
#study-assistant-float:hover {
opacity: 1;
}
.assistant-status {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
transition: all 0.3s ease;
border: 2px solid #fff;
}
.assistant-status:hover {
transform: scale(1.1);
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.4);
}
.assistant-status.inactive {
background: linear-gradient(135deg, #8c8c8c 0%, #595959 100%);
box-shadow: 0 4px 12px rgba(140, 140, 140, 0.3);
}
.status-circle {
width: 12px;
height: 12px;
border-radius: 50%;
background: #fff;
transition: all 0.3s ease;
}
.assistant-status.active .status-circle {
background: #52c41a;
box-shadow: 0 0 8px #52c41a;
}
.control-panel {
position: absolute;
bottom: 60px;
left: 0;
width: 200px;
background: #fff;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
border: 1px solid #e8e8e8;
padding: 16px;
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.panel-header {
font-size: 14px;
font-weight: 600;
color: #262626;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
}
.control-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 13px;
color: #595959;
}
.control-item:last-child {
margin-bottom: 0;
}
.hw-switch {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
}
.hw-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #ccc;
transition: .4s;
border-radius: 20px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
}
input:checked + .slider:before {
transform: translateX(16px);
}
.speed-control {
flex-direction: column;
align-items: flex-start;
}
.speed-buttons {
display: flex;
gap: 4px;
margin-top: 8px;
width: 100%;
}
.speed-btn {
flex: 1;
padding: 6px 0;
border: 1px solid #d9d9d9;
background: #fff;
border-radius: 6px;
font-size: 12px;
color: #595959;
cursor: pointer;
transition: all 0.3s ease;
}
.speed-btn:hover {
border-color: #1890ff;
color: #1890ff;
}
.speed-btn.active {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
color: #fff;
border-color: #1890ff;
}
`;
document.head.appendChild(style);
document.body.appendChild(floatingWindow);
// 获取控制元素
controlPanel = document.getElementById('control-panel');
const statusButton = document.getElementById('assistant-status');
const masterSwitch = document.getElementById('master-switch');
const autoPlaySwitch = document.getElementById('auto-play');
const autoSkipSwitch = document.getElementById('auto-skip');
const autoNextSwitch = document.getElementById('auto-next');
const speedButtons = document.querySelectorAll('.speed-btn');
// 悬浮按钮点击事件
statusButton.addEventListener('click', function(e) {
e.stopPropagation();
controlPanel.style.display = controlPanel.style.display === 'none' ? 'block' : 'none';
});
// 总开关事件
masterSwitch.addEventListener('change', function() {
controlState.masterSwitch = this.checked;
if (controlState.masterSwitch) {
// 开启总开关时,自动开启所有子功能
controlState.autoPlay = true;
controlState.autoSkipTest = true;
controlState.autoNext = true;
autoPlaySwitch.checked = true;
autoSkipSwitch.checked = true;
autoNextSwitch.checked = true;
// 启动所有功能
startAllFunctions();
} else {
// 关闭总开关时,关闭所有功能
controlState.autoPlay = false;
controlState.autoSkipTest = false;
controlState.autoNext = false;
autoPlaySwitch.checked = false;
autoSkipSwitch.checked = false;
autoNextSwitch.checked = false;
// 停止所有功能
stopAllFunctions();
}
updateStatusDisplay();
});
// 自动播放开关事件
autoPlaySwitch.addEventListener('change', function() {
controlState.autoPlay = this.checked;
updateMasterSwitchState();
});
// 跳过答题开关事件
autoSkipSwitch.addEventListener('change', function() {
controlState.autoSkipTest = this.checked;
updateMasterSwitchState();
});
// 自动下一节开关事件
autoNextSwitch.addEventListener('change', function() {
controlState.autoNext = this.checked;
updateMasterSwitchState();
});
// 倍速按钮事件
speedButtons.forEach(button => {
button.addEventListener('click', function() {
const speed = parseFloat(this.dataset.speed);
controlState.playbackRate = speed;
// 更新按钮状态
speedButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
// 应用播放速度
applyPlaybackRate();
});
});
// 点击页面其他地方关闭控制面板
document.addEventListener('click', function() {
controlPanel.style.display = 'none';
});
// 阻止控制面板内部点击事件冒泡
controlPanel.addEventListener('click', function(e) {
e.stopPropagation();
});
// 启动播放速度监控
startPlaybackRateMonitor();
// 启动视频检查
startVideoCheck();
}
// 启动播放速度监控
function startPlaybackRateMonitor() {
if (playbackRateInterval) {
clearInterval(playbackRateInterval);
}
playbackRateInterval = setInterval(() => {
updateSpeedButtonsFromVideo();
}, 1000);
}
// 启动视频检查,确保倍速设置生效
function startVideoCheck() {
if (videoCheckInterval) {
clearInterval(videoCheckInterval);
}
videoCheckInterval = setInterval(() => {
ensurePlaybackRate();
}, 1500);
}
// 确保播放速度设置生效
function ensurePlaybackRate() {
if (!controlState.masterSwitch) return;
const video = document.querySelector('video');
if (video && Math.abs(video.playbackRate - controlState.playbackRate) > 0.1) {
console.log(`检测到播放速度被重置为 ${video.playbackRate}x,重新设置为 ${controlState.playbackRate}x`);
applyPlaybackRate();
}
}
// 根据视频实际速度更新倍速按钮状态
function updateSpeedButtonsFromVideo() {
const video = document.querySelector('video');
if (!video) return;
const currentRate = video.playbackRate;
const speedButtons = document.querySelectorAll('.speed-btn');
// 找到与当前播放速度匹配的按钮
let foundMatch = false;
speedButtons.forEach(button => {
const buttonSpeed = parseFloat(button.dataset.speed);
if (Math.abs(buttonSpeed - currentRate) < 0.1) {
button.classList.add('active');
foundMatch = true;
} else {
button.classList.remove('active');
}
});
// 如果没有找到匹配的按钮,更新控制状态
if (!foundMatch) {
controlState.playbackRate = currentRate;
}
}
// 启动所有功能
function startAllFunctions() {
// 确保定时器已清除
clearIntervals();
// 重新设置定时器
interval1 = setInterval(checkVideo, 2000);
interval2 = setInterval(playNext, 2000);
// 立即应用播放速度,并设置定期检查
setTimeout(applyPlaybackRate, 500);
setTimeout(applyPlaybackRate, 1500);
setTimeout(applyPlaybackRate, 3000);
console.log('所有功能已启动');
}
// 停止所有功能
function stopAllFunctions() {
// 清除定时器
clearIntervals();
// 暂停视频
pauseVideo();
console.log('所有功能已停止');
}
// 更新总开关状态(根据子开关状态)
function updateMasterSwitchState() {
const masterSwitch = document.getElementById('master-switch');
// 如果有任何子开关开启,则自动开启总开关
if (controlState.autoPlay || controlState.autoSkipTest || controlState.autoNext) {
if (!controlState.masterSwitch) {
controlState.masterSwitch = true;
masterSwitch.checked = true;
startAllFunctions();
}
} else {
// 所有子开关都关闭时,关闭总开关
if (controlState.masterSwitch) {
controlState.masterSwitch = false;
masterSwitch.checked = false;
stopAllFunctions();
}
}
updateStatusDisplay();
}
// 更新状态显示
function updateStatusDisplay() {
const statusButton = document.getElementById('assistant-status');
if (controlState.masterSwitch) {
statusButton.classList.add('active');
statusButton.classList.remove('inactive');
} else {
statusButton.classList.remove('active');
statusButton.classList.add('inactive');
}
}
// 应用播放速度
function applyPlaybackRate() {
const video = document.querySelector('video');
if (video) {
video.playbackRate = controlState.playbackRate;
console.log(`播放速度设置为: ${controlState.playbackRate}倍速`);
// 更新按钮状态
updateSpeedButtonsFromVideo();
}
}
// 暂停视频
function pauseVideo() {
const video = document.querySelector('video');
if (video && !video.paused) {
video.pause();
console.log('视频已暂停');
}
}
// 清除定时器
function clearIntervals() {
if (interval1) {
clearInterval(interval1);
interval1 = null;
}
if (interval2) {
clearInterval(interval2);
interval2 = null;
}
if (videoCheckInterval) {
clearInterval(videoCheckInterval);
videoCheckInterval = null;
}
}
// 原有功能函数
function closeTest() {
if (!controlState.autoSkipTest || !controlState.masterSwitch) return;
const dialog = document.querySelector('.el-dialog__body');
if (dialog) {
const exitBtn = dialog.querySelector('.exitBtn');
if (exitBtn){
exitBtn.click();
testShow = true;
console.log('已跳过答题');
}
}
}
function isEnd() {
const currentTime = document.querySelector('.vjs-current-time-display');
const totalTime = document.querySelector('.vjs-duration-display');
if (currentTime && totalTime) {
return currentTime.innerText === totalTime.innerText;
}
return false;
}
function checkVideo() {
if (!controlState.autoPlay || !controlState.masterSwitch) return;
if (isEnd() || testShow) {
return;
}
if (document.querySelector('.vjs-paused')) {
const videoBox = document.querySelector('.vjs-icon-placeholder');
if (videoBox) {
videoBox.click();
console.log('检测到暂停,已自动播放');
// 播放后重新设置倍速,确保生效
setTimeout(applyPlaybackRate, 500);
}
}
}
function playNext() {
if (!controlState.autoNext || !controlState.masterSwitch) return;
if(isEnd() || testShow) {
const wrap = document.querySelector('div[class$="CourceList-wrap"]');
if (!wrap) return;
const list = wrap.querySelector('.course-list-warp');
if (!list) return;
for (let course of list.children) {
const stateEl = course.querySelector('.state-paused');
const courseNameEl = course.querySelector('.top-title');
const videoNameEl = document.querySelector('.path');
if (!stateEl || !courseNameEl || !videoNameEl) continue;
const state = stateEl.innerText;
const courseName = courseNameEl.innerText;
const videoName = videoNameEl.innerText;
if (state === '未学习' || state === '学习中') {
if (courseName === videoName) {
location.reload();
continue;
}
const playVideo = course.querySelector('.play');
if (playVideo) {
playVideo.click();
console.log('自动跳转到下一节视频');
// 跳转后等待视频加载,然后设置倍速
setTimeout(() => {
setTimeout(applyPlaybackRate, 1000);
setTimeout(applyPlaybackRate, 3000);
}, 1000);
break;
}
}
}
let listOver = true;
for (let course of list.children) {
const stateEl = course.querySelector('.state-paused');
if (stateEl && (stateEl.innerText === '未学习' || stateEl.innerText === '学习中')) {
listOver = false;
}
}
if (listOver) {
const rightButton = wrap.querySelector('.right button');
if (rightButton && rightButton.classList.contains('is-disabled')) {
clearInterval(interval2);
console.log('所有课程已完成');
} else if (rightButton) {
rightButton.click();
console.log('切换到下一组课程');
}
}
}
}
// 初始化
window.onload = function(event) {
// 创建悬浮UI
createFloatingUI();
// 设置定时器(默认开启状态,所以启动所有功能)
startAllFunctions();
setInterval(closeTest, 3000);
console.log('学习助手已完全加载');
};
})();