一个单文件的浏览器用户脚本(UserScript),面向 mtyy4.com(含 mtyy1.cc 镜像)。
它做两件事:把页面上的广告清干净,以及把站内播放器变得更好用。
| 项目 | 说明 |
|---|---|
| 脚本文件 | mtyy-adblock.user.js(单文件,无外部依赖,约 1700 行) |
| 当前版本 | 3.5.1 |
| 运行环境 | 脚本猫(ScriptCat)、篡改猴(Tampermonkey)等主流用户脚本管理器 |
| 生效域名 | *://*.mtyy4.com/*、*://*.mtyy1.cc/* |
| 执行时机 | document-start(必须早于站点脚本,否则拦不住注入) |
| 权限声明 | GM_addStyle、GM_setValue、GM_getValue、GM_registerMenuCommand、unsafeWindow |
| 界面语言 | 全中文 |
mtyy-adblock.user.js 全部内容 → 保存(Ctrl/Cmd + S)。覆盖安装时 不要修改
@name与@namespace,否则脚本管理器会当成新脚本,出现双份执行。
| 模块 | 功能 | 说明 |
|---|---|---|
| 广告净化 | 拦截第三方广告 / 统计脚本 | 在脚本注入前就切断请求 |
| 清理横幅、悬浮、内嵌广告位 | 站点已知广告容器与广告图 | |
| 识别并移除透明点击劫持层 | 精确条件判定,不误伤播放器遮罩 | |
| 拦截弹窗与强制跳转 | 广告域名弹窗、自动弹窗、广告链接点击 | |
| 播放增强 | 自动跳过片头 / 片尾 | 全局配置,带完整输入校验 |
| 记住播放倍速 | 每次调整自动持久化,下次打开自动沿用 | |
| 临时 2 倍速(长按) | 桌面长按方向键 / 移动端长按播放区两侧热区 |
所有播放增强的设置项都在播放器自带的「设置」面板里(控制栏那个 ⚙ 图标),
页面上没有游离的悬浮按钮或弹窗,设置项与「画面比例」「画面翻转」等原生项同排同风格。
| 域名 | 用途 |
|---|---|
f0cmqjk.com |
主广告脚本(/sc/1198,端口 9003,桌面端) |
480ipil.com |
移动端广告点位,会强制跳转 |
cb1nvdt.com / z8ioqja.com / 2svk4ps.com |
WebSocket 广告通道(端口 9903) |
aizhantj.com |
爱站统计追踪(/tjjs,端口 21233) |
ieo0s3m.cc |
横幅广告的落地推广页(带 share_utm 参数) |
兜底规则:广告服务固定使用的端口 9003 / 9903 / 21233,只要访问域名不是站点自身,
一律拦截——这样即使对方轮换域名也依然有效。
白名单(永不拦截):mtyy4.com、mtyy1.cc、mtzy2.com、mtyy2.com。
| 层次 | 做法 | 效果 |
|---|---|---|
| 脚本注入前 | 劫持 HTMLScriptElement.prototype.src 与 Element.prototype.setAttribute |
广告脚本的 src 根本写不进去,请求永不发出 |
| 网络请求 | 接管 fetch、XMLHttpRequest、sendBeacon、WebSocket、EventSource |
广告上报、WS 通道全部失效;WS 返回"死对象"避免对方疯狂重试 |
| 事件监听接管 | 接管 addEventListener 与事件属性赋值(ontouchend 等) |
注册带广告特征码的 touchend / click 监听时直接丢弃,掐断移动端「碰一下屏幕下方就跳走」的恶意跳转 |
| 跳转与弹窗 | 接管 window.open、location.assign/replace、href 写入、<a> 点击 |
广告域名跳转与点击劫持失效;非用户触发的自动弹窗也会被拦(可关) |
| DOM 清理 | MutationObserver + requestAnimationFrame 批处理,并在 0.5 / 1.2 / 2.5 / 4 / 6 秒补刀 |
清理站点延迟注入(站点在 1s、2.5s 会二次注入)的广告与广告脚本 |
| 样式兜底 | 注入 display:none !important 等规则 |
防止广告在清理前闪一下 |
.ec-ad、.ec-ad-double、.ads_w、.ads_close_btn、.slide-wap、[data-id^="ad-"]、[class*="ads_w"]、[id*="gvbqxwcb"] 等/static/images/gg/ 目录下的素材<script> / <iframe> / <img> / <link> / <a>div 覆盖在视频上骗点击。position: fixed|absolute + opacity < 0.06 + z-index ≥ 50 + 有背景色 +.art-mask 等)已加入排除名单,不会被误删。影片条目、轮播、导航、站点图片与链接、播放器控件全部保留(冒烟测试中有专项断言)。
站点播放器为 ArtPlayer 5.2.2,跑在同源 iframe /static/player/art.html 里。
所有设置项都注入到播放器自带的「设置」面板中,打开控制栏的 ⚙ 即可看到:
⚙ 设置面板
├ 画面比例 › ← 播放器原生
├ 画面翻转 › ← 播放器原生
├ 片头跳过 [1分36秒] ← 本脚本新增(行内输入框)
├ 片尾跳过 [未开启] ← 本脚本新增(行内输入框)
└ 记忆倍速 当前 1.5x › ← 本脚本新增(8 档可选)
配置格式
| 填写方式 | 示例 | 解析结果 |
|---|---|---|
| 分 + 秒 | 1分30秒、1分30 |
90 秒 |
| 仅分钟 | 1分、1.5分 |
60 / 90 秒 |
| 仅秒数 | 90、90秒、90s |
90 秒 |
| 冒号写法 | 1:30 |
90 秒 |
| 关闭该项 | 留空 或 0 |
关闭 |
输入校验(不通过一律拒绝保存,并给出具体原因)
| 情况 | 提示 |
|---|---|
含非法字符(如 abc) |
「片头时长」含有非法字符,只支持「分/秒」写法(如 1分30秒)或纯秒数(如 90) |
负数(如 -5) |
「片头时长」不能填写负数 |
| 超过 24 小时 | 「片头时长」不能超过 24 小时(86400 秒) |
| 超过视频总时长 | 「片头时长」1分30秒 超过了视频总时长 1分00秒,已拒绝保存 |
| 片头 + 片尾 ≥ 总时长 | 片头(1分00秒)+ 片尾(1分00秒)不小于视频总时长(1分40秒),无法跳过,请调整后保存 |
校验基准是当前正在播放的视频总时长(面板底部会标注基准值);若当前没有检测到视频,
则跳过"总时长"这一项校验。输入时右侧会实时显示换算结果(如 = 1分30秒),非法输入会标红。
生效与边界
ended,从而由站点自己的"下一集"逻辑接管。为什么有时不跳(旧版缺陷)与现在的做法
旧版靠「一次性 1.2 秒定时器上膛 + 仅 timeupdate 一条触发链 + 任何 seeking 都作废片头」,
只要播放器自己定位过一次(HLS 首帧对齐最常见),整集就再也不会跳——这就是"时灵时不灵"的根因。
现在改为状态机 + 多路触发 + 巡检重试:
| 机制 | 说明 |
|---|---|
| 状态化上膛 | 是否可跳过由「元数据就绪时间戳 + 600ms 静置期」实时判定,不再依赖一次性定时器,换源 / 时长更新都不会把状态顶死 |
| 多路触发 | timeupdate / seeked / canplay / playing / durationchange / 配置热更新 / 250ms 巡检,任一可用即触发 |
| 巡检兜底 | 移动端自动播放被拦截、视频暂停时浏览器不派发 timeupdate,靠 250ms 巡检在元数据就绪后 20 秒内持续尝试 |
| 落点校验+重试 | 跳转后 1.2 秒复核播放位置,被播放器拉回就复位重试,最多 8 次,避免"跳了但没跳成" |
| 时长后到/换源 | 收到 durationchange、emptied、loadstart 会重新开一轮,HLS 时长晚到、切清晰度、换下一集都能覆盖 |
| 元素/实例重绑 | 1 秒守护轮询检测 window.art 被替换或 <video> 离开 DOM,自动重新绑定,不会丢失事件监听 |
| 兜底上膛 | 即使 loadedmetadata 因故没派发,只要时长已知就自行上膛,不存在"永远没上膛"的死路 |
| 换集自动重开 | 站点用 hls.loadSource 切下一集时不会派发 emptied/loadedmetadata,只有 durationchange;判据「上一集刚 ended(或时长明显变化)+ 进度回到开头」命中即完整重开一轮 |
| 卡死自检 | 若「从没真正跳过片头」却仍停在片头区间内且用户没拖过 → 判定状态被意外置位,立即复位重来(下一个 timeupdate 或巡检即自愈) |
明确的触发条件(全部满足才会跳):已配置片头 > 0 · 视频元素在文档中且 readyState ≥ 1 ·
总时长已知且 > 片头 + 1 秒 · 当前进度 < 片头 − 0.5 秒 · 本集未跳过且用户未手动拖动 · 已过静置期且不在定位中。
1.5x / 2.0x / 0.75x 这类文案。在当前基准倍速上临时 ×2,松开立即恢复;结果同样受 4x 上限约束(3.0x 长按 → 4.0x)。
| 设备 | 操作方式 | 说明 |
|---|---|---|
| 桌面 / 鼠标 | 按住键盘 → 约 0.28 秒 |
加速期间画面中央显示 ▶▶ 3.0x 角标;轻点 → 仍是快进 5 秒 |
| 手机 / 平板 | 长按播放画面左、右两侧的透明热区约 0.25 秒 | 每侧覆盖播放区宽度的 22%,无底色、无边框,完全透明、不放任何图标;每次播放开始提示一次,用过即收起 |
移动端两侧热区的设计要点
»),改为每次播放开始时提示一次「长按画面左右两侧 · 临时 2 倍速」,用过一次即自动收起,避免常驻元素遮挡画面。<video> 会弹出「视频信息 / 保存视频」等菜单,
脚本在两侧热区内(以及长按加速生效期间)吞掉 contextmenu 事件,并给视频加上 -webkit-touch-callout: none;
画面中间区域保留播放器原生菜单,不做干预。pointer-events: none,只做提示不抢事件;落点判定由播放区容器在捕获阶段的被动监听完成,click,脚本会精确吞掉这一次(仅一次、700ms 超时、新的触摸开始立即作废),所有配置存在脚本管理器的 GM 存储中,刷新、切集、重启浏览器后依然生效。
| 存储键 | 内容 | 说明 |
|---|---|---|
mtyy_player_cfg_v1 |
JSON | 播放增强配置:intro(片头秒数)、outro(片尾秒数)、rate(记忆倍速)、lastDuration(上次播放时长,仅供参考) |
block_auto_popups |
布尔 | 是否拦截非用户触发的自动弹窗,默认开 |
debug_mode |
布尔 | 是否输出调试日志,默认关 |
想彻底重置:删除脚本存储(或卸载重装),或在菜单里执行相关命令。
点开脚本管理器图标,可以看到以下命令(建议在播放页使用播放相关的两条):
| 命令 | 作用 |
|---|---|
| 立即清理广告 | 手动跑一次广告清理 |
| 拦截统计 | 查看本次会话拦截了多少条广告 / 请求 |
| 切换「自动弹窗拦截」:开 / 关 | 拦截所有非用户触发的弹窗 |
| 切换「调试日志」:开 / 关 | 控制台输出拦截与配置详情 |
| 打开播放器设置面板(片头 / 片尾 / 倍速) | 直接拉起播放器自带的设置面板 |
| 清除记忆倍速(恢复 1.0x) | 忘掉上次的倍速,下次从 1.0x 开始 |
@noframes,主页面与播放器帧各司其职;playbackRate 是实例上的不可配置属性,无法二次包装,HTMLMediaElement.prototype.playbackRate,这样站内控件、面板档位、内部重置全部能捕获。art.setting.add({ name, html, icon, mounted, selector, onSelect });$parent.tooltip 并抛错),.art-control-selector .art-selector-value(通过 art.controls.cache 精确定位带 rate 字段的控件)。document.addEventListener('touchend', fn) 注册劫持,
命中屏幕下方就 top.location = 广告地址。location 是 [LegacyUnforgeable],属性改写拦不住,
所以在 document-start 接管 EventTarget.prototype.addEventListener(以及 ontouchend 等事件属性),
按广告特征码(域名 / 端口 / gvbqxwcb / /1198)把这类监听器在注册时直接丢弃。document 冒泡阶段且不判断 e.repeat,document 捕获阶段 stopImmediatePropagation() 完整接管右方向键,自行实现"短按快进 / 长按加速"。Player.trySkip():它被 timeupdate / seeked / canplay / playing /
durationchange / 配置热更新 / 250ms 巡检 多路调用,且幂等。改判定条件请只改这一处,
不要在各事件回调里各写一份,否则又会退化成"某条链路断了就永久失效"。armSkip() 只维护 armedAt / skipDeadline,
是否可跳由 canSkipNow() 实时算。换源时会 token++,旧定时器自然失效——这正是旧版的坑。internalSeekUntil(400ms)只认脚本自己的跳转;真正的"用户拖动"用
lastSeekGestureAt(只有落在 .art-progress 等进度条元素上的 pointerdown/mousedown/touchstart 才更新)。
不要把全局手势当拖动依据,否则点一下画面暂停就会被误判成用户拖动。test-skip.js 26 项覆盖跳过稳定性;test-regress.js 27 项覆盖倍速记忆、设置面板、键鼠与触屏长按、广告净化),
改动后需全部通过再交付。可把旧逻辑还原后跑 test-skip.js 反证用例确实能抓到失效。| 版本 | 变更 |
|---|---|
| 1.1.0 | 初始版本:基础广告清理 |
| 2.0.0 | 逆向站点广告机制后重写:五层拦截、透明劫持层识别、端口兜底规则 |
| 3.0.0 | 新增播放增强:跳过片头片尾(含校验)、记忆倍速、长按临时 2 倍速 |
| 3.1.0 | 设置项全部收进播放器自带设置面板,移除页面上的浮动入口与弹窗 |
| 3.2.0 | 移动端加速改为播放区两侧的透明长按热区,不再占用控制栏 |
| 3.2.1 | 修复记忆倍速未回写到控制栏「倍速」控件的问题,统一倍速文案格式 |
| 3.3.0 | 删除控制栏的长按加速按钮(桌面端只用键盘,移动端用两侧热区) |
| 3.4.0 | 拦截移动端恶意跳转(丢弃广告劫持监听);两侧热区去掉图标,改为每次播放提示一次;长按加速时不再弹出系统菜单 |
| 3.5.0 | 重写片头/片尾跳过:状态机上膛 + 多路触发 + 250ms 巡检兜底 + 落点校验重试 + 播放器实例/视频元素自动重绑,修复「间歇性失效」 |
| 3.5.1 | 修复「跳过片尾后片头失效」:hls.loadSource 换集只派发 durationchange 时重开一轮;userSeeked 改为每集独立;脚本定位改为「250ms 窗口 + 落点认领」,不再误吞真实拖动;新增卡死自检兜底 |