Bilibili-Evolved 跳过充电鸣谢组件深度解析:自动跳过视频结尾充电鸣谢的实现原理与配置指南
2026/9/20 6:10:01 网站建设 项目流程
  • 前端
  • 音视频

【免费下载链接】Bilibili-Evolved

强大的哔哩哔哩增强脚本

项目地址:https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved
点击查看免费下载

导读

视频末尾的"充电鸣谢"列表会打断观看节奏,尤其在连续追剧时非常烦人。Bilibili-Evolved(哔哩哔哩增强脚本)内置了"跳过充电鸣谢"组件,可在视频播放结束后自动跳过 UP 主充电鸣谢展示,并支持隐藏播放器内的充电鸣谢面板。本文以该组件的功能文档为起点,结合仓库源码逐层拆解其"CSS 隐藏 + 事件触发自动点击"的双重实现机制、页面匹配范围、生命周期管理与功能边界,帮助读者理解增强脚本如何以最小侵入方式干预 B 站播放器,并掌握该组件在 Bilibili-Evolved 中的启用与行为控制方式。

一、功能概述与使用场景

该组件的功能描述定义在 registry/lib/components/video/player/skip-charge-list/index.md:

自动跳过视频结尾的充电鸣谢。注意:不包括番剧承包鸣谢。

这段描述虽然简短,但明确了三个关键事实:

  1. 自动跳过:无需手动点击,视频播放结束后组件会自动触发跳过操作;
  2. 跳过对象是"充电鸣谢":即视频播放完毕、画面结束后展示的 UP 主充电(打赏)用户名单;
  3. 功能边界:番剧(Bangumi)承包鸣谢不在此组件处理范围内,二者是 B 站两套不同的鸣谢机制。

从使用场景看,该组件主要服务于:追番/看视频时不想被片尾鸣谢列表打断,希望在视频结束的第一时间自动跳过,进入推荐流或下一集。组件属于"视频播放器"类别(tags: [componentsTags.video]),可在 Bilibili-Evolved 的设置面板中按组件名skipChargeList查找并开关。

二、双重实现机制:隐藏面板与自动点击

跳过充电鸣谢并非单一的 JS 逻辑,而是由样式隐藏事件监听两条链路共同完成,二者分别承载"静态隐藏"与"动态跳过"两个职责。

2.1 样式层:直接隐藏充电鸣谢面板

组件通过 SCSS 文件 charge-list.scss 定义隐藏规则:

body:not(.skip-charge-list-disable) .bilibili-player .bilibili-player-electric-panel, body:not(.skip-charge-list-disable) #bilibili-player .bpx-player-electric-panel { display: none !important; }

该规则同时覆盖了 B 站两代播放器的充电鸣谢面板:

  • .bilibili-player .bilibili-player-electric-panel:老版(2.X)播放器的充电鸣谢面板;
  • #bilibili-player .bpx-player-electric-panel:新版(3.X,即 bpx)播放器的充电鸣谢面板。

选择器以body:not(.skip-charge-list-disable)作为前置条件,意味着当body上存在skip-charge-list-disable类时,隐藏规则失效——这正是组件卸载(unload)时恢复原状的开关,详见下文生命周期分析。

2.2 逻辑层:监听 ended 事件自动点击跳过按钮

样式隐藏适用于面板"已经出现"的情况,但如果播放器在视频结束后立即弹出充电鸣谢并阻塞播放流程,仅靠隐藏 DOM 不够彻底,组件因此在 index.ts 中实现了自动点击逻辑:

videoChange(async () => { const video = await select('video') video?.addEventListener('ended', async () => { if (document.body.classList.contains(SkipChargeListDisabledClass)) { return } // 选择2.X或3.X的跳过按钮 const jumpButton = (await select( '.bilibili-player-electric-panel-jump,.bpx-player-electric-jump', )) as HTMLElement jumpButton?.click() }) })

核心流程分为三步:

  1. 等待视频变化:通过videoChange监听当前页面的视频切换(换集、换 P 等场景),每次视频变化后重新绑定监听;
  2. 等待 video 元素就绪:使用select('video')轮询获取播放器中的<video>元素;
  3. 监听 ended 事件:视频播放结束时,轮询查找"跳过按钮"并触发.click()。按钮选择器同样兼容两代播放器——.bilibili-player-electric-panel-jump(2.X)与.bpx-player-electric-jump(3.X)。
轮询机制:select 的底层实现

select来自 src/core/spin-query.ts,其本质是基于setTimeout的轮询查询器(spin-query)。默认轮询配置定义在源码第 11-14 行:

const defaultConfig: Required<SpinQueryConfig> = { maxRetry: 15, // 最大重试次数 queryInterval: 1000, // 重试间隔(ms) }

即:每隔 1 秒查询一次选择器结果,最多重试 15 次;查询期间若页面处于后台标签页,会通过waitForForeground等待页面重新获得焦点后再继续重试(见 spin-query.ts)。这种"带前台等待的轮询"避免了后台标签页空转消耗资源,也保证了切换回页面时组件能及时响应。

select还带有查询缓存:同一选择器在短时间内重复调用会复用上一次的 Promise(源码selectCache),减少重复轮询开销;查询结束后缓存自动清除,防止拿到过期的 DOM 引用。

事件驱动:videoChange 的视频变化监听

videoChange定义在 src/core/observer.ts,它通过allMutations(MutationObserver 封装)持续观察页面 DOM 变化,并检测全局unsafeWindow.cid的变更来判定"视频是否切换"。源码中还对 B 站的一个特殊行为做了兼容——更换 cid 时 B 站代码会临时把 cid 改成一个数组,监听逻辑会忽略这种异常值,仅在 cid 正常变化时触发videoChange事件(见 observer.ts)。

这意味着:在普通视频页、分 P 切换、连续播放下一集等场景下,组件都会重新绑定ended监听,确保每一段视频的结尾都能被自动跳过,而不是只在首次加载时生效。

三、组件生命周期:reload 与 unload 的正确开关

组件在元数据中显式声明了reloadunload钩子(见 index.ts):

reload: () => document.body.classList.remove(SkipChargeListDisabledClass), unload: () => document.body.classList.add(SkipChargeListDisabledClass),

其中SkipChargeListDisabledClass = 'skip-charge-list-disable'(源码第 5 行)。这套设计非常精巧:

  • 卸载(unload)时:向body添加skip-charge-list-disable类。由于 SCSS 中所有规则都以body:not(.skip-charge-list-disable)为前置条件,样式隐藏立即失效,充电鸣谢面板恢复显示;同时 JS 逻辑在ended回调开头检查该类,一旦存在就直接return,不再自动点击跳过按钮。两条链路被一个类名同时关停,且无需移除已注入的<style>标签,代价极低、切换瞬时完成;
  • 重载(reload)时:移除该类,两条链路即刻恢复。

与之对比,styled-component.ts 中提供的通用toggleStyle工具(源码第 22-45 行)采用"动态创建/移除<style>标签"的方式实现样式开关。skip-charge-list 组件选择"类名开关"方案而非移除样式标签,既避免了频繁增删 DOM 节点,也保证了样式隐藏与 JS 跳过逻辑在开关状态上始终一致——这是该组件在工程实现上一个值得借鉴的细节。

四、页面匹配范围:仅在播放器页面生效

组件元数据中声明了urlInclude: playerUrls(见 index.ts),限定组件只在包含播放器的页面注入。playerUrls定义于 src/core/utils/urls.ts:

export const playerUrls = [ '//player.bilibili.com', '//www.bilibili.com/html/player.html', ...videoUrls, // 普通视频页 ...bangumiUrls, // 番剧页 ...cheeseUrls, // 课程页 ]

即组件覆盖四类页面:

页面类型匹配模式说明
嵌入式播放器//player.bilibili.com嵌入第三方页面的播放器 iframe
播放器独立页//www.bilibili.com/html/player.html播放器独立页面
普通视频页videoUrlswww.bilibili.com/video/
番剧/课程页bangumiUrlscheeseUrls番剧与付费课程播放页

页面匹配通过 urls.ts 的matchCurrentPage实现,videoChange内部也会先调用matchCurrentPage(playerUrls)做一次页面预检,非播放器页面直接返回false,避免无意义的监听注册(见 observer.ts)。

五、组件注册方式与扩展阅读

从组件定义结构看(defineComponentMetadata定义于 src/components/define.ts),skipChargeList是一个典型的 Bilibili-Evolved 标准组件,具备名称、显示名、分类标签、入口、生命周期钩子与 URL 匹配规则等完整元数据。其入口entry经由styledComponentEntry包装(styled-component.ts 实际上是 src/components/styled-component.ts),该包装器会先动态加载组件 SCSS 并注入全局样式,再执行真正的逻辑入口——也就是说,样式注入与逻辑挂载被统一封装,任何采用styledComponentEntry的组件都遵循"先样式、后逻辑"的加载顺序。

读者如果希望深入理解本文涉及的底层能力,可以按以下路径继续阅读仓库源码:

  • registry/lib/components/video/player/skip-charge-list/index.ts:组件完整实现(入口、选择器、生命周期钩子);
  • registry/lib/components/video/player/skip-charge-list/charge-list.scss:样式隐藏规则(两代播放器面板选择器);
  • src/core/spin-query.ts:select轮询器实现(重试次数、间隔、缓存与前台等待策略);
  • src/core/observer.ts:videoChange视频变化监听机制(cid 钩子与数组异常值兼容);
  • src/core/utils/urls.ts:playerUrls页面匹配列表定义;
  • src/components/styled-component.ts:styledComponentEntry样式注入封装与toggleStyle工具对比。

六、小结:一个"小而完整"的增强组件范本

"跳过充电鸣谢"虽然功能描述只有一句话,但它的实现浓缩了 Bilibili-Evolved 组件体系的多项核心能力:样式与逻辑的双通道协作(SCSS 隐藏面板 + ended 事件自动点击)、跨播放器版本兼容(2.X 与 3.X 选择器双覆盖)、低开销的开关机制(body 类名统一控制两条链路)、事件驱动的动态绑定(videoChange 跟随视频切换重挂监听)以及页面范围精确限定(playerUrls)。对于希望理解 Bilibili-Evolved 组件如何与 B 站播放器交互、或参考其模式编写新组件的开发者而言,这是一个麻雀虽小、五脏俱全的范本。使用时只需在脚本设置面板中找到"跳过充电鸣谢"并保持开启即可,组件会自动完成其余工作。

  • 前端
  • 音视频

【免费下载链接】Bilibili-Evolved

强大的哔哩哔哩增强脚本

项目地址:https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询