(function () {
// 检查页面是否已加载jQuery,如果没有则加载
if (typeof window.jQuery === 'undefined') {
const script = document.createElement('script');
script.src = 'https://code.jquery.com/jquery-3.6.0.min.js';
script.type = 'text/javascript';
script.onload = function () {
console.log("jQuery loaded.");
initializePlayer();
};
document.head.appendChild(script);
} else {
initializePlayer();
}
function initializePlayer() {
window.app = {
configs: {
playbackRate: 1,
autoplay: true,
retryInterval: 2000,
maxRetries: 10,
videoCheckInterval: 1000,
},
_videoEl: null,
_treeContainerEl: null,
_isPlaying: false,
_currentRetryCount: 0,
_checkInterval: null,
_cellData: {
cells: 0,
nCells: 0,
currentCellIndex: 0,
currentNCellIndex: 0,
currentVideoTitle: "",
},
_quizState: {
active: false,
optionsCount: 0,
attemptIndex: 0,
combinations: [],
lastSubmitTime: 0
},
get cellData() {
return this._cellData;
},
run() {
console.log("%c=== 学习通自动刷课脚本 V9 完美闭环版启动 ===", "color:#4CAF50;font-size:16px;font-weight:bold");
this._getTreeContainer();
this._initCellData();
this._videoEl = null;
this._getVideoEl();
this._clearCheckInterval();
this.play();
},
nextUnit() {
console.log("%c=== 准备切换到下一小节 ===", "color:#2196F3;font-size:14px");
const el = this._getTreeContainer();
const cells = el.children("ul").children("li");
const nCells = $(cells.get(this._cellData.currentCellIndex)).find('.posCatalog_select:not(.firstLayer)');
if (nCells.length > this._cellData.currentNCellIndex + 1) {
const nextNIndex = this._cellData.currentNCellIndex + 1;
console.log(`%c切换到同章节下一个视频: ${nextNIndex + 1}/${nCells.length}`, "color:#FF9800");
this.playCurrentIndex(nCells.get(nextNIndex));
} else {
const nextIndex = this._cellData.currentCellIndex + 1;
if (nextIndex >= cells.length) {
console.log("%c==============本课程学习完成了==============", "color:#4CAF50;font-size:16px;font-weight:bold");
this._clearCheckInterval();
return;
}
console.log(`%c切换到下一个章节: ${nextIndex + 1}/${cells.length}`, "color:#FF9800");
this._cellData.currentCellIndex = nextIndex;
this._cellData.currentNCellIndex = 0;
this.playCurrentIndex();
}
},
_clearCheckInterval() {
if (this._checkInterval) {
clearInterval(this._checkInterval);
this._checkInterval = null;
}
},
_startVideoMonitoring() {
this._clearCheckInterval();
this._checkInterval = setInterval(() => {
this._checkVideoStatus();
}, this.configs.videoCheckInterval);
},
_generateCombinations(numOptions, isMulti) {
const combos = [];
if (!isMulti) {
for (let i = 0; i < numOptions; i++) combos.push([i]);
} else {
const max = 1 << numOptions;
for (let i = 1; i < max; i++) {
const currentCombo = [];
for (let j = 0; j < numOptions; j++) {
if ((i & (1 << j)) !== 0) {
currentCombo.push(j);
}
}
combos.push(currentCombo);
}
combos.sort((a, b) => a.length - b.length);
}
return combos;
},
/// 【核心修复】:优先检测“继续学习”,再处理答题逻辑
_handleVideoQuiz() {
try {
const frameObj = $("iframe").eq(0).contents().find("iframe.ans-insertvideo-online");
if (frameObj.length === 0) {
this._quizState.active = false;
return false;
}
const doc = frameObj.contents();
if (doc[0].defaultView && !doc[0].defaultView._alertOverridden) {
doc[0].defaultView.alert = function(msg) {
console.log("%c拦截到系统弹窗: " + msg, "color:#9C27B0");
};
doc[0].defaultView._alertOverridden = true;
}
// ==========================================
// 步骤 1:最高优先级,寻找“通关”按钮(答对后出现的按钮)
// 只要出现这些按钮,不管选项还在不在,直接点!
// ==========================================
let continueBtn = null;
doc.find('.ans-videoquiz-button, button, a, span, div, input').each(function() {
const text = $(this).text().replace(/\s+/g, '');
const val = $(this).val() ? $(this).val().replace(/\s+/g, '') : '';
// 加入了“继续学习”作为首要匹配项
if (['继续学习', '继续', '关闭', '完成', '继续播放'].some(k => text === k || val === k) && $(this).is(':visible')) {
continueBtn = this;
return false; // 找到就退出循环
}
});
if (continueBtn) {
console.log("%c检测到 [继续学习/关闭] 按钮,执行二次点击以恢复播放...", "color:#4CAF50;font-weight:bold");
setTimeout(() => {
continueBtn.click();
continueBtn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
}, 500);
this._quizState.active = false; // 彻底重置答题状态
return true;
}
// ==========================================
// 步骤 2:如果没有通关按钮,说明还在【答题中】
// ==========================================
const visibleOptions = doc.find('input[type="radio"]:visible, input[type="checkbox"]:visible');
const isMulti = doc.find('input[type="checkbox"]:visible').length > 0;
// 频率控制锁
const now = Date.now();
if (now - this._quizState.lastSubmitTime < 2500) {
return true;
}
if (visibleOptions.length > 0) {
// 寻找提交按钮
let submitBtn = null;
doc.find('.ans-videoquiz-submit, .btn-submit, button, span, div, a, input').each(function() {
const text = $(this).text().replace(/\s+/g, '');
const val = $(this).val() ? $(this).val().replace(/\s+/g, '') : '';
if (['提交', '确定', '确认'].some(k => text === k || val === k) && $(this).is(':visible')) {
submitBtn = this;
return false;
}
});
// 初始化穷举序列
if (!this._quizState.active || this._quizState.optionsCount !== visibleOptions.length) {
this._quizState.active = true;
this._quizState.optionsCount = visibleOptions.length;
this._quizState.attemptIndex = 0;
this._quizState.combinations = this._generateCombinations(visibleOptions.length, isMulti);
console.log(`%c检测到新题目,生成 ${this._quizState.combinations.length} 种组合方案...`, "color:#FF9800;font-weight:bold");
}
// 如果所有组合都试完了
if (this._quizState.attemptIndex >= this._quizState.combinations.length) {
console.error("%c所有选项已穷举完毕,可能题目异常,暂停尝试", "color:#F44336;font-weight:bold");
this._quizState.lastSubmitTime = now + 10000;
return true;
}
// 勾选选项
const currentCombo = this._quizState.combinations[this._quizState.attemptIndex];
if (isMulti) visibleOptions.prop('checked', false);
currentCombo.forEach(idx => visibleOptions.get(idx).click());
console.log(`%c尝试选中组合序号: ${currentCombo.join(',')}`, "color:#2196F3");
// 执行点击提交
if (submitBtn) {
setTimeout(() => {
submitBtn.click();
submitBtn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
console.log("%c已点击 [提交/确定] 按钮", "color:#9C27B0;font-weight:bold");
}, 500);
} else {
console.log("%c正在寻找提交按钮,可能尚未渲染...", "color:#607D8B");
}
this._quizState.attemptIndex++;
this._quizState.lastSubmitTime = now;
return true;
}
if (this._quizState.active) {
this._quizState.active = false;
}
return false;
} catch (e) {
console.error("检测/处理弹题失败:", e);
return false;
}
},
_checkVideoStatus() {
try {
const video = this._getVideoEl();
if (!video) return;
if (video.paused && this._isPlaying) {
if (this._handleVideoQuiz()) return;
console.log("%c检测到视频暂停,尝试恢复播放...", "color:#FF5722");
video.play().catch(e => console.error("恢复播放失败:", e));
}
if (video.ended && this._isPlaying) {
console.log("%c检测到视频结束,准备切换下一个...", "color:#9C27B0");
this._isPlaying = false;
setTimeout(() => this.nextUnit(), 1000);
}
} catch (e) {
console.error("视频状态检查失败:", e);
}
},
_tryTimes: 0,
async play() {
try {
const el = this._getVideoEl();
if (el == null) {
if (document.getElementsByClassName("prev_title")[0] &&
document.getElementsByClassName("prev_title")[0].title !== "章节测验") {
throw new Error("视频元素为空");
}
$("#prevNextFocusNext").click();
setTimeout(() => this.play(), 2000);
return;
}
this._tryTimes = 0;
this._isPlaying = true;
this._videoEventHandle();
el.playbackRate = this.configs.playbackRate;
try {
await el.play();
console.log(`%c视频开始播放,倍速: ${el.playbackRate}x`, "color:#4CAF50");
this._startVideoMonitoring();
} catch (playError) {
console.error("视频播放失败:", playError);
this._handlePlayError(playError);
}
} catch (e) {
if (this._tryTimes > this.configs.maxRetries) {
this._clearCheckInterval();
return;
}
this._tryTimes++;
setTimeout(() => this.play(), this.configs.retryInterval);
}
},
_handlePlayError(error) {
const video = this._getVideoEl();
if (video) {
video.muted = true;
video.play().then(() => {
console.log("%c静音播放成功", "color:#4CAF50");
setTimeout(() => { video.muted = false; }, 2000);
}).catch(e => {
setTimeout(() => this.nextUnit(), 3000);
});
}
},
playCurrentIndex(nCell) {
if (!nCell) {
const el = this._getTreeContainer();
const cells = el.children("ul").children("li");
const nCells = $(cells.get(this._cellData.currentCellIndex)).find('.posCatalog_select:not(.firstLayer)');
nCell = nCells.get(this._cellData.currentNCellIndex);
}
const $nCell = $(nCell);
const clickableSpan = $nCell.find(".posCatalog_name")[0];
if (!clickableSpan) {
setTimeout(() => this.nextUnit(), 2000);
return;
}
$(clickableSpan).click();
this._videoEl = null;
this._isPlaying = false;
setTimeout(() => {
this._initCellData();
if (this.configs.autoplay) this.play();
}, 3000);
},
_initCellData() {
const el = this._getTreeContainer();
const cells = el.children("ul").children("li");
this._cellData.cells = cells.length;
let nCellCounts = 0;
let foundCurrent = false;
cells.each((i, v) => {
const nCells = $(v).find('.posCatalog_select:not(.firstLayer)');
nCellCounts += nCells.length;
nCells.each((j, e) => {
const _el = $(e);
if (_el.hasClass("posCatalog_active")) {
this._cellData.currentCellIndex = i;
this._cellData.currentNCellIndex = j;
foundCurrent = true;
const titleSpan = _el.find('.posCatalog_name')[0];
if (titleSpan) {
this._cellData.currentVideoTitle = $(titleSpan).attr('title');
}
}
});
});
this._cellData.nCells = nCellCounts;
},
_getTreeContainer() {
if (!this._treeContainerEl) {
const el = $('#coursetree');
if (el.length <= 0) throw new Error("找不到视频列表");
this._treeContainerEl = el;
}
return this._treeContainerEl;
},
_getVideoEl() {
if (!this._videoEl) {
try {
const frameObj = $("iframe").eq(0).contents().find("iframe.ans-insertvideo-online");
if (frameObj.length === 0) return null;
this._videoEl = frameObj.contents().eq(0).find("video#video_html5_api").get(0);
} catch (e) {
return null;
}
}
if (!this._videoEl) throw new Error("视频组件Video未加载完成");
return this._videoEl;
},
_videoEventHandle() {
const el = this._videoEl;
if (!el) return;
el.removeEventListener("ended", this._handleVideoEnded);
el.removeEventListener("loadedmetadata", this._handleVideoLoaded);
el.removeEventListener("play", this._handleVideoPlay);
el.removeEventListener("pause", this._handleVideoPause);
el.addEventListener("ended", this._handleVideoEnded.bind(this));
el.addEventListener("loadedmetadata", this._handleVideoLoaded.bind(this));
el.addEventListener("play", this._handleVideoPlay.bind(this));
el.addEventListener("pause", this._handleVideoPause.bind(this));
},
_handleVideoEnded(e) {
this._isPlaying = false;
this._clearCheckInterval();
setTimeout(() => this.nextUnit(), 1000);
},
_handleVideoLoaded(e) {
if (this.configs.autoplay && !this._isPlaying) this.play();
},
_handleVideoPlay(e) {
this._isPlaying = true;
},
_handleVideoPause(e) {}
};
try {
window.app.run();
const preventPause = (e) => { e.stopPropagation(); e.preventDefault(); };
document.addEventListener("mouseleave", preventPause);
window.addEventListener("mouseleave", preventPause);
document.addEventListener("mouseout", preventPause);
window.addEventListener("mouseout", preventPause);
window.addEventListener("blur", (e) => {
const video = window.app._getVideoEl();
if (video && video.paused && window.app._isPlaying) {
video.play().catch(err => {});
}
});
} catch (error) {
console.error("%c脚本运行失败: ", "color:#F44336;font-weight:bold", error.message);
}
}
})();``