惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
CERT Recently Published Vulnerability Notes
K
Kaspersky official blog
S
Schneier on Security
Latest news
Latest news
Cisco Talos Blog
Cisco Talos Blog
T
Threatpost
T
Tor Project blog
T
Tenable Blog
The Register - Security
The Register - Security
T
Threat Research - Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
F
Fortinet All Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
Visual Studio Blog
Google DeepMind News
Google DeepMind News
H
Help Net Security
博客园_首页
I
Intezer
aimingoo的专栏
aimingoo的专栏
Last Week in AI
Last Week in AI
Project Zero
Project Zero
Recorded Future
Recorded Future
C
Cybersecurity and Infrastructure Security Agency CISA
Vercel News
Vercel News
A
Arctic Wolf
P
Palo Alto Networks Blog
The Hacker News
The Hacker News
博客园 - 三生石上(FineUI控件)
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Proofpoint News Feed
S
SegmentFault 最新的问题
Know Your Adversary
Know Your Adversary
P
Privacy & Cybersecurity Law Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Proofpoint News Feed
P
Privacy International News Feed
G
GRAHAM CLULEY
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
Simon Willison's Weblog
Simon Willison's Weblog
C
CXSECURITY Database RSS Feed - CXSecurity.com
Scott Helme
Scott Helme
T
The Exploit Database - CXSecurity.com
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Security Latest
Security Latest
Cyberwarzone
Cyberwarzone
L
Lohrmann on Cybersecurity
C
Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
AWS News Blog
AWS News Blog

Anyeの小站

Peg - 命令行便签 · 把终端输出钉上布告板 2025 年终总结:折腾不止,热情常青 告别公网暴露风险:mTLS助你构建家里云安全边界 🚀 0 代码部署:Halo 博客与企业门户系统 B站跟随系统深色模式脚本——让你的B站体验更丝滑 【1024抽奖】EdgeOne下线源站防护?这个办法更简单! 全新!Edge 还是卸载不掉?来试试这个! 混元模型玩家必看:CNB API 不限量调用! 1Panel 应用商店如何上架应用 Next Terminal 实战:内网无密码安全登录 用了 EdgeOne 降本增效,源站却可能裸奔? 降本增效!上云真香! 在线重装 Proxmox VE SSL证书申请老出问题?这套组合直接帮你搞定! 🌐 森屿云:让数字化转型像呼吸一样自然 1Panel v2 首发体验(alpha) Vscode/Code-Server 安装中文包——CI/CD DockerHub Mirror 终极解决方案——零成本,速度超快! 本地跑不动 QwQ?快用 CNB,免费运行满血 QwQ! Docker Push 巨慢?Skopeo 来拯救 2024 年终总结:与 17170 位读者相遇的旅程 云厂 200Mbps 小鸡,家里云的希望 Halo 插件 thyuu-embed 主题样式注入模板 Windows 11 卸载 Edge 小破站 6 级萌新前来挑战硬核会员,答题分享 手机系统杂谈 1Panel 自建邮局 - Docker Mailserver 1Panel 推送 SSL 证书到阿里云、腾讯云 整了几个简洁的单页,欢迎来扒 1Panel 部署 Logto 对接自有平台 1Panel 开心版,到底开不开心? 环境配置快速复制 Public API 一文搞懂 CloudFlare CDN/Page/Workers 优选 Windows 自己的 Systemctl —— WinSW Github Action 自动部署&更新静态网页服务 Docker 私有代理 浅测 长亭雷池 WAF “动态防护” PVE 8.2 LXC 容器直通 USB 设备实现局域网共享打印机 一个花活玩法:随身站点 1Panel v1 环境下使用 Caddy 实现 HTTP/3 1Panel 新版 WAF 配置建议 黑域宝盒 全网优惠省钱神器 Anye Docker Proxy 1Panel 部署 苹果 CMS v10 部分问题解决办法 提问的艺术 网络带宽与存储单位中的 bit、Byte、Mbps、MB/s 傻傻分不清? 如何在1Panel上偷渡HTTP/3 1Panel使用GMSSL+Openresty实现国密/RSA单向自适应 如何在OpenEuler中安装1Panel
PlayGuard,让你的播放器老实点!
https://www.anye.xyz/authors/anye · 2026-06-20 · via Anyeの小站

本文由 GLM-5.2 依据 https://cnb.cool/Anyexyz/PlayGuard 完成。

前言

你是否也遇到过这种抓狂的瞬间:

开着某个网页播放器听歌、听课、听播客,切到别的窗口敲点东西,或者把标签页随手切到后台——声音突然就没了。等切回来才发现,它早因为「页面失焦」「标签页不可见」而暂停了。

更气人的是,有些站点更狠:鼠标一离开窗口区域就暂停,甚至直接判断 document.hiddendocument.hasFocus(),在脚本里主动掐掉播放。

对于「我只想在后台安安静静听个响」的用户来说,这种行为非常反直觉。于是就有了 这个小扩展。

🛡 PlayGuard 是什么?

https://cnb.cool/Anyexyz/PlayGuard

PlayGuard 是一个轻量级 Chrome / Edge 扩展(Manifest V3),干的事就一件:在你明确授权的网站上,降低网页播放器因页面失焦、隐藏或脚本调用而停止播放的概率。

说白了,就是把这些「非自愿暂停」的信号和暂停行为,骗回「我还在前台正常播放」的状态。

✨ 核心特点

  • 双 World 协作:充分利用 MV3 的 MAIN 与隔离两种执行环境,既能拿到 chrome.* 权限,又能改写页面原型。

  • 精准拦截:伪造 document.hidden / visibilityState / hasFocus(),并拦下 visibilitychangeblur 等事件。

  • 自动续播:劫持 video.pause(),页面一暂停就偷偷 10ms 后续播,用 WeakMap 防重复。

  • 动态守护MutationObserver 盯着新增的 <video>,点开新一集 / 新一节课也照样保护。

  • :500ms 一轮扫描,不放过任何「应续播却暂停」的视频。

  • 无后台、按需启用:只有你主动加入列表的域名才会被守护,命中次数透明展示。

🏗 它是怎么做到的

整个架构看起来是这样的——脚本分处两个互不相通的执行域,靠 window.postMessage 桥接:

为什么要分两个 world?这是整个方案最妙的地方:

  • page-guard.js 跟页面 JavaScript 共享 DOM、共享原型链。只有在这里才能改写 HTMLMediaElement.prototype.pausedocument.hidden 这些「页面脚本能感知到」的东西。

  • 隔离 world(content.js 拥有 chrome.storage 等扩展权限,但看不到页面的变量。它负责读站点配置、判断当前域名是否启用,再把结果告诉 page-guard.js

  • 弹窗(popup.html / popup.js 管理域名列表、展示状态和命中次数,数据全落在 chrome.storage.local 这个唯一「真相源」上。

各司其职、隔离又协作,这就是它既能拿到 chrome.* 权限、又能改写页面原型的关键。

抢救一条视频的完整流程

当某个已启用站点的视频要被暂停时,PlayGuard 会走这样一条「抢救链」:

核心逻辑都集中在 page-guard.js,拆开看是四件事。

① 伪造页面可见性与焦点

在 MAIN world 里直接重写属性描述符,让页面脚本读到的永远是「我还在前台」:

Object.defineProperty(document, 'hidden', {
  get: () => state.enabled ? false : originalHidden?.get?.call(document),
  configurable: true
});
Object.defineProperty(document, 'visibilityState', {
  get: () => state.enabled ? 'visible' : originalVisibilityState?.get?.call(document),
  configurable: true
});
document.hasFocus = () => state.enabled ? true : originalHasFocus();

同时把 document 上的 visibilitychangewindow 上的 blur 用「捕获阶段 + stopImmediatePropagation」提前拦下,页面脚本根本收不到这些事件:

originalAddEventListener.call(document, 'visibilitychange', stopEvent, true);
originalAddEventListener.call(window, 'blur', stopEvent, true);

pause() 并自动续播

更直接的招数是把 HTMLMediaElement.prototype.pause 换掉。页面想暂停一个「正在播放、未结束、无错误」的视频时,不真正暂停,而是 10ms 后偷偷调一次 play() 续上:

HTMLMediaElement.prototype.pause = function (...args) {
  if (state.enabled && this.tagName === 'VIDEO' && shouldResumeVideo(this)) {
    scheduleResume(this, 10);
    return undefined;          // 骗过调用方:暂停「成功」了
  }
  return originalPause.apply(this, args);
};

为了避免重复排队,用 WeakMap 给每个 <video> 记一个定时器,互不干扰、还能随元素回收自动清理。

③ 守护新增的 <video>

网页常在交互后才动态插入 <video>(比如点开某节课、某集剧)。PlayGuard 用 MutationObserver 盯着整个文档,一旦冒出新的 <video>,立刻挂上 play / pause 监听并打上保护标记:

new MutationObserver(mutations => {
  if (!state.enabled) return;
  mutations.forEach(m => m.addedNodes.forEach(node => {
    if (node.nodeName === 'VIDEO') protectVideo(node);
    else if (node.querySelectorAll) node.querySelectorAll('video').forEach(protectVideo);
  }));
}).observe(document.documentElement, { childList: true, subtree: true });

万一页面用了非常规手段让视频停下,还有个每 500ms 跑一轮的 setInterval 兜底:凡是「应续播却处于暂停」的视频,再补一次 scheduleResume,尽量不留死角。

🚀 使用方法

项目没有构建步骤,加载即用,三步搞定:

  1. 打开 chrome://extensions(Edge 是 edge://extensions)。

  2. 右上角开启「开发者模式」。

  3. 点「加载已解压的扩展」,选中 PlayGuard 目录。

logo.png

加载好之后,点扩展栏里的 PlayGuard 图标,把想保护的网站域名加进去就行:

域名规则很宽松:填 music.example.comhttps://music.example.com、甚至带端口的地址都行,扩展会自动归一化(去 www.、忽略协议)。子域名会跟随主域名一起生效,所以加个主域名就全覆盖了。

🎯 适用场景

说说我自己平时用它对付的几种站点:

  • 在线音乐 / 播客站:后台听歌被打断的元凶,加进去就老实了

  • 网课 / 直播回放:边听课边做笔记,切窗口再也不会暂停

  • 电台 / 听书:长时间后台播放,离开页面也不掐

  • 某些「防挂机」的视频站:那种鼠标离开就暂停的,直接拿捏

📊 数据与状态:一个存储,三处共享

PlayGuard 没有后台常驻脚本,所有状态都存在 chrome.storage.local 里:

  • cleanerSites:你启用的域名列表

  • siteCounters:每个域名被「成功守护」的累计次数

content.js 每次进页面都会把当前域名归一化,再用「最长匹配」判断是否启用;命中就自增计数。弹窗和内容脚本都监听 chrome.storage.onChanged,所以你在弹窗里加一个站点,已经打开的页面会实时生效,无需刷新。命中次数也会随着每次访问自动累加,一目了然。

写在最后

PlayGuard 代码量不大,但把一组很干净的浏览器 API 组合起来,解决了一个非常贴近日常体验的小痛点:MV3 双 world、原型劫持、事件捕获拦截、WeakMap 去重、MutationObserver 动态守护、storage 驱动的无后台架构——每一块都不复杂,凑在一起就刚好。

它不会无差别改写所有网站:只有你主动加入列表的域名才会被守护,命中次数也透明地展示在弹窗里,用着安心。

如果你也有「后台听东西却被打断」的困扰,不妨试试,顺手点个 ⭐。