Skip to content

About

PiliPlus

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

PiliLocal

使用 Flutter 开发的 BiliBili 第三方客户端

基于 PiliPlus 的本地化分支:不要求账号,收藏/关注/拉黑/进度/观看记录都存在本机


适配平台

  • Android
  • iOS
  • Pad
  • Windows
  • Linux

PiliLocal 做了什么

上游 PiliPlus 的绝大部分功能都依赖登录态(cookie / access_key / csrf)。本分支在其之上加了一层本地模式: 未登录时,收藏、关注、拉黑、不感兴趣、观看记录、稍后再看、播放进度这些数据改存本机(Hive), 相关的读取接口也改成读本地,因此不登录也能正常用。

登录状态下行为完全不变:仍然走服务端,数据与服务端同步。


新增功能(相对上游)

  • 播放进度记忆(本地)

    • 按分 P(cid)记录进度,未登录也能续播
    • 记住"最后观看的分 P",再次进入自动续播该分 P
    • 播放到结尾附近(距总时长 < 400)标记为看完,不再续播
    • 入口:视频播放页(无需任何额外入口,行为与登录时一致)
  • 观看记录(本地)

    • 记录稿件标题/封面/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 的唤起路径)

设计原则

  1. 服务端优先:所有本地分支都以 !Accounts.main.isLogin 为门槛,登录用户走的还是原来的接口,行为不变。
  2. 只做"可见效果",不伪造账号能力:本地模式不改变 B 站算法、不产生任何服务端状态。 本地的拉黑/不感兴趣不会告诉 B 站——网页端和其它客户端仍然看得到;动态的点赞/评论/转发/删除仍然需要登录(本地模式是只读浏览)。
  3. 凡是能用公开接口拿到的信息就补齐:收藏、关注、黑名单展示所需的标题/封面/昵称/头像, 都用公开的 x/web-interface/* 接口补(这些接口匿名可用),拿不到时用兜底文案,不允许因为是 null 就崩。
  4. 不做会被风控的高频请求:实测(见下)匿名并发拉多个 UP 的动态会直接撞风控, 因此不做"关注流聚合",改为"点头像才拉一个 UP",把请求量压到每次点击 1 次。
  5. 写入即生效:拉黑与不感兴趣直接复用 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 字符 体积上限(实测一页动态约 150350 KB,约合 4080 页)
touchIntervalSeconds 600 命中后多久刷新一次"最近使用时间"(LRU)
ttl 3 天 兜底数据最长保留时间

代码改动清单

新增(lib/utils/,共约 1350 行)

文件 行数 职责
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 + 体积上限)

修改(30 个文件,按职责分组)

  • 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.dart key 隔离、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 本仓库做了更激进的修改,感谢原作者的开源精神。

感谢使用


致谢

About

PiliPlus

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages