能不能蹭蹭你的大会员看个视频?
最近偶然得知朋友开通了某站大会员,于是有了个兴趣:我能不能在使用自己账号的前提下,仅针对视频流,复用朋友的权限来观看高码率视频?
其实这个问题也并非空穴来风的兴趣。大约四年前在 Web 端我就使用过 Joybook,后来还有 hd2a;移动端虽然了解不多,但同样可以沿用类似的思路——最直接的方式是在第三方客户端(如 PiliPlus)上实现,当然,或许也可以尝试通过 ReVanced(如哔哩漫游X)的方式直接对官方客户端进行集成。
这篇文章不提供任何现成的产品或服务,也不涉及具体的代码实现,仅从技术层面探讨相关思路:客户端怎样在尽量不改动播放器主体的前提下,获得更高权限下的官方出流结果。
先定义要解决什么问题
官方播放接口的定义、请求参数拼装方式以及 JSON 结构这些问题已经被开源社区相关的 API 收集仓库(如 bilibili-API-collect)整理得非常完善,所以我们不需要做重复的抓包与分析。
我们只需要理解一个无需赘述的事实:
同一个视频流接口,在不同身份下,返回的结果不一样。
以同一条视频为例,请求者可能是游客、普通登录用户、具备完整权益的大会员用户,此时接口的差异会直接体现在:
- 是否能拿到高码率/高帧率流(如 1080P+、4K、HDR、高帧率)
- 是否能拿到完整的
dash流 - 是否会被要求强行试看
- 是否会返回会员限制、付费提示等附加状态
所以,“复用权限,仅针对视频流进行替换”,最主要解决的也就是:我们需要把“请求视频流接口的人”换掉。
数据应怎样流转?
如果从客户端架构的角度看,这个方案最准确的流转逻辑应该是:
[客户端] ──(1. 挂载高权限凭证)──> [发起官方 playurl 请求]
│ │
│ (2. 官方接口鉴权)
▼ ▼
[喂给原播放器] <──(3. 返回完整 DASH 流)─┘
- 客户端先获得一份可用的高权限身份凭证
- 由客户端自己继续向官方播放接口发起请求
- 官方接口根据这份凭证,返回更完整的 DASH 播放信息
- 客户端再把这份响应数据注入回自己的播放器中
它不是自己生成媒体流,也不是伪造脱离官方的播放结果。
它更像是:客户端仍然走官方原路,只是把认证视角从“当前普通身份”临时切换为“另一份可用身份”。
这一本质决定了整个方案在工程上的设计可以非常“克制”:
- 不重写播放器:播放器本身处理媒体的能力是完备的
- 不私有化协议:不需要自建中转服务,官方响应本身是最完整的
- 只做两端增强:干预动作仅发生在“请求前”与“响应后”,不做其他额外的操作
四个层级
我们可以将这套方案抽象为四个核心层级:
┌─────────────────────────────────────────────────────────┐
│ 状态与 UI 协同层 │ <-- 弹窗/清晰度菜单
├─────────────────────────────────────────────────────────┤
│ 响应注入与标准化层 │ <-- 假装原装响应
├─────────────────────────────────────────────────────────┤
│ 请求接管与重发层 │ <-- 拦截/Fallback
├─────────────────────────────────────────────────────────┤
│ 身份上下文提供层 │ <-- Cookie/Token 管理
└─────────────────────────────────────────────────────────┘
身份上下文提供层
这一层只负责一件事:提供一个可用于请求官方播放接口的高权限认证上下文。
最常见的形式就是 Web Cookie,尤其是参与鉴权的关键字段。这一层不必关心播放器与 UI,它的边界非常明确:
| 输入 | 内部处理 | 输出 |
|---|---|---|
| 配置、令牌 | 检查有效性、判定过期、自动化刷新 | 一份认证上下文 |
请求接管与重发层
这一层决定了方案的接入形态,根据环境的不同,通常分为两种设计方案:
- 方案 A:客户端内部前置解析器(适用于 App)
播放器请求地址 -> 自定义解析中间件 -> (成功) -> 注入高权限 DASH
-> (失败) -> 回退原生官方逻辑
优点:链路清晰、可控性高、易于做容错回退,属于标准的工程插件化设计。
- 方案 B:页面侧拦截代理(适用于 Web 注入脚本 / 浏览器扩展)
思路:劫持 fetch / XHR。当发现目标为关键播放接口时抢先处理,挂载高权限 Cookie 重发请求,并将响应伪装后返回给页面。
优点:无侵入式接入,无需修改原网页播放器源码。
在复现请求时,应遵循以下原则:
- 保留原请求中的
bvid、cid、qn、fnval等核心参数,只替换认证身份 - 区分直接出流接口与带
playview壳结构的接口,针对普通视频、番剧、课程等不同类型做路径适配。 - 必须接受身份过期或接口风控的可能性。“先尝试增强请求 -> 成功则使用 -> 失败则立即回退原生”,绝不能让增强逻辑成为单点故障源。
响应注入与标准化层
拿到高权限视角下的 JSON 响应后,需要将其“清洗”为当前播放器所期待的格式:
- 模式一:直接替代(适用于单纯请求 URL 的播放器),直接将高权限 DASH JSON 作为 Response 返回。
- 模式二:局部替换(适用于复杂的网页播放器)。保留原接口返回的壳结构,仅将内部与出流相关的
dash/quality字段替换,同时清除need_vip、trial_view等标志位。
状态与 UI 协同层
只换掉视频流是不够的。由于页面行为由多方面构成,若 UI 状态不跟上,用户体验会极度割裂(例如:视频已是 4K,但画质菜单仍显示灰字,或频繁弹出付费提示)。
所以我们还需要:
- 隐藏会员付费弹窗与试看提示
- 修正页面对用户 VIP 身份的局部前端判断
- 保持画质菜单项与当前高码率流对齐
体验优化
锁定清晰度
网页初始化时,由于普通账号的权限问题,播放器的默认偏好会将画质锁定在1080P及以下,所以我们可以采用适度介入策略:
[播放启动] ──> 锁定目标高画质 (防止页面初始化被降级)
│
▼
[播放器就绪] ──> 监听用户操作 ──(检测到手切画质)──> 解除锁定,完全跟手
弹幕加载
如果只把视频流换掉,在一些时长较长的 PGC(番剧/影视)内容中,比如一部时长为2小时的电影,通常会遇到一个很影响体验的问题:视频播到一定的时长,后半段的弹幕没了。
问题的根源其实在于弹幕接口的加载机制,播放器本身是以 6 分钟为单位分段拉取弹幕,在发起具体弹幕请求前,它会先请求一次“弹幕元数据接口”,获取当前的 pageSize(分段时长)和 total(总分段数)。
当高权限视频流被注入后,视频的实际播放时长虽然变长了,但播放器持有的 total 依然是低权限状态下的旧值,这导致播放器误以为视频很短,拉完前几段后便不再继续请求后续弹幕。
因此为了保证体验的完整性,我们需要在视频流注入成功时,同步记录当前视频的 cid 与真实 timelength(总时长),针对性地拦截弹幕接口,解析其二进制 Protobuf 数据并提取 pageSize,随后按真实时长重新计算正确的总段数:
targetTotal = Math.ceil(timelength / pageSize)
最后将改写后的 Protobuf 字节流交回给播放器,即可让播放器按正确的总段数完整拉取弹幕。
最后的总结
整套方案的实现过程其实并不复杂,一方面,社区开源的 bilibili-API-collect 提供了非常完善的接口文档,另一方面,Web 端的 Joybook 和 hd2a 等先行方案也给出了宝贵的思路参考,最本质的“接口替换与签名重写”,在理解了相关的数据流是如何与播放器交互后,借助 AI 编程工具即可在很短的时间里就验证出原型。
真正拉开体验差距的,其实只在前面提到的那些细节——如何解决普通账号权限下,播放器的默认偏好问题,如何解决弹幕因视频时长变化而导致的消失等等问题。这些体验层面的打磨,才是类似方案最需要关注的重点。
文章开头虽然提到,仅从技术层面探讨相关思路。但我相信,如果你也有类似的需求,理解了这套解法背后的逻辑,你也能够实现一套属于自己的解法。
毕竟就连我自己,也在思考后续抽空换成一种基于浏览器网络层的无感方案,来替代掉文中 Web 端所用到的方案B。在初始化时序上,单靠劫持 fetch / XHR 的方式来换流,始终存在局限,为了保障换流后播放器的首帧加载体验,不仅需要强制禁用掉播放器的自动开播功能,还需额外兜底一些兼容细节,整体的侵入性依然偏高。


