// ==UserScript== // @name 柠檬文才学习平台自动刷课【全自动】 // @namespace `https://learning.wencaischool.net/` // @namespace 一心向善 // @version 1.1 // @description 全自动完成刷视频章节,支持自动化下一节【如需答题、考试请联系作者有偿代处理】 // @match *://learning.wencaischool.net/* // @match *://site.wencaischool.net/* // @match *://*.wencaischool.net/* // @grant none // ==/UserScript== (function() { 'use strict'; if (window.__studyHelper) { console.log('🎓 已有脚本在运行,跳过初始化'); return; } console.log('🎓 天府文才自动刷课 v1.0 启动!'); console.log('📌 当前URL:', location.href); const config = { speed: 2.0, autoPlay: true, autoNext: true, debug: true }; function log(msg, type = 'info') { if (config.debug || type !== 'info') { const prefix = type === 'error' ? '❌' : type === 'success' ? '✅' : type === 'warning' ? '⚠️' : '🔍'; console.log(`${prefix} ${msg}`); } } function createUI() { const existingPanel = window.top.document.getElementById('study-helper-v11'); if (existingPanel) { log('⚠️ 主页面已有控制面板,跳过创建', 'warning'); return; } if (window !== window.top) { log('⚠️ 当前是iframe,不创建控制面板', 'warning'); return; } const panel = document.createElement('div'); panel.id = 'study-helper-v11'; panel.innerHTML = `
🎬 天府文才视频助手
v1.0
播放速度 (最高16x):
📊 状态:
运行中
1、本脚本仅适用于处理视频;
2、如需处理答题、考试等联系作者有偿代处理:TFJYPXJG
`; document.body.appendChild(panel); // 初始化拖拽功能 initDrag(); document.getElementById('speed-sel').addEventListener('change', function() { config.speed = parseFloat(this.value); applySpeed(); updateStatus(`速度: ${config.speed}x`, 'success'); }); document.getElementById('autoplay-cb').addEventListener('change', function() { config.autoPlay = this.checked; updateStatus(config.autoPlay ? '自动播放 开启' : '自动播放 关闭', 'info'); }); document.getElementById('autonext-cb').addEventListener('change', function() { config.autoNext = this.checked; updateStatus(config.autoNext ? '自动切换 开启' : '自动切换 关闭', 'info'); }); log('✅ UI创建成功', 'success'); } function initDrag() { const panel = document.getElementById('study-panel-drag'); if (!panel) return; let isDragging = false; let startX, startY, panelX, panelY; panel.addEventListener('mousedown', function(e) { isDragging = true; startX = e.clientX; startY = e.clientY; panelX = panel.offsetLeft; panelY = panel.offsetTop; panel.style.cursor = 'grabbing'; panel.style.zIndex = '999999999'; }); document.addEventListener('mousemove', function(e) { if (!isDragging) return; const dx = e.clientX - startX; const dy = e.clientY - startY; let newX = panelX + dx; let newY = panelY + dy; const maxX = window.innerWidth - panel.offsetWidth; const maxY = window.innerHeight - panel.offsetHeight; newX = Math.max(0, Math.min(newX, maxX)); newY = Math.max(0, Math.min(newY, maxY)); panel.style.left = newX + 'px'; panel.style.top = newY + 'px'; panel.style.right = 'auto'; }); document.addEventListener('mouseup', function() { if (isDragging) { isDragging = false; panel.style.cursor = 'move'; } }); panel.addEventListener('mouseup', function() { panel.style.cursor = 'move'; }); } function updateStatus(text, type) { if (window.top !== window) { const el = window.top.document.getElementById('status-txt'); if (el) { el.textContent = text; el.style.color = type === 'success' ? '#4ade80' : type === 'warning' ? '#fbbf24' : type === 'error' ? '#ef4444' : '#60a5fa'; } } else { const el = document.getElementById('status-txt'); if (el) { el.textContent = text; el.style.color = type === 'success' ? '#4ade80' : type === 'warning' ? '#fbbf24' : type === 'error' ? '#ef4444' : '#60a5fa'; } } log(text, type); } function updateInfo(text) { if (window.top !== window) { const el = window.top.document.getElementById('info-txt'); if (el) el.textContent = text; } else { const el = document.getElementById('info-txt'); if (el) el.textContent = text; } } function updateChapter(text) { if (window.top !== window) { const el = window.top.document.getElementById('chapter-txt'); if (el) el.textContent = text; } else { const el = document.getElementById('chapter-txt'); if (el) el.textContent = text; } } function findVideo() { const frames = document.querySelectorAll('iframe'); for (let frame of frames) { try { const v = frame.contentDocument.querySelector('video'); if (v) { log('📺 在iframe中找到视频', 'success'); return v; } } catch (e) {} } const videos = document.querySelectorAll('video'); if (videos.length > 0) { for (let v of videos) if (v.offsetParent !== null || v.style.display !== 'none') return v; return videos[0]; } return null; } function applySpeed() { const v = findVideo(); if (v) v.playbackRate = config.speed; } // ============== 改进:更好的章节检测 ============== function findChapters() { const allChapters = []; const frames = document.querySelectorAll('iframe'); for (let frame of frames) { try { const doc = frame.contentDocument; const sections = doc.querySelectorAll('.childSection'); log(`📋 在iframe中找到 ${sections.length} 个.childSection`, 'info'); for (let sec of sections) { if (!sec.offsetParent) continue; const text = (sec.textContent || '').trim(); if (!text || !text.includes('应耗能量')) continue; const isActive = checkIsActive(sec); // 调试:输出找到的章节 if (isActive) { log(`✅ 找到当前激活章节: ${text.substring(0, 40)}`, 'success'); } allChapters.push({ element: sec, text: text.substring(0, 50), isActive: isActive }); } if (allChapters.length > 0) break; } catch (e) {} } log(`✅ 总共找到 ${allChapters.length} 个章节`, 'success'); return allChapters; } // 改进:更好的当前章节检测 function checkIsActive(element) { // 方法1:检查元素自身或子元素的样式 const children = element.querySelectorAll('*'); for (let child of children) { const style = window.getComputedStyle(child); // 检查背景色(蓝色圆点) if (style.backgroundColor) { const bg = style.backgroundColor.toLowerCase(); if (bg.includes('blue') || bg.includes('rgb(33, 150, 243)') || bg.includes('rgb(30, 144, 255)') || bg.includes('rgb(0, 122, 255)') || bg.includes('rgb(52, 152, 219)') || bg.includes('rgb(66, 153, 225)')) { return true; } } // 检查文字颜色 if (style.color) { const color = style.color.toLowerCase(); if (color.includes('blue') || color.includes('rgb(33, 150, 243)') || color.includes('rgb(30, 144, 255)')) { return true; } } // 检查border颜色(如果圆点是边框实现的) if (style.borderColor) { const bc = style.borderColor.toLowerCase(); if (bc.includes('blue') || bc.includes('rgb(33, 150, 243)')) { return true; } } } // 方法2:检查元素的class是否包含active相关的词 const className = element.className.toLowerCase(); if (className.includes('active') || className.includes('current') || className.includes('selected') || className.includes('playing')) { return true; } // 方法3:检查父元素的class if (element.parentElement) { const parentClass = element.parentElement.className.toLowerCase(); if (parentClass.includes('active') || parentClass.includes('current')) { return true; } } // 方法4:检查元素的文本颜色(可能是蓝色高亮) const selfStyle = window.getComputedStyle(element); if (selfStyle.color) { const color = selfStyle.color.toLowerCase(); if (color.includes('blue') || color.includes('rgb(33, 150, 243)') || color.includes('rgb(30, 144, 255)')) { return true; } } return false; } function findNextChapter() { const chapters = findChapters(); if (chapters.length === 0) { updateStatus('未找到章节', 'error'); return null; } let currentIndex = -1; for (let i = 0; i < chapters.length; i++) { if (chapters[i].isActive) { currentIndex = i; log(`📍 找到当前章节: [${i}] ${chapters[i].text.substring(0, 40)}`, 'success'); break; } } if (currentIndex === -1) { log('⚠️ 未找到当前激活章节', 'warning'); // 改进:不要直接返回第一个,而是保持当前状态 updateStatus('未识别当前章节,请手动选择', 'warning'); return null; // 返回null,不跳转 } const nextIndex = currentIndex + 1; if (nextIndex >= chapters.length) { log('🎉 已完成所有章节!', 'success'); updateStatus('🎉 已完成所有章节!', 'success'); return null; } log(`➡️ 下一章: [${nextIndex}] ${chapters[nextIndex].text.substring(0, 40)}`, 'success'); updateChapter(`当前: ${chapters[currentIndex].text.substring(0, 25)}\n下一章: ${chapters[nextIndex].text.substring(0, 25)}`); return chapters[nextIndex].element; } let counter = 0; let videoFinished = false; let switchCooldown = false; function mainLoop() { counter++; if (!config.autoNext && !config.autoPlay) { if (counter % 120 === 0) updateStatus('脚本已暂停', 'warning'); return; } const v = findVideo(); if (!v) { if (counter % 60 === 0) { updateStatus('未检测到视频', 'warning'); updateInfo('请进入视频播放页面'); } return; } if (Math.abs(v.playbackRate - config.speed) > 0.01) applySpeed(); if (config.autoPlay && v.paused && !v.ended && v.readyState >= 2) { v.play().catch(e => log('播放失败: ' + e.message, 'error')); updateStatus('正在播放', 'success'); videoFinished = false; } if (v.duration && !isNaN(v.duration)) { const cur = Math.floor(v.currentTime); const total = Math.floor(v.duration); const pct = Math.floor((cur / total) * 100); const cm = Math.floor(cur / 60), cs = cur % 60, tm = Math.floor(total / 60), ts = total % 60; updateInfo(`📊 ${pct}% | ${cm}:${String(cs).padStart(2, '0')} / ${tm}:${String(ts).padStart(2, '0')} | ${config.speed}x`); if (cur >= total - 2 && !videoFinished && config.autoNext && !switchCooldown) { videoFinished = true; updateStatus('视频即将完成,准备切换...', 'warning'); setTimeout(() => { const next = findNextChapter(); if (next) { updateStatus('点击下一章', 'success'); switchCooldown = true; next.click(); setTimeout(() => { switchCooldown = false; videoFinished = false; }, 10000); } else { updateStatus('未找到下一章', 'error'); } }, 3000); } } } function init() { log('⏳ 初始化...', 'info'); if (window === window.top) { setTimeout(createUI, 800); } setInterval(mainLoop, 500); updateStatus('脚本已启动', 'success'); } if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); window.addEventListener('DOMContentLoaded', init); } window.__studyHelper = { config: config, findVideo: findVideo, findChapters: findChapters, findNextChapter: findNextChapter, version: '11.3' }; })();