全站通用的网页视频播放辅助用户脚本。带可拖拽悬浮面板、快捷键与设置持久化; 在超星 / 学习通下有额外增强。
| 版本 | 2.1.0 |
| 适用 | 所有网站(@match *://*/*) |
| 产物 | web-video-play-helper.user.js(单文件,无构建步骤) |
| 运行环境 | 脚本猫 ScriptCat / Tampermonkey / Violentmonkey 等支持 GM_getValue 的管理器 |
| 许可 | MIT |
网页播放器普遍缺三样东西:倍速不够细、切标签页会被暂停、每个网站都要重设一次倍速。 这个脚本把这三件事一次性解决,并且尽量不打扰你——没有视频的页面它完全不出现。
在超星 / 学习通上,它额外处理那套课程的"特殊照顾":检测倍速、检测长时间无操作、 必须手动点"下一节"。
web-video-play-helper.user.js 全文粘进去,保存。从旧版本升级:本项目原先叫「学习通 · 播放助手」,存储键前缀是
cxh_。 v2.1.0 起改为wvph_,并内置了一次性迁移——启动时若新键为空而旧键有数据就自动搬过来。 你已有的设置和每站倍速不会丢。
功能分两组:通用(任何网站都有意义)与 学习通专属(其他站点自动空转,不会误动作)。
| 功能 | 说明 | 默认 |
|---|---|---|
| 倍速滑杆 | 18 档(0.5× → 16×),一个滑杆调完,无档位按钮 | 1× |
| 刻度跳档 | 滑杆下方 6 个刻度锚点,点一下直接跳档 | — |
| 滚轮微调 | 鼠标悬在滑杆上滚动,逐档增减 | — |
| 防误暂停 | 被平台因切屏 / 失焦 / 长时间无操作强制暂停时自动恢复;你自己按的暂停不会被抢回 | 开 |
| 自动播放 | 检测到视频后自动开始播放 | 开 |
| 静音 | 将视频静音 | 关 |
| 每站倍速记忆 | 按域名分别记住倍速,下次打开该站自动应用 | 开 |
chaoxing.com / xuexitong.com 及其子域真正生效)| 功能 | 说明 | 默认 |
|---|---|---|
| 隐藏真实倍速 | 对平台隐藏真实倍速——它读到的永远是 1×,从而绕过倍速检测 | 自动(仅学习通开) |
| 防挂机保活 | 定时向播放器派发合成鼠标事件,避免"长时间无操作"弹窗 | 开 |
| 自动连播 | 播完自动进入下一节 | 开 |
| 弹题提醒 | 出现视频弹题时响三声 + 标签页标题闪烁 | 常开 |
面板是一块深色玻璃悬浮卡片(290px 宽),可拖拽、可折叠、可整体隐藏。
它只在检测到页面上确实有视频时才出现——没有视频的页面它不会打扰你。
(视频在 iframe 里也算,例如学习通:顶层页面自己并没有 <video>。)
┌─────────────────────────────────┐
│ ● Web Video Play Helper ⌄ × │ 标题栏(状态灯 / 折叠 / 隐藏)
├─────────────────────────────────┤
│ 2 个视频 · 19:11 / 28:59 ▶ 播放中│ 状态行
│ ████████████░░░░░░░░░░░░░░░░░░ │ 进度条
│ │
│ 1.5× − [1.5] + │ 倍速读数 + 微调
│ ────────●─────────────────── │ 滑杆(唯一倍速控件)
│ 0.5 1 1.5 2 4 16 │ 刻度锚点(点击跳档)
│ │
│ 通用 4 / 4 ⌄│ ┐
│ 防误暂停 ● 静音 ○ │ │ 可折叠
│ 自动播放 ● 记住倍速 ● │ ┘
│ │
│ 学习通专属 当前站点不适用 2/3 ⌄│ ┐ 非学习通站点整块标灰
│ 隐藏倍速 ● 防挂机 ● │ │
│ 自动连播 ● │ ┘
│ │
│ [播放] [暂停] [下一节] [重检测] [诊断] │
└─────────────────────────────────┘
| 颜色 | 含义 |
|---|---|
| 灰色 | 未检测到视频,或视频已暂停 |
| 绿色(带辉光) | 正在播放 |
| 红色(带辉光) | 检测到弹题,需手动作答 |
点标题栏的 ⌄ 折叠后,面板变成一个紧凑胶囊:
● 1.5× ▶ 播放中
点胶囊即展开。折叠状态会被记住。
每个分区标题右侧常驻一个 已开 / 总数 计数,收起时也能一眼看出开了几项。
分区可独立折叠,状态各自记忆。
在非学习通站点,专属分区会整块标灰并显示 「当前站点不适用」 徽标, 同时操作区的「下一节」按钮置灰——这不是禁用,而是如实告诉你这些功能在这里没有作用对象。
| 按钮 | 作用 |
|---|---|
| 播放 / 暂停 | 跨 iframe 驱动视频播放状态 |
| 下一节 | 点击学习通的"下一节 / 下一集"入口(仅学习通可用) |
| 重检测 | 重新扫描页面上的视频元素 |
| 诊断 | 输出各 frame 的视频数、播放器数、URL,用于定位问题 |
滑杆下方 6 个锚点:0.5 / 1 / 1.5 / 2 / 4 / 16。
低倍速段排得密,高倍速段稀疏——精度该给的地方给足,用不到的地方不占位。
点锚点直接跳档;当前倍速完全命中某个锚点时它才点亮(2.5× 时没有任何锚点亮起,
这是刻意的,把最近的「2」点亮属于误导)。
| 快捷键 | 作用 |
|---|---|
Alt + ↑ / ↓ |
倍速 ±0.25 |
Alt + P |
播放 / 暂停 |
Alt + M |
静音开关 |
Alt + N |
下一节(仅学习通生效) |
Alt + H |
显隐面板 |
快捷键有前置判断:只在「本帧存在视频」或「面板已存在」时才响应,
所以不会在没有视频的页面上劫持你的 Alt 组合键。在输入框内也不会拦截。
面板底部只列了 4 个最常用的快捷键(
Alt+N是学习通专属,故未列入,以免面板变高)。
| 配置项 | 默认 | 说明 |
|---|---|---|
speed |
1 |
目标倍速 |
spoof |
'auto' |
隐藏真实倍速:'auto'(仅学习通)/ 'on' / 'off' |
antiPause |
true |
防误暂停 |
autoplay |
true |
自动播放 |
mute |
false |
静音 |
rememberSpeed |
true |
每站倍速记忆 |
keepAlive |
true |
防挂机(仅学习通生效) |
autoNext |
true |
自动连播(仅学习通有匹配入口) |
swsOpen |
true |
通用分区展开 |
swsCxOpen |
false |
学习通专属分区展开 |
panelX / panelY |
null |
面板位置 |
collapsed / hidden |
false |
折叠 / 隐藏状态 |
| 键 | 内容 |
|---|---|
wvph_cfg_v1 |
上表的全部配置 |
wvph_sitespeed_v1 |
每站倍速记忆 { "域名": 倍速 } |
wvph_cmd_v1 |
跨 frame 指令通道(见下文) |
旧前缀
cxh_*会在启动时一次性迁移到上述新键;新键已有数据时不会被旧键覆盖。
spoof)的三态这是本项目最有用的一个开关,也是唯一会改变页面行为的开关,所以给了三态:
auto(默认):只在超星 / 学习通下开启。那些站点需要它来绕过倍速检测;
而 B 站 / YouTube 这类站点自带倍速菜单,伪装开着会让它们读到的倍速永远是 1×,
菜单显示会错乱,所以在那里自动关闭。on / off:你在面板上手动拨一下,就从 auto 变成明确的开 / 关,不再随域名变化。这一节解释几个不那么显然的设计决策——如果你想改代码,请先读这里。
playbackRate直接把 video.playbackRate 设成 2,平台读一下 video.playbackRate 就知道了。
所以要改原型上的属性描述符:
// 保存原始描述符,然后替换成:
// get → 对外永远返回 1
// set → 平台的写入直接忽略
// 真实倍速用原始描述符旁路写入:rawRateDesc.set.call(el, rate)
元素上挂两个标记:__wvphRate(真实倍速)/ __wvphFakeRate(对外读数)。
defaultPlaybackRate 同样处理。
难点在于区分「平台强制暂停」和「你自己按的暂停」—— 如果无脑自动恢复,你就再也暂停不了视频了。
做法是记一个用户交互时间戳:
一个必须注意的坑:不能在这个锁的解除逻辑上想当然。包装播放器会派发抖动的
play 事件,如果 play 事件无条件解锁,你按的暂停会被自己冲掉,
表现为"暂停键不起作用"。所以解锁条件限定为「本帧 1.5 秒内确有真实用户操作」。
<video>超星的播放器不是裸 <video>,而是 videojs / 自研包装播放器,内部维护着自己的
paused 状态。只调原生 element.play()/pause(),包装器不知情,下一帧就把视频改回去——
表现就是"播放/暂停键没反应"。
所以控制播放时优先操纵播放器对象,再回落原生元素,两条路都走。
识别顺序:videojs.players(v5/6)→ videojs.getPlayers()(v7+)→ 页面全局变量
(player / myPlayer / playerObj / h5player 等)→ 媒体元素上挂的引用 →
按元素 id 反查 videojs.getPlayer(id)。
postMessage播放 / 暂停这类动作必须送到"有视频的那一帧",而它可能在 iframe 里。只靠
postMessage 时,在某些环境(跨域 iframe、沙箱)里会送不到,于是按钮点了没反应。
所以指令走两条通道:
postMessage 广播——遍历 frame 树,即时送达(首选)wvph_cmd_v1,每帧 300 ms 轮询一次(兜底)两条通道会各自投递同一条指令,若不去重会把"下一节"点两次从而跳章。
所以给每条指令分配一个全局递增序号 cs,每个 frame 只执行序号更大的指令,
实现跨通道的"恰好一次"。
另外,发送时先在本帧无条件执行一次再广播——这样即使 frame 树枚举失效, 只要视频就在当前窗口也一定能生效。
脚本跑在每个网站的所有 frame 上,所以做了几件必要的事:
| 措施 | 原因 |
|---|---|
| DOM 变化观察器节流 400 ms | 重度 SPA 的 DOM 变动极频繁,不节流的话每次变动都要跑一遍 querySelectorAll('video, audio') |
主循环 300 ms(不是 requestAnimationFrame) |
视频状态不需要 60fps,300 ms 足够 |
| 配置同步只跑 每 4 个 tick(≈1.2 s) | 跨 frame 配置同步不需要那么频繁 |
| 子帧状态上报做签名去重 + 4 秒心跳 | 状态没实质变化就不重复广播 |
| 快捷键加前置判断 | 避免在没有视频的页面劫持 Alt 组合键 |
| 学习通专属功能全部有域名门控 | 尤其在别的站点不合成鼠标事件,避免触发各种悬浮 / 预览行为 |
| 面板检测到视频才构建 | 无视频的页面零 UI 侵入 |
| 周期 | 内容 |
|---|---|
| 300 ms | 指令兜底轮询、防挂机保活、弹题检测、状态上报 |
| 1.2 s | (主循环每第 4 个 tick)跨 frame 配置同步 |
| 2.5 s | 兜底扫描视频元素(观察器未捕捉到的延迟注入) |
| 400 ms | DOM 观察器节流 |
| 350 ms | 被动暂停后自动恢复的延迟 |
| 600 ms | 检测到视频后自动播放的延迟 |
| 1.5 s | 视频结束 / 接近结束时切换下一节的延迟 |
| 80 ms | 倍速被外部篡改后拉回目标值的延迟 |
部分超星播放器走到结尾不派发 ended,所以用双触发:
timeupdate:剩余 ≤ 0.8 s 时触发(兜底)ended:正常触发用 __wvphNextFired 标记保证只触发一次。找不到"下一节"按钮时,
回退到点击章节列表里下一个未完成节点。
playbackRate"这一类检测)。Alt 系列快捷键理论上可能与个别站点冲突;已通过"仅在有视频的页面响应"降低概率。wvph_cfg_v1。Alt + H 强制显隐面板。诊断会输出每个 frame 的:视频数、识别到的包装播放器数、frame 总数、URL。
先按 F12 打开控制台,日志前缀是 [Web Video Play Helper]。
| 诊断结果 | 说明 |
|---|---|
视频 0 个 且帧数很少 |
脚本没注入到播放器所在的 frame,检查 iframe 权限 |
视频 ≥1 个 · 包装播放器 0 个 |
没识别到包装播放器,会以原生元素方式控制(通常仍可用) |
| 视频数正常但按钮无效 | 看控制台是否有 通过兜底通道收到指令 之类日志,判断指令是否送达 |
| 文件 | 说明 |
|---|---|
web-video-play-helper.user.js |
唯一产物,单文件、无构建步骤 |
panel-preview.html |
面板预览页(从脚本抽取真实 PANEL_CSS / PANEL_HTML 渲染,不重画稿子) |
panel-designs.html |
面板主题设计对照(4 套方案,更名前存档) |
speed-controls.html |
倍速控件设计对照(4 套方案,更名前存档) |
脚本是一个 IIFE,按编号分节,改动时按节定位:
0. 基础常量与工具(含 isChaoxing 域名判定)
1. 配置与状态(DEFAULTS / storage / 每站倍速 / setCfg)
2. 原型级 Hook(倍速伪装、可见性伪装、事件拦截)
3. 跨 frame 消息总线(含指令兜底通道与去重)
4. 媒体元素:发现 / 注册 / 控制
5. 播放器适配层(videojs / 自定义播放器)
6. 指令执行
7. 自动连播:找"下一节"
8. 防挂机保活
9. 弹题检测
10. 状态上报与诊断
11. 悬浮面板
12. 快捷键
13. 用户交互时间戳
14. 启动
本项目使用Node 手搓最小 DOM 假环境验证,不依赖真实浏览器:
HTMLMediaElement / Document / window.frames / 定时器 / localStorage{hidden:true} 以免构建面板)node --check 语法goNext 可点、保活生效goNext 拒绝执行、保活不动作setCfg 本地生效、每站倍速写入正确键state / ui 无未读取的键UI_IDS 引用的 id 都存在panel-preview.htmlSWITCHES 表生成(HTML / 渲染 / 计数三处共用),加开关只需改表。UI_IDS 映射表,加元素只需在表里加一行 + 在 PANEL_HTML 里加 id。grid-template-rows: 1fr → 0fr,不要换成 max-height(要猜最大值,容易露馅)。wvph 前缀;存储键改名并内置一次性迁移,升级不丢设置__cxh → __cx → cxh,否则 __cxh 会被 __cx 咬掉一半buildPanel 的 21 行 getElementById 收成映射表@match *://*/*)auto / on / off)MIT