使用 Flutter 开发的 BiliBili 第三方客户端
基于 PiliPlus 的本地化分支:不要求账号,收藏/关注/拉黑/进度/观看记录都存在本机
- Android
- iOS
- Pad
- Windows
- Linux
上游 PiliPlus 的绝大部分功能都依赖登录态(cookie / access_key / csrf)。本分支在其之上加了一层本地模式: 未登录时,收藏、关注、拉黑、不感兴趣、观看记录、稍后再看、播放进度这些数据改存本机(Hive), 相关的读取接口也改成读本地,因此不登录也能正常用。
登录状态下行为完全不变:仍然走服务端,数据与服务端同步。
-
播放进度记忆(本地)
- 按分 P(
cid)记录进度,未登录也能续播 - 记住"最后观看的分 P",再次进入自动续播该分 P
- 播放到结尾附近(距总时长 < 400)标记为看完,不再续播
- 入口:视频播放页(无需任何额外入口,行为与登录时一致)
- 按分 P(
-
观看记录(本地)
- 记录稿件标题/封面/UP/时长/分 P,进度以秒计
- 支持搜索记录、单条删除、清空
- 入口:
我的→观看记录
-
稍后再看(本地)
- 加入/移除/清空,已看进度、播放量、弹幕数都会显示
- 老数据缺播放量/弹幕数时会用公开接口补齐(最多 10 条)
- 入口:视频页右下角
⋯→稍后再看;我的→稍后再看
-
收藏夹(本地)
- 默认收藏夹懒创建;新建/编辑/删除/排序收藏夹
- 收藏/取消收藏稿件,支持多选文件夹、批量移除、夹内排序
- 「收藏」状态、视频页收藏按钮高亮、
已收藏在哪些夹子全部来自本地 - 收藏时用公开接口补齐标题/封面/BV/cid/UP/播放量,收藏页可直接播放
- 入口:视频页右下角
⋯收藏、播放页收藏面板、我的→我的收藏
-
拉黑 UP(本地)
- 拉黑/解除,被拉黑的 UP 的视频会从推荐流、热门榜、相关视频、排行榜里过滤掉
- 黑名单管理页展示昵称与头像(缺信息时用公开名片接口补齐,见下方说明)
- 入口:视频卡片
⋯→拉黑;设置→隐私→黑名单管理
-
不感兴趣(本地)
- 标记后稿件从推荐流、热门榜、相关视频、排行榜里消失
- 支持直接「不想看这个 UP 主」=顺手拉黑
- 入口:视频卡片
⋯→不感兴趣
-
关注 UP(本地)
- 关注/取关,关注状态驱动按钮显示「关注 / 已关注」
-
我的关注列表(本地分页)、我的页关注数即时更新 - 入口:只有现成的两处 —— 视频播放页下方 UP 行、UP 主页的关注按钮
- 关注时顺手保存昵称/头像;
我的关注打开时用公开名片接口补齐缺失信息
-
动态页按 UP 看动态(本地)
- 动态页顶部头像条 = 本地关注列表(点某个 UP 原地切换到「UP」页签)
- 「UP」页签的数据来自该 UP 的空间动态(与用户主页的「动态」页签同一接口)
- 未登录时隐藏
Live面板与「我」,隐藏「发布动态」入口 - 未登录时「全部/投稿/番剧/专栏」页签给出明确提示(这些是服务端聚合流)
-
动态缓存与稳定性(未登录时)
- 首屏缓存优先:命中缓存立刻出内容,同时在后台静默刷新
- 服务端返回空页或失败时回退缓存,不再直接显示「没有数据」
- 下拉刷新会清掉该 UP 的首屏缓存,保证这次一定走网络
- 首屏失败/空包自动重试 2 次(延时 1s、2s;翻页不重试)
-
load_next多页改为合并 + 按动态 id 去重,不再丢页(此前会跳过一段动态)
-
桌面端窗口修复(Windows,附带改动,未单独回归验证)
- 修复候选:窗口只剩边框、内容透明(
pages/main/view.dart的透明度时机、utils/app_scheme.dart的唤起路径)
- 修复候选:窗口只剩边框、内容透明(
- 服务端优先:所有本地分支都以
!Accounts.main.isLogin为门槛,登录用户走的还是原来的接口,行为不变。 - 只做"可见效果",不伪造账号能力:本地模式不改变 B 站算法、不产生任何服务端状态。 本地的拉黑/不感兴趣不会告诉 B 站——网页端和其它客户端仍然看得到;动态的点赞/评论/转发/删除仍然需要登录(本地模式是只读浏览)。
- 凡是能用公开接口拿到的信息就补齐:收藏、关注、黑名单展示所需的标题/封面/昵称/头像,
都用公开的
x/web-interface/*接口补(这些接口匿名可用),拿不到时用兜底文案,不允许因为是null就崩。 - 不做会被风控的高频请求:实测(见下)匿名并发拉多个 UP 的动态会直接撞风控, 因此不做"关注流聚合",改为"点头像才拉一个 UP",把请求量压到每次点击 1 次。
- 写入即生效:拉黑与不感兴趣直接复用 App 原有的本地过滤集合, 推荐流/热门榜/相关视频/排行榜本来就在按它过滤,所以写完立刻生效。
同一台机器、匿名状态实测(脚本见 .fvm/,未纳入版本库):
| 接口 | 用途 | 匿名实测 |
|---|---|---|
/x/web-interface/view、/x/web-interface/card |
视频详情、用户名片 | ✅ code 0(用于补齐收藏/关注/黑名单信息) |
/x/web-interface/popular、首页推荐 |
热门、推荐 | ✅ code 0 |
/x/polymer/web-dynamic/v1/opus/feed/space |
空间图文动态 | ✅ code 0(20/20) |
/x/polymer/web-dynamic/v1/feed/space |
空间动态(单 UP) | -101,偶发 HTTP 412 风控(这就是加缓存+自动重试的原因) |
/x/polymer/web-dynamic/v1/feed/all(含 type=up) |
关注流(服务端聚合) | ❌ -101 账号未登录 |
/x/polymer/web-dynamic/v1/portal、/uplist |
UP 列表、直播面板 | ❌ -101 账号未登录 |
/x/v2/space、/x/v2/space/archive/cursor |
用户主页、空间投稿(app 接口) | ❌ -400(app 接口需要有效 access_key) |
/x/upload/web/image |
图片上传(发布动态) | ❌ 需要登录(csrf),未登录时界面没有失败回退 → 直接隐藏入口 |
结论:匿名可以拿到"内容类"的 web 接口,拿不到"用户态/聚合流"的接口,并且单 UP 动态会偶发风控。 所以本地模式选择"缓存 + 自动重试 + 只按点击拉单个 UP",而不是拼一条关注流。
新增的 Hive box(均在 lib/utils/storage.dart 注册,随 init / compact / close / clear 一起管理):
| box | 类型 | 内容 |
|---|---|---|
localWatchList |
Box<String> |
观看记录 h:<business>_<kid>、稍后再看 l:<aid>(JSON) |
localFav |
Box<String> |
收藏夹 f:<id>、收藏项 i:<folderId>:<rid>:<type>(JSON) |
localFollow |
Box<String> |
<mid> → {mid, name, face, time}(JSON) |
dynCache |
Box<String> |
空间动态缓存 <mid>:<offset> → {t, d}(原始 data) |
复用原有存储:
| 位置 | 内容 |
|---|---|
watchProgress(原有 box) |
原本用于离线缓存的播放进度,本地进度复用同一份:key 为 cid、值为毫秒,-1 表示已看完 |
localCache: blackMids |
已拉黑 mid 集合(App 原有,推荐流过滤共用) |
localCache: blackMidsInfo |
mid → {uname, face, mtime}(黑名单管理页展示用) |
localCache: dislikedItems |
本地「不感兴趣」的稿件列表 |
localCache: lastCid:<bvid> |
该稿件最后观看的分 P |
缓存预算(lib/utils/local_dyn_cache.dart,均可调):
| 参数 | 默认值 | 说明 |
|---|---|---|
maxEntries |
400 | 条数上限,每个 UP 的每一页算一条 |
maxTotalChars |
12 M 字符 | 体积上限(实测一页动态约 150 |
touchIntervalSeconds |
600 | 命中后多久刷新一次"最近使用时间"(LRU) |
ttl |
3 天 | 兜底数据最长保留时间 |
| 文件 | 行数 | 职责 |
|---|---|---|
| local_watch_progress.dart | 54 | 本地播放进度(按 cid)+ 最后观看分 P |
| local_watch_list.dart | 416 | 本地观看记录 + 稍后再看 |
| local_fav.dart | 446 | 本地收藏夹与收藏项 |
| local_follow.dart | 147 | 本地关注 + 变更通知 + 昵称头像回填 |
| local_black.dart | 114 | 本地黑名单(复用 blackMids)+ 信息回填 |
| local_dislike.dart | 76 | 本地「不感兴趣」 |
| local_dyn_cache.dart | 103 | 空间动态缓存(LRU + TTL + 体积上限) |
- http 层数据分支:
fav.dart、user.dart、video.dart、member.dart、follow.dart、black.dart- 收藏夹/收藏项、观看记录/稍后再看、收藏状态、关注状态、空间动态、关注列表、黑名单列表、用户关系、空间动态缓存
- 页面与门控:
fav/video/controller.dart、fav_sort/view.dart、common_intro_controller.dart、video/controller.dart、video/introduction/{ugc,pgc}/controller.dart、video/introduction/ugc/view.dart、video/member/view.dart、member/controller.dart、member_dynamics/controller.dart、mine/controller.dart、dynamics/controller.dart、dynamics/view.dart、dynamics_tab/controller.dart、dynamics/widgets/up_panel.dart、dynamics_create/view.dart、setting/models/privacy_settings.dart - 播放器与工具:
plugin/pl_player/controller.dart(进度上报时先存本地)、utils/request_utils.dart(关注动作)、utils/storage.dart(新 box 注册)、utils/storage_key.dart(新 key)、utils/app_scheme.dart(窗口透明度恢复)、pages/main/view.dart(窗口透明度)
-
单元测试:103 个用例全部通过(
flutter test)文件 覆盖 test/utils/local_watch_progress_test.dart进度读写、分 P 隔离、看完判定、非法参数 test/utils/local_watch_list_test.dart观看记录/稍后再看增删查、进度换算、数据隔离 test/utils/local_fav_test.dart夹子增删改排序、条目字段完整性、分页、收藏状态 test/utils/local_follow_test.dart关注/取关、时间序、回填、变更通知 test/utils/local_black_test.dart拉黑/解除、占位昵称、回填只补缺失项 test/utils/local_dislike_test.dart标记/撤销/去重/坏数据容错 test/utils/local_dyn_cache_test.dartkey 隔离、TTL、坏数据、条数/体积淘汰、LRU 续期 test/http/*_local_test.dart各 http 层的本地分支(act 5/6、act 1/2、收藏、稍后再看、不感兴趣、关注列表、头像条) test/http/dyn_merge_test.dart多页动态合并去重(不丢页) -
静态检查:
flutter analyze保持与改动前相同的基线(37 条 info,0 error / 0 warning)。 -
真网络检查:
.fvm/下的探针脚本(未纳入版本库)验证了匿名链路: 收藏补齐元数据、黑名单头像回填、关注状态、我的关注列表补全、不感兴趣过滤(热门榜 50 → 49 条)、 动态缓存兜底与缓存优先。 -
手工验证:视频续播、观看记录/稍后再看、收藏夹,以及"三个小点"里的拉黑/不感兴趣、 视频下方与主页的关注、动态页切换 UP 均已实测通过。
- 不会同步到 B 站:本地的拉黑、不感兴趣、关注、收藏都只在本机生效;B 站算法不学习,网页端/其它客户端仍可见。
- 本地关注没有分组(「特别关注」「关注分组」需要登录)。
- 动态只能按 UP 单独看:未登录拿不到服务端聚合的「全部动态」,也拿不到"有更新"标记、直播面板、未读数。
- 发布动态不可用:未登录时入口已隐藏(上传/提交都需要登录态)。
- 点赞/投币/评论/转发仍需要登录;本地模式下动态与视频为只读浏览。
- 收藏支持视频(type 2):番剧/课程/专栏/话题/笔记的收藏仍需要登录。
- 用户主页(
/x/v2/space)在部分网络环境下匿名返回-400:此时主页入口不可达, 但视频页下方、⋯菜单等入口仍然可用。 - 缓存可能比"此刻最新"旧一点:首屏缓存优先展示的是上次成功抓到的内容,下拉刷新会立刻取新的。
本仓库的构建产物见 Releases(若尚未发布,可克隆仓库在本地编译)。
上游 PiliPlus 的版本见 PiliPlus Releases。
与原项目一致。
本仓库使用 fvm 固定 Flutter 版本(.fvmrc);Windows 端需要 Visual Studio 工具链与 nuget
(flutter_inappwebview_windows 依赖)。
fvm flutter pub get
fvm flutter analyze
fvm flutter test
fvm flutter build windows --debug此项目(PiliLocal)是 PiliPlus 的本地化分支, 在保留上游全部功能的前提下,增加了"不要求账号、数据存本机"的本地模式(收藏、关注、拉黑、不感兴趣、 观看记录、稍后再看、播放进度)。
本分支与上游一样,是个人为了兴趣而开发,仅用于学习和测试,请于下载后24小时内删除。 所用 API 皆从官方网站收集,不提供任何破解内容。 本地模式的所有数据仅保存在你自己的设备上,不会上传到任何第三方服务器。
在此致敬原作者:guozhigq/pilipala 在此致敬上游作者:orz12/PiliPalaX 在此致敬本项目上游:bggRGjQaUbCoE/PiliPlus 本仓库做了更激进的修改,感谢原作者的开源精神。
感谢使用
