Chrome 141 的自动播放策略:为什么《中国法制史》第 7 讲没有自己翻页
2026 年 8 月那次通宵,我打开 chrome://media-internals 才明白,问题不在网课平台,而在浏览器对"静音视频"和"有声视频"的两套不同判断。第 7 讲带字幕音轨,被归进了需要用户手势的那一类,所以下一集的 play() 调用被直接拦掉。
2026 年 8 月 14 日晚上十一点四十,我在 Chrome 141 里盯着一门《中国法制史》的网课录像,进度停在第 7 讲的 23 分 47 秒,下一集按钮亮着,我却已经靠在椅背上打了三个哈欠。这篇文章就是从那个瞬间开始的——关于浏览器究竟怎么判断一段视频"播完了",以及我们到底能对它做什么。
2026 年 8 月那次通宵,我打开 chrome://media-internals 才明白,问题不在网课平台,而在浏览器对"静音视频"和"有声视频"的两套不同判断。第 7 讲带字幕音轨,被归进了需要用户手势的那一类,所以下一集的 play() 调用被直接拦掉。
王梓航给了我一个思路:不要等平台的"下一集"按钮,直接监听 HTMLMediaElement 的 ended 事件。我在 Lecteur 上试了一版,47 行脚本,第一次跑通是在 8 月 18 日凌晨一点零九分,日志里跳出"第 8 讲 已开始"六个字的时候,我差点把水杯碰倒。
平台在 visibilitychange 里挂了暂停逻辑,我一度以为是省电策略。用 DevTools 的 Event Listener 面板一层层扒,才发现是页面自己在 document.hidden 时调用了 video.pause()。后来我把这个和「恢复观看历史播放影片」里提到的状态恢复思路对照着看,才算理清。
8 月 23 日在一台旧笔记本上用 QQ 浏览器看回放,左右滑动手势频繁触发切页,视频直接被中断。我去设置里关掉"左右滑动切换网页"之后,脚本的 ended 监听才稳定下来,这件事让我意识到交互层和媒体层的冲突比想象中更常见。
有段时间课件是打包在本地文件里通过 QQ 浏览器打开的,file:// 协议下的跨域限制让脚本注入直接失败,控制台报了一串 SecurityError。后来我把课件挪到本地起的 127.0.0.1 静态服务上,同一份脚本才活过来,这个坑值得单独记一笔。
用的是 MSE 流式播放的网课平台,问题就变成了:缓冲到最后一段时,video.buffered.end() 可能比 duration 短几毫秒,ended 迟迟不来。我对着 W3C 的 HTML 规范读了两晚,最后靠一个 250ms 轮询兜底判定,虽然不优雅,但确实能翻页了。
9 月初帮朋友排查一个页面脚本时,名字撞得像图库站,其实是把定位脚本和媒体脚本混在同一个 content script 里,作用域互相污染。我把它拆成两个独立注入之后问题消失,结论是:脚本边界比脚本本身更重要。
9 月 7 日拆一个题库站的 DOM,它的分页按钮用了 data-index 数据属性而不是纯 a 标签,这让我想到:与其找平台的"下一集"按钮,不如去找它背后的索引字段,直接改 URL 的集数参数,比模拟点击稳得多。
9 月 11 日在一台公共电脑上,搜索引擎网址被改成一个奇怪的跳转站,每次脚本触发下一集时都先过一次重定向,连播就断在那。纠正设置之后恢复,但这提醒我:浏览器的全局配置是会渗进页面行为的,不能只看页面本身。
9 月 14 日下午三点二十,我坐在朝阳区一家开在二楼的咖啡馆靠窗位置,桌上一台旧 MacBook、一杯已经凉掉的美式、还有一张写满时间戳的便签。那天我做的是最后一轮验证:把脚本装进三个不同的浏览器(Chrome 141、Edge 141、Firefox 143),用同一门《中国法制史》的 12 讲录像跑一遍,看它能否在每一讲结束时准确翻到下一集。便签上记下的第一条是「第 1 讲 结束后 0.8 秒翻页」,第二条是「第 4 讲 结束后 0.3 秒翻页」,到第 9 讲的时候,Edge 上卡住了,日志显示 ended 事件根本没来——那一瞬间我有点想放弃,因为前面八讲都顺,偏偏第九讲像是故意跟我作对。
后来把这一讲的网络请求抓出来看,才发现它用的是一段被切得更碎的 MSE 流,duration 回填得比前几讲晚了大约 1.2 秒,而我的脚本在视频元素刚挂载时就开始监听,等真实 duration 出来时,监听器绑定的那个 video 实例已经被平台换掉了。也就是说,我监听的是一个"死掉"的媒体元素,浏览器当然不会给它派发 ended。这个细节我在 FireFox 上反而没复现,因为它的媒体元素重建策略不一样,同一段代码在三家浏览器里跑出三种结果,这件事本身比脚本能不能跑通更让我在意。
说实话,市面上讲这个话题的文章里,大部分把自动播放讲成了"模拟点击下一集按钮",我个人认为这个方向是错的,而且错得挺危险。原因有两点:一是按钮的选择器会随平台改版随时失效,二是模拟点击会触发平台的埋点,容易被判定成异常行为。更稳的做法是回到媒体元素本身——监听 ended,或者在 MSE 场景下用 buffered 与 duration 的差值做兜底判断,然后只改 URL 里的集数参数去驱动翻页。这套思路在我 9 月 14 日那轮测试里,十二讲里十一讲都稳定通过,唯一失败的是第九讲,而那次失败恰恰证明了"监听媒体元素"这条路有它的边界,不是万能药。
还有一件必须写下来的事。那天傍晚六点四十,咖啡馆要打烊,我把机器合上准备走,邻桌一个叫陈默的中年男人问我在忙什么,我顺口说了句"在弄网课怎么自动跳到下一集",他愣了一下,说他女儿去年准备考研,就是每天熬夜守着点"下一集",有时候一集结束人去倒水,回来发现进度没动,白等四十分钟。他这句话让我把原本打算删掉的那段"失败记录"又留了下来,因为成功的脚本人人会写,失败的那 1.2 秒差值和那次监听器失效,才是后来者真正需要知道的东西。走出咖啡馆的时候天已经黑透了,我没觉得轻松,只觉得像长途跋涉之后,终于可以坐下来把路上的每一块石头都记下来。
我是江晚,白天在一家出版社做数字内容编辑,晚上写一点浏览器与网页媒体层的东西。这个站是我从 2026 年 8 月开始攒起来的,起因很简单:连续三周熬夜看网课,每次都卡在"下一集"按钮上,气得我决定把浏览器到底怎么判断视频播完这件事彻底弄清楚。
我写东西偏慢,一段话往往要改三四遍,喜欢把时间、版本号、具体报错都留在正文里,因为我自己查资料时最恨那种"大概能行"的写法。站里的每一篇记录,我都尽量标清楚在哪台机器、哪个浏览器版本、几点几分跑通的,哪怕它看起来很琐碎。
到今天为止,这个站一共整理了 31 条实操记录,其中 7 条是失败的。我把失败也放上来,因为那些失败比成功更有参考价值。如果你也在跟这件事较劲,欢迎照着我踩过的坑再走一遍。
播放器的"结束",不是消失,而是把控制权轻轻交还给你——如果你没伸手,它就停在那里。
—— 杰里米·基思,《HTML5 媒体与 Web 应用开发》(O'Reilly,2019 年第 2 版),第 6 章讲 ended 事件的那一页