// ==UserScript== // @name 联大学堂 自主计时70%+OCR答题(原生强制提交)【播放才计时+停止不重置+立刻显示总长】 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 播放才计时 + 停止不重置 + 页面加载立刻显示视频总长 + OCR图片识别答题(适配答案解析图) // @match *://*.jxjypt.cn/* // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1.min.js // @require https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js // @grant GM_xmlhttpRequest // @run-at document-end // ==/UserScript== (function () { 'use strict'; // ====================== OCR 识别函数 ====================== async function recognizeImage(file) { const { data } = await Tesseract.recognize(file, 'chi_sim+eng'); let text = data.text; // 修复常见识别错误 text = text.replace(/4/g, 'A'); text = text.replace(/©/g, 'C'); text = text.replace(/×/g, 'x'); return text; } // ====================== 面板UI ====================== function createPanel() { let panel = document.createElement('div'); panel.id = 'autoStudyPanel'; panel.style.cssText = ` position:fixed;top:140px;right:20px;z-index:999999; background:#fff;padding:12px;border-radius:10px;box-shadow:0 0 10px rgba(0,0,0,0.2); width:220px;max-height:80vh;overflow-y:auto; `; panel.innerHTML = `
🎯 自主计时70%+OCR答题
计时:00:00
总长:00:00
进度:0%
状态:等待启动
OCR状态:未获取
📋 题目&答案:
`; document.body.appendChild(panel); } let isRunning = false; let timer = 0; let total = 0; let interval = null; let jumped = false; let aiAlreadyCalled = false; let autoAnswered = false; function fmt(s) { let m = Math.floor(s / 60); let sec = Math.floor(s % 60); return `${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`; } function closePopups() { try { $("button:contains('确定'),button:contains('关闭'),.close,.layui-layer-close").click(); $(".layui-layer,.modal,.popup").hide(); } catch (e) { } } // 立刻获取视频总时长 function loadDurationNow() { let checkVideo = setInterval(() => { let video = document.querySelector('video'); if (video && video.duration > 0) { total = video.duration; updateInfo(); clearInterval(checkVideo); } }, 100); } // 更新信息 function updateInfo() { let percent = total > 0 ? (timer / total) * 100 : 0; $('#videoInfo').html(`计时:${fmt(timer)}
总长:${fmt(total)}
进度:${percent.toFixed(1)}%`); } function loop() { if (!isRunning || jumped) return; closePopups(); let video = document.querySelector('video'); if (!video) return; // 自动播放 if (video.paused) video.play().catch(() => {}); // 播放才计时 if (!video.paused && !video.ended && video.readyState >= 4) { timer++; } updateInfo(); let percent = total > 0 ? (timer / total) * 100 : 0; if (percent >= 70 && !autoAnswered) { autoAnswered = true; $('#status').text('✅ 达到70%,OCR答题中...'); setTimeout(() => getAnswerOnce(), 1200); } } function goNext() { aiAlreadyCalled = false; autoAnswered = false; // 切换下一个视频时才清空 $('#questionDisplay').text(''); $('#answerStatus').text('OCR状态:未获取'); let cur = $('dd.z-color.z-class-icon').first(); if (!cur.length) return; let next = cur.next('dd'); if (next.length) next.click(); else { let nextChap = cur.closest('.course-list-txt').next().find('dd').first(); if (nextChap.length) nextChap.click(); } setTimeout(() => { timer = 0; total = 0; jumped = false; loadDurationNow(); $('#status').text('状态:运行中'); // 切换新视频后,自动显示题目 setTimeout(showQuestions, 1500); }, 4000); } function start() { isRunning = true; $('#status').text('状态:运行中'); clearInterval(interval); interval = setInterval(loop, 1000); } function stop() { isRunning = false; clearInterval(interval); $('#status').text('状态:已暂停(计时保留)'); let video = document.querySelector('video'); if (video) video.pause(); } // ====================== 提取题目 ====================== function getExamQuestions() { const result = []; document.querySelectorAll('.m-question').forEach(el => { const q = {}; q.title = el.querySelector('.sub-dotitle pre')?.innerText.trim() || '无题目'; q.options = {}; el.querySelectorAll('.m-question-option').forEach(opt => { const k = opt.getAttribute('data-value'); const v = opt.innerText.trim().replace(/^[A-Z]\./, '').trim(); if (k && v && !v.includes('null')) q.options[k] = v; }); result.push(q); }); return result; } function showQuestions() { const qs = getExamQuestions(); let txt = ''; if (!qs.length) txt = '未检测到题目'; else qs.forEach((q, i) => { txt += `第${i+1}题:${q.title}\n`; Object.entries(q.options).forEach(([k, v]) => txt += `${k}. ${v}\n`); txt += `——————————\n`; // 分隔线 }); $('#questionDisplay').text(txt); } // ====================== 核心:识别答案解析图 ====================== async function getImageAnswer() { try { // 先自动展开解析(显示答案图) $('.zkjx').click(); // 精准找到你网站的答案图片(.solution 下的 img) const img = document.querySelector('.solution img'); if (!img) { $('#answerStatus').text('OCR:未找到答案图'); return null; } console.log('识别答案图片:', img.src); // 转成 blob 供 OCR 使用 const response = await fetch(img.src); const blob = await response.blob(); // OCR 识别 const text = await recognizeImage(blob); // 🔥 关键:答案追加在题目后面,不覆盖 let oldText = $('#questionDisplay').text(); $('#questionDisplay').text(oldText + `\n✅ 识别答案:${text}\n`); // 提取答案 A/B/C/D const match = text.match(/[A-D]/); if (match) return match[0]; } catch (e) { console.error('OCR识别失败', e); $('#answerStatus').text('OCR:识别失败'); } return null; } function realSubmit() { setTimeout(() => { try { let submitBtn = document.getElementById('submitSelfBtn'); if (submitBtn) { $('#status').text('✅ 答案已提交,正在跳转'); submitBtn.click(); submitBtn.dispatchEvent(new Event('click', { bubbles: true })); } } catch (e) {} setTimeout(() => { jumped = true; goNext(); }, 1500); }, 1500); } async function getAnswerOnce() { if (aiAlreadyCalled) return; aiAlreadyCalled = true; $('#answerStatus').text('OCR状态:识别中...'); // 使用 OCR 获取答案 const ans = await getImageAnswer(); if (ans) { $('#answerStatus').text(`OCR状态:答案 ${ans}`); let opt = document.querySelector(`.m-question-option[data-value="${ans}"]`); if (opt) { opt.click(); opt.dispatchEvent(new Event('click', { bubbles: true })); } } else { $('#answerStatus').text('OCR状态:识别失败,跳过'); } realSubmit(); } // ====================== 初始化 ====================== $(function () { createPanel(); loadDurationNow(); // 🔥 页面加载完成自动显示题目 setTimeout(showQuestions, 800); $('#startBtn').click(start); $('#stopBtn').click(stop); $('#showQBtn').click(showQuestions); $('#getAnswerBtn').click(getAnswerOnce); }); })();