如何备份B站收藏夹?mybili弹幕下载背后的WBI签名与protobuf解析完全揭秘
【免费下载链接】mybili哔哩哔哩收藏夹备份程序 ~ アレ 视频不见了哟!项目地址: https://gitcode.com/gh_mirrors/my/mybili
mybili是一款开源的哔哩哔哩收藏夹备份程序(README.md),它能把你的 B 站收藏夹视频、封面、弹幕定时同步到本地 NAS,实现"视频不见了也有备份"的安全感。其中最有技术含量的部分,就是它如何绕过 B 站的接口保护:用 WBI 签名算法合法请求弹幕接口,再把返回的 protobuf 二进制数据手工解析成可读弹幕。本文将带你完整看懂这套逆向实现。
📺 mybili 是什么:一句话看懂功能
mybili 是一个部署在 NAS 上的后台服务,核心能力包括:
- ⏰ 定时 5 分钟拉取收藏夹所有视频信息(标题、封面、简介)
- 🚀 按最高画质自动下载视频到本地
- 💬全量下载并缓存每个分 P 的弹幕,支持在线播放装载
- 📂 生成 Emby 等媒体库可读的目录结构
- 📡 订阅 UP 主 / 合集,自动发现新视频
先看它的 Web 管理界面长什么样:
进入具体收藏夹后,可以看到所有已备份的视频,右上角可以切换下载状态:
下载进度页会展示已缓存视频比例、有效/失效/冻结视频分类统计:
🎫 为什么需要 WBI 签名?
B 站为了保护接口不被滥用,在 2023 年给一批重要接口(包括弹幕接口)加上了WBI 签名校验:请求 URL 必须携带一个由动态密钥计算出的w_rid参数,否则服务端直接拒绝。
传统做法是解析 B 站前端混淆 JS 来还原算法。而 mybili 的工程师选择了更"硬核"的路线——纯 PHP 逆向复现整个签名流程,全部逻辑集中在一个文件里:app/Services/BilibiliService.php。
第 1 步:从导航接口偷取动态密钥
WBI 密钥不是固定的,每次都要实时获取。mybili 请求 B 站网页导航接口/x/web-interface/nav,从返回的wbi_img字段中取出两张图片 URL(img_key和sub_key):
https://i0.hdslb.com/bfs/wbi/xxxxxx.png ← img_key https://i0.hdslb.com/bfs/wbi/yyyyyy.png ← sub_key注意取 key 的技巧(BilibiliService.php 第120-121行):从 URL 中截取去掉扩展名的文件名作为密钥字符串——图片本身只是幌子,真正有用的是文件名。
第 2 步:固定乱序表混合出 32 位密钥
拿到两个 key 后拼接,再通过一张写死的 64 位乱序索引表重新洗牌:
| 混合表(前10位) | 46 | 47 | 18 | 2 | 53 | 8 | 23 | 32 | 15 | 50 |
|---|
按索引从拼接串中逐个取字符,取满 32 位,得到mixin_key。这张表是逆向前端代码时挖出来的常量,只要 B 站不改混淆逻辑它就永远有效(encWbi 函数)。
第 3 步:参数排序 + MD5 出 w_rid
签名最后一步非常朴素:
- 把所有请求参数值中的特殊字符(
!'()*)过滤掉 - 按键名字典序排序
- 拼成查询串,末尾追加
mixin_key - 整串做MD5→ 得到
w_rid参数
w_rid = md5(排序后的query字符串 + 32位混合密钥)整个签名只有约 40 行 PHP 代码,没有任何外部依赖。这套算法的完整实现见 encWbi()。
🧩 弹幕接口:按 6 分钟一片分段请求
弹幕接口/x/v2/dm/wbi/web/seg.so有两个特点:
- 必须带 WBI 签名(就是上面破解的那套)
- 按时间段分片返回:每 6 分钟(360 秒)一个分段,长视频要循环请求多段
mybili 的处理方式很聪明(getDanmaku 方法):
$segmentCount = ceil($duration / 360); // 视频时长 ÷ 6 分钟 = 分段数一个 30 分钟的视频会被拆成 5 段依次请求,每段都独立签名,最后把各段弹幕合并成一个完整数组。
更关键的是:这个接口不返回 JSON,而是返回 protobuf 二进制流。
🔬 硬核部分:手工解析 protobuf 二进制
protobuf 是 Google 的高性能二进制序列化格式,一般解析需要引入google/protobuf库并预先生成 PHP 类文件。但 B 站弹幕接口的结构并不公开 proto 定义文件,mybili 选择了一条更野的路:逐字节手写解析器,不依赖任何 protobuf 库。
先搞懂 protobuf 的两个基本功
① Varint(变长整数):每个字节只取 7 位有效数据,最高位是"是否还有下一字节"的标记位。小于 128 的数只占 1 字节,越大占得越多。
② Tag 字节:每个字段开头都有一位 tag,高 5 位是字段编号,低 3 位是线型(wire type):
| 线型 | 含义 | 读取方式 |
|---|---|---|
| 0 | Varint | 循环读,直到最高位为 0 |
| 1 | 64 位固定长度 | 直接跳过 8 字节 |
| 2 | 长度前缀(字符串/嵌套消息) | 先读 varint 长度,再读对应字节 |
| 5 | 32 位固定长度 | 直接跳过 4 字节 |
外层结构:找出"弹幕数组"字段
解析器从二进制流第 0 字节开始循环(parseDanmakuProtobuf):
- 读到字段编号 = 1且线型 = 2 → 这就是"弹幕条目",读出长度后截取该段,交给单条弹幕解析器
- 其他字段 → 按上表线型规则直接跳过(B 站以后加新字段也不会导致解析崩溃)
内层结构:字段编号对照表
单条弹幕的子字段解析在 parseSingleDanmaku,逆向还原出的字段表如下:
| 字段编号 | 名称 | 线型 | 含义 |
|---|---|---|---|
| 1 | id | varint | 弹幕唯一 ID |
| 2 | progress | varint | 出现时间(毫秒) |
| 3 | mode | varint | 弹幕模式(1-3 滚动 / 4 底部 / 5 顶部) |
| 4 | fontsize | varint | 字号 |
| 5 | color | varint | 颜色(十进制,如 16777215 = 白色) |
| 6 | midHash | bytes | 用户哈希(脱敏 mid) |
| 7 | content | bytes | 弹幕文本内容 |
| 8 | ctime | varint | 发送时间戳 |
| 9 | weight | varint | 弹幕权重(决定优先显示) |
| 12 | action | bytes | 动作类弹幕标记 |
解析结果长这样(getDanmaku 中的注释):
{ "progress": 390379, "mode": 1, "color": 16777215, "content": "是我", "id": 162000000000000000 }从一坨不可读的二进制字节,到结构化弹幕数据,全程不到 200 行代码,且每个读取点都有边界检查防止越界崩溃——这就是"手工逆向"的完整闭环。
📥 从二进制到屏幕:弹幕的入库与展示
解析完只是开始,mybili 的弹幕链路还包含三个环节:
- 异步下载:DownloadDanmakuJob 队列任务驱动 DownloadDanmakuAction,用 Redis
setnx加锁防止同一分 P 重复下载,按分片全量拉取 - 增量入库:解析后的弹幕按 1000 条一批插入 SQLite 的 danmaku 表,通过弹幕 ID 去重只存新增
- 格式转换:Web 端播放时,DanmakuConverterService 把 B 站原始字段转成播放器通用格式——毫秒转秒、十进制颜色转
#FFFFFF十六进制、高级模式(6-9)降级为滚动模式,高级弹幕还会自动降级提取纯文本
前端弹幕播放器通过 /danmaku 接口 获取转换后的数据,即可在本地备份的视频上还原出"原汁原味"的弹幕体验。
📚 相关代码导航
| 模块 | 路径 | 说明 |
|---|---|---|
| WBI 签名 + 弹幕获取 | app/Services/BilibiliService.php | getDanmaku / encWbi |
| protobuf 解析器 | app/Services/BilibiliService.php | parseDanmakuProtobuf / parseSingleDanmaku |
| 弹幕下载动作 | app/Services/VideoManager/Actions/Danmaku/DownloadDanmakuAction.php | 加锁、分片、增量入库 |
| 弹幕格式转换 | app/Services/DanmakuConverterService.php | B 站格式 → 播放器格式 |
| 弹幕表结构 | database/migrations/2025_06_14_130110_create_danmaku_table.php | SQLite 存储 |
✅ 总结
mybili 用一次教科书级的逆向实践证明了:没有公开 API 文档 ≠ 数据不可得。
- WBI 签名:导航接口取动态 key → 固定乱序表混合 → 参数排序 MD5,40 行代码复现 B 站前端混淆逻辑
- protobuf 解析:不装库、不要 proto 文件,靠 Varint + Tag 两个基本功手写逐字节解析器,兼容 B 站后续字段变更
对于想学习逆向分析的开发者,app/Services/BilibiliService.php 这一个文件就是绝佳的实战教材:WBI 签名与 protobuf 二进制解析两大硬核技术,都浓缩在其中。
【免费下载链接】mybili哔哩哔哩收藏夹备份程序 ~ アレ 视频不见了哟!项目地址: https://gitcode.com/gh_mirrors/my/mybili
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考