// ==UserScript== // @name 课程伴侣视频自动播放助手 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 自动播放视频,默认静音,显示实时进度(反馈群:612441267) // @author lakay666 // @match https://mooc.ytu.edu.cn/meol/jpk/course/layout/newpage/index.jsp?courseId=* // @match https://mooc.ytu.edu.cn/meol/jpk/course/layout/newpage/index.jsp* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; var CHECK_INTERVAL = 2000; var DEBUG = true; function log(msg) { if (DEBUG) console.log('[YTU助手] ' + msg); } function getCtx() { try { var f1 = document.querySelector('iframe#mainFrame'); if (!f1) return null; var d1 = f1.contentDocument || f1.contentWindow.document; var f2 = d1.querySelector('iframe#main-content-two'); if (!f2) return null; var w2 = f2.contentWindow; var d2 = f2.contentDocument || w2.document; return { win: w2, doc: d2 }; } catch (e) { return null; } } function parseTime(s) { if (!s) return 0; var p = s.split(':'); if (p.length === 3) return parseInt(p[0])*3600 + parseInt(p[1])*60 + parseInt(p[2]); if (p.length === 2) return parseInt(p[0])*60 + parseInt(p[1]); return 0; } function fmtTime(sec) { if (!sec || sec < 0) return '00:00:00'; var h = Math.floor(sec/3600); var m = Math.floor((sec%3600)/60); var s = Math.floor(sec%60); return (h<10?'0'+h:h) + ':' + (m<10?'0'+m:m) + ':' + (s<10?'0'+s:s); } function getTimes(ctx) { if (!ctx || !ctx.doc) return null; var els = ctx.doc.querySelectorAll('div, span'); var re = /^(\d{1,2}:\d{2}:\d{2})\/(\d{1,2}:\d{2}:\d{2})$/; for (var i = 0; i < els.length; i++) { var t = els[i].textContent ? els[i].textContent.trim() : ''; var m = t.match(re); if (m) { return { cur: parseTime(m[1]), tot: parseTime(m[2]), raw: t }; } } return null; } function simClick(el, ctx) { if (!el || !ctx) return false; try { var r = el.getBoundingClientRect(); var x = r.left + r.width/2; var y = r.top + r.height/2; var w = ctx.win; var types = ['mouseover', 'mousedown', 'mouseup', 'click']; for (var i = 0; i < types.length; i++) { el.dispatchEvent(new w.MouseEvent(types[i], { bubbles: true, cancelable: true, view: w, clientX: x, clientY: y, button: 0 })); } return true; } catch (e) { return false; } } function clickPlay(ctx) { if (!ctx || !ctx.doc) return false; var btn = ctx.doc.querySelector('div[data-title="点击播放"]'); if (btn) { var st = ctx.win.getComputedStyle(btn); if (st.display !== 'none') { log('点击播放'); return simClick(btn, ctx); } } return false; } function clickMute(ctx) { if (!ctx || !ctx.doc) return false; var btn = ctx.doc.querySelector('div[data-title="静音"]'); if (btn) { var st = ctx.win.getComputedStyle(btn); if (st.display !== 'none') { log('点击静音'); return simClick(btn, ctx); } } return true; } var lastTime = 0; var stuck = 0; var hasMuted = false; var hasStarted = false; var timer = null; function updatePanel(cur, tot) { var c = document.getElementById('ytu-cur'); var t = document.getElementById('ytu-tot'); var f = document.getElementById('ytu-fill'); var info = document.getElementById('ytu-info'); if (c) c.textContent = fmtTime(cur); if (t) t.textContent = fmtTime(tot); if (f && tot > 0) f.style.width = (cur/tot*100).toFixed(1) + '%'; if (info) info.textContent = fmtTime(cur) + ' / ' + fmtTime(tot); } function tick() { var ctx = getCtx(); if (!ctx) { log('等待iframe加载...'); return; } var times = getTimes(ctx); if (!times) return; log('进度: ' + times.raw); updatePanel(times.cur, times.tot); var playing = times.cur > 0 && times.cur < times.tot; if (playing) { if (times.cur === lastTime) { stuck++; if (stuck > 3) { log('卡住,重试播放'); stuck = 0; clickPlay(ctx); } } else { stuck = 0; } lastTime = times.cur; if (!hasMuted) { clickMute(ctx); hasMuted = true; } } else if (!hasStarted) { log('开始播放'); clickPlay(ctx); hasStarted = true; } } function start() { log('助手已启动'); timer = setInterval(tick, CHECK_INTERVAL); } function stop() { if (timer) { clearInterval(timer); timer = null; } lastTime = 0; stuck = 0; hasMuted = false; hasStarted = false; } function createPanel() { var p = document.createElement('div'); p.id = 'ytu-panel'; p.innerHTML = '
' + '
' + 'YTU视频助手' + '运行中' + '
' + '
' + '
播放进度
' + '
' + '
' + '
' + '
' + '00:00:00' + '00:00:00' + '
' + '
' + '
' + '' + '' + '
' + '
等待视频加载...
' + '
'; document.body.appendChild(p); document.getElementById('ytu-play').addEventListener('click', function() { var ctx = getCtx(); if (ctx) clickPlay(ctx); }); document.getElementById('ytu-mute').addEventListener('click', function() { var ctx = getCtx(); if (ctx) clickMute(ctx); }); var drag = false; var sx, sy, sl, st; var c = p.firstElementChild; c.addEventListener('mousedown', function(e) { drag = true; sx = e.clientX; sy = e.clientY; var r = c.getBoundingClientRect(); sl = r.left; st = r.top; }); document.addEventListener('mousemove', function(e) { if (!drag) return; c.style.left = (sl + e.clientX - sx) + 'px'; c.style.top = (st + e.clientY - sy) + 'px'; c.style.right = 'auto'; }); document.addEventListener('mouseup', function() { drag = false; }); } function init() { createPanel(); start(); window.addEventListener('beforeunload', stop); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();