// ==UserScript==
// @name 巴中继续教育自动切课(低检测优化版)
// @namespace http://tampermonkey.net/
// @version 0.4.0
// @description 跨大节自动切课+倍速隐藏+真人行为模拟,降低检测概率
// @author 自用辅助
// @match *://bzys.jjyxt.cn/*
// @grant none
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// ========== 配置区 ==========
const CONFIG = {
targetPlaybackRate: 2.0, // 目标倍速,建议1.2-1.5更安全
rateFluctuation: 0.2, // 倍速随机波动幅度
rateStep: 0.1,
skipSec: 10,
autoResume: true,
autoMuted: true,
autoSkipPopup: true,
// 随机暂停间隔(毫秒):6-15分钟
pauseIntervalMin: 360000,
pauseIntervalMax: 900000,
// 随机暂停时长(毫秒):1-3秒
pauseDurationMin: 1000,
pauseDurationMax: 3000,
// 切课延迟随机范围(毫秒)
nextDelayMin: 1000,
nextDelayMax: 4000,
nextBtnKeywords: ['下一节', '下一课', '下一章节', '下一章', '继续学习', '下一个'],
chapterSelector: '.chapter-item, .section-item, .course-item, li.course, .list-item',
activeClass: 'active, current, playing, now, on'
};
// =================================================
let videoDom = null;
let domObserver = null;
let isJumping = false;
let toastTimer = null;
let pauseTimer = null;
// ========== 前置:劫持playbackRate隐藏真实倍速 ==========
(function hidePlaybackRate() {
const proto = HTMLMediaElement.prototype;
const originalDesc = Object.getOwnPropertyDescriptor(proto, 'playbackRate');
if (!originalDesc) return;
let realRate = 1.0;
Object.defineProperty(proto, 'playbackRate', {
get: function() {
// 页面读取时返回1.0
return 1.0;
},
set: function(value) {
realRate = value;
originalDesc.set.call(this, value);
},
configurable: true,
enumerable: true
});
})();
// 悬浮提示
function showToast(text) {
let toast = document.getElementById('helper-toast');
if (!toast) {
toast = document.createElement('div');
toast.id = 'helper-toast';
toast.style.cssText = `
position: fixed; top: 80px; left: 50%; transform: translateX(-50%);
background: rgba(0,0,0,0.75); color: #fff; padding: 8px 16px;
border-radius: 6px; font-size: 14px; z-index: 99999;
pointer-events: none; transition: opacity 0.3s;
`;
document.body.appendChild(toast);
}
toast.textContent = text;
toast.style.opacity = '1';
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toast.style.opacity = '0', 1200);
}
// 右下角状态面板
function createStatusPanel() {
let panel = document.getElementById('study-status-panel');
if (panel) return panel;
panel = document.createElement('div');
panel.id = 'study-status-panel';
panel.style.cssText = `
position: fixed; right: 10px; bottom: 10px; background: rgba(0,0,0,0.65);
color: #fff; padding: 10px; border-radius: 8px; font-size:12px;
z-index:99998; min-width:140px; line-height:1.6;
`;
panel.innerHTML = `
【学习辅助状态】
目标倍速:${CONFIG.targetPlaybackRate}x
播放:00:00/00:00
静音:否
H=快捷键说明
`;
document.body.appendChild(panel);
return panel;
}
function updateStatusPanel() {
if (!videoDom) return;
const panel = createStatusPanel();
panel.querySelector('#rate-line').innerText = `目标倍速:${CONFIG.targetPlaybackRate}x`;
panel.querySelector('#mute-line').innerText = `静音:${videoDom.muted ? "是" : "否"}`;
const cur = formatTime(videoDom.currentTime);
const total = formatTime(videoDom.duration || 0);
panel.querySelector('#time-line').innerText = `播放:${cur}/${total}`;
}
function formatTime(s) {
if (isNaN(s)) return "00:00";
let m = Math.floor(s / 60);
let sec = Math.floor(s % 60);
return `${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
}
function showHelpInfo() {
const info = `
快捷键列表:
C:加速 +0.1 | X:减速 -0.1
Z:重置1倍速 | N:手动切下一课
←→:进退${CONFIG.skipSec}秒 | 空格:播放/暂停
M:切换静音 | Ctrl↑↓:调音量
F:全屏 | H:关闭此提示
`.trim();
alert(info);
}
// 随机数工具
function random(min, max) {
return Math.random() * (max - min) + min;
}
// 切课逻辑
function clickNextByButton() {
const els = document.querySelectorAll('a, button, div[role="button"], span');
for (const el of els) {
const txt = el.innerText?.trim() || '';
const match = CONFIG.nextBtnKeywords.some(k => txt.includes(k));
const vis = el.offsetParent !== null;
const dis = el.disabled || el.classList.contains('disabled');
if (match && vis && !dis) {
el.click();
return true;
}
}
return false;
}
function clickNextByCatalog() {
const items = Array.from(document.querySelectorAll(CONFIG.chapterSelector));
if (!items.length) return false;
const activeCls = CONFIG.activeClass.split(',').map(c => c.trim());
let currItem = null;
for (const cls of activeCls) {
currItem = document.querySelector(`${CONFIG.chapterSelector}.${cls}`);
if (currItem) break;
}
if (!currItem) currItem = items.find(i => i.innerText.match(/正在播放|学习中/));
if (!currItem) return false;
const idx = items.indexOf(currItem);
if (idx === -1) return false;
const nextIdx = idx + 1;
if (nextIdx >= items.length) {
showToast('🎉 全部课程学习完毕');
return true;
}
items[nextIdx].click();
return true;
}
function goToNextChapter() {
if (isJumping) return;
isJumping = true;
const delay = random(CONFIG.nextDelayMin, CONFIG.nextDelayMax);
showToast(`${(delay/1000).toFixed(1)}秒后自动跳转下一课`);
setTimeout(() => {
clickNextByButton() || clickNextByCatalog();
setTimeout(() => isJumping = false, 3000);
}, delay);
}
// 倍速控制(带随机波动)
function setFluctuationRate() {
if (!videoDom) return;
const base = CONFIG.targetPlaybackRate;
const fluct = CONFIG.rateFluctuation;
const rate = base + random(-fluct, fluct);
const finalRate = Math.max(0.5, Math.min(3, rate));
// 直接调用原生设置,不走被劫持的setter
const originalDesc = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate');
originalDesc.set.call(videoDom, finalRate);
}
function adjustRate(delta) {
if (!videoDom) return;
CONFIG.targetPlaybackRate = Math.max(0.5, Math.min(3, CONFIG.targetPlaybackRate + delta));
setFluctuationRate();
showToast(`目标倍速:${CONFIG.targetPlaybackRate.toFixed(1)}x`);
}
// 随机暂停模拟真人
function startRandomPause() {
clearTimeout(pauseTimer);
const nextPause = random(CONFIG.pauseIntervalMin, CONFIG.pauseIntervalMax);
pauseTimer = setTimeout(() => {
if (!videoDom || videoDom.paused || isJumping) {
startRandomPause();
return;
}
videoDom.pause();
const duration = random(CONFIG.pauseDurationMin, CONFIG.pauseDurationMax);
setTimeout(() => {
videoDom.play().catch(() => {});
startRandomPause();
}, duration);
}, nextPause);
}
function videoSkip(sec) {
if (!videoDom) return;
videoDom.currentTime = Math.max(0, Math.min(videoDom.duration, videoDom.currentTime + sec));
showToast(sec>0 ? `前进${sec}秒` : `后退${Math.abs(sec)}秒`);
}
function togglePlay() {
if (!videoDom) return;
if(videoDom.paused) videoDom.play();
else videoDom.pause();
showToast(videoDom.paused ? "已暂停" : "开始播放");
}
function toggleMute() {
if (!videoDom) return;
videoDom.muted = !videoDom.muted;
showToast(videoDom.muted ? "已静音" : "取消静音");
}
function volumeChange(delta) {
if (!videoDom) return;
videoDom.volume = Math.max(0, Math.min(1, videoDom.volume + delta));
showToast(`音量:${(videoDom.volume*100).toFixed(0)}%`);
}
function toggleFullscreen() {
if (!videoDom) return;
if(document.fullscreenElement) document.exitFullscreen();
else videoDom.requestFullscreen();
}
// 视频绑定
function bindVideoEvent() {
const videos = document.querySelectorAll('video');
if (!videos.length) return false;
videoDom = videos[0];
videoDom.removeEventListener('ended', handleVideoEnd);
videoDom.addEventListener('ended', handleVideoEnd);
// 初始先1倍速播放5秒,再升到目标倍速
const originalDesc = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate');
originalDesc.set.call(videoDom, 1.0);
setTimeout(() => {
setFluctuationRate();
// 每4分钟刷新一次波动倍速
setInterval(setFluctuationRate, 240000);
}, 5000);
videoDom.muted = CONFIG.autoMuted;
videoDom.autoplay = true;
if (CONFIG.autoResume) {
videoDom.removeEventListener('pause', handleVideoPause);
videoDom.addEventListener('pause', handleVideoPause);
}
setInterval(updateStatusPanel, 1000);
startRandomPause();
console.log(`[视频绑定完成] 目标倍速${CONFIG.targetPlaybackRate}x`);
return true;
}
function handleVideoEnd() {
goToNextChapter();
}
function handleVideoPause() {
if (isJumping) return;
setTimeout(() => videoDom?.play().catch(() => {}), 800);
}
// 全局快捷键
function initHotkeys() {
document.addEventListener('keydown', e => {
const tag = document.activeElement?.tagName;
if (['INPUT','TEXTAREA','SELECT'].includes(tag)) return;
const key = e.key.toLowerCase();
if(e.ctrlKey){
if(key === 'arrowup') {e.preventDefault();volumeChange(0.1);return;}
if(key === 'arrowdown') {e.preventDefault();volumeChange(-0.1);return;}
}
switch(key){
case 'c': e.preventDefault(); adjustRate(CONFIG.rateStep); break;
case 'x': e.preventDefault(); adjustRate(-CONFIG.rateStep); break;
case 'z': e.preventDefault(); CONFIG.targetPlaybackRate = 1; setFluctuationRate(); showToast("已重置为1倍速"); break;
case 'n': e.preventDefault(); goToNextChapter(); break;
case 'arrowleft': e.preventDefault(); videoSkip(-CONFIG.skipSec); break;
case 'arrowright': e.preventDefault(); videoSkip(CONFIG.skipSec); break;
case ' ': e.preventDefault(); togglePlay(); break;
case 'm': e.preventDefault(); toggleMute(); break;
case 'f': e.preventDefault(); toggleFullscreen(); break;
case 'h': e.preventDefault(); showHelpInfo(); break;
}
})
}
// 自动弹窗关闭
function autoSkipPopup() {
if (!CONFIG.autoSkipPopup) return;
const btns = document.querySelectorAll('button,a,.el-button,.confirm-btn');
btns.forEach(b=>{
const t = b.innerText?.trim()||'';
if(['确定','知道了','继续','确认'].includes(t) && b.offsetParent) b.click();
})
}
// DOM监听
function initObserver() {
domObserver = new MutationObserver(()=>{
if(!videoDom || !document.body.contains(videoDom)) bindVideoEvent();
autoSkipPopup();
})
domObserver.observe(document.body,{childList:true,subtree:true})
}
function init() {
bindVideoEvent() || initObserver();
initHotkeys();
autoSkipPopup();
createStatusPanel();
console.log('[低检测优化版脚本加载完成,按H查看快捷键]');
}
window.addEventListener('load', ()=>setTimeout(init, 1200));
if(['complete','interactive'].includes(document.readyState)) setTimeout(init, 1200);
})();