Haven的小空间

自建 B站学习进度追踪工具

2 min read

B站上有很多高质量的课程,但它自带的「学习记录」功能只能看到单集进度,看到一半合集视频时根本不知道整体学了多少。于是我做了一个自己用的工具:Bili Progress。

它能做什么

  • 追踪 B站收藏夹里每个视频的整体进度(不是单集,是整个合集的百分比)
  • 每天凌晨自动同步一次,也可以手动触发
  • 进度达到 100% 自动标记完成
  • 手机可以安装成 PWA,像 App 一样使用

怎么用

首先在设置页填入 B站账号的 SESSDATA(登录凭证)。

找 SESSDATA:

  1. 登录 B站,打开任意页面
  2. 按 F12 → Application → Cookies → https://www.bilibili.com
  3. 找到 SESSDATA 这一行,复制值

添加要追踪的视频:

点击主页右上角的「+」按钮,会拉取 B站最近的历史观看记录,点击列表里的视频即可加入追踪。

有两点需要注意:历史记录只显示固定数量,所以需要追踪哪个视频,最好先去 B站看一下,确保它出现在列表里;另外历史记录里的视频封面目前无法加载,只能靠标题识别。

填好 SESSDATA 之后点「立即同步」,工具会拉取所有已追踪视频的观看进度并更新圆柱体高度。之后每天凌晨自动同步,也可以随时手动触发。

手机上在浏览器菜单里选「添加到主屏幕」可以装成 PWA,图标直接放桌面,用起来和原生 App 差不多。

技术栈

  • 前端:Vue 3 + Vite,部署后是纯静态文件
  • 后端:Node.js + Express,调用 B站 API 拉取历史记录
  • 数据库:SQLite,轻量够用
  • 部署:运行在自己的服务器上,通过 Cloudflare Tunnel 对外暴露

最难的一个问题:多 P 进度计算

B站 API 返回的是每一集(分 P)的观看进度,但我需要的是整个合集的整体进度。

比如一个 70 小时的课程,第一集看了 1 小时,如果直接用第一集的进度(100%)来展示,显然不对。

正确的算法:

整体进度 = (前面所有集的总时长 + 当前集已看时长) / 合集总时长 × 100%

听起来简单,但实现时需要把所有分 P 的时长都拿到,B站 API 不会一次返回这些数据,要额外请求并缓存。

缓存设计

每次同步都要查视频信息(分 P 列表、每集时长),如果每次都请求 B站 API 会很慢,也容易被限流。

所以做了三层缓存:

  1. 内存缓存:同一次同步里如果多个视频属于同一合集,只请求一次
  2. SQLite 缓存:7 天有效期,大部分视频信息不会变
  3. B站 API:只有缓存过期才真正请求

安全方面踩的坑

做完第一版后做了一次完整的安全审查,发现了不少问题,挑几个典型的说:

密码比较时序泄露

原来的代码在比较密码前先判断长度是否相等,这会泄露密码长度信息。正确做法是不管长度,直接用 crypto.timingSafeEqual 做常数时间比较。

错误信息泄露

B站 API 返回的错误码被直接拼进了响应里,攻击者可以通过错误信息推断内部状态。改成统一返回「未知错误」,错误码只写到服务端日志。

默认密钥

JWT_SECRET || 'dev-secret-change-me' 这种写法很危险——如果忘了设置环境变量,系统会用一个公开的默认值。改成启动时检查,没设置直接报错退出。

数据库文件权限

SQLite 文件默认权限是 644(所有用户可读),而里面存着加密后的 B站 Cookie。改成 600,只有运行服务的用户可以读。

SESSDATA 加密存储

B站的登录凭证(SESSDATA)是最敏感的数据,存到数据库前用 AES-256-GCM 加密,密钥从 JWT_SECRET 派生。即使数据库文件被拿走,没有密钥也解不开。

总结

做这个项目最大的收获不是功能本身,而是第一次认真地把安全当成需求来做——不只是让功能跑起来,还要考虑每个数据的存储方式、每个接口的边界情况、每个错误信息会不会暴露内部信息。

工具本身只供自己使用,但安全意识是可以迁移的。


项目页面和截图:Bili Progress →