8.1 KiB
对齐弹弹play能力(核心链路 + 协议/数据格式)— 设计规格
- 日期:2026-09-26
- 状态:设计已获用户批准,待规格审查 → writing-plans
- 仓库:本产品仓(业务进入现有分层 + 新增
@app/danmaku) - 前置:
2026-09-25-dandanplay-web-design.md(Web 版弹弹play 主体)已实现主链路
1. 目标与范围
在已有「挂载 → 找片 → 播放 → 弹幕 → 进度」之上,把弹幕能力与弹弹play 开放弹幕网络在体验与协议上对齐。
In scope
- 协议/数据兼容
- 开放 API:
/api/v2/match、/api/v2/comment/{episodeId}(含withRelated)、AppId 签名 - 数据格式:文件识别 hash(前 16MB MD5)、B 站/弹弹play 兼容弹幕 XML、匹配元数据
- 开放 API:
- 核心链路深对齐
- 候选集匹配与手选(不盲取第一条)
- 弹幕持久化与同目录自动关联(修 P1-3)
- 关联弹幕与渲染体验(
withRelated、prefs 服务端同步) - 弹幕过滤与屏蔽(关键词、类型)
Out of scope(YAGNI)
- 远程访问协议(弹弹play 连帆幕)
- protobuf 弹幕、多弹幕站聚合
- BT/RSS、AI 字幕、投屏、Emby/Jellyfin 直连(见产品文档「还没有」)
验收口径:真实链路可跑通(文件 → 匹配候选 → 手选 → 拉弹幕 → 过滤 → 持久化,刷新/多设备可恢复)。无凭证时降级路径也要可用。
实现路径(用户选定):方案 B — 独立 @app/danmaku 协议包(非垂直增强、非先契约后功能)。
2. 架构与包边界
新增纯协议包 @app/danmaku:无 DB、无 tRPC;HTTP 仅用全局 fetch。
| 模块 | 职责 |
|---|---|
signature |
base64(sha256(AppId + Timestamp + Path + AppSecret)) |
hash |
文件头 16MB MD5(流式,禁止整文件缓冲) |
match |
POST /api/v2/match 入参/出参、hashAndFileName / fileNameOnly、候选排序纯函数 |
comment |
GET /api/v2/comment/{episodeId}、withRelated、comments 结构化 |
xml |
B 站/弹弹play 兼容 XML 解析与序列化(p 字段双向) |
types |
EpisodeId、MatchCandidate、DanmakuComment 等纯类型 |
filter |
filterComments(关键词、类型、防重叠可选) |
依赖方向(只新增向下边,禁止循环):
web ──► trpc ──► dao ──► db ──► models
│ │
│ ├──► types
│ └──► danmaku(协议包)
└──► danmaku(XML 解析复用)
- trpc
danmaku.service:鉴权、落库、缓存、绑定编排 + 调@app/danmaku - web:
danmaku-render.ts格式解析改为引用@app/danmaku的xml(单一解析实现) - 包位置:
packages/danmaku,构建方式对齐@app/types(vite + tsc dts)
3. 数据模型
Schema 源仍为 @app/models;查询进 @app/dao。
media_items 增列
| 字段 | 说明 |
|---|---|
dandanEpisodeId |
开放网络 episodeId;空 = 未绑定 |
danmakuMatchSource |
auto | manual | none |
danmakuMatchedAt |
上次匹配时间 |
danmaku_prefs(用户级,新表)
userId · enabled · opacity · density · blockKeywords(JSON) · blockTypes(JSON) · updatedAt
唯一 (userId)。blockTypes:scroll / top / bottom。
danmaku_docs(正文持久化,新表,修 P1-3)
id · userId · mediaItemId · source: open-network|local-xml|related · xml · byteSize · createdAt
唯一 (userId, mediaItemId, source)(同源仅留最新)。userId 级隔离。
danmaku_cache(保留,全局共享)
未绑定时 matchKey = sha256(path:size);绑定后缓存键改为 episodeId(集身份比文件身份稳定)。
Zod(@app/types)
danmakuMatchSelectSchema(mediaItemId + episodeId)danmakuImportSchema(替代仅byteSize的importMeta,可含 fileName)danmakuPrefsSchema(含 blockKeywords / blockTypes)- 输出:
MatchCandidate[]、DanmakuFetchOutput(source 扩展local-xml等)
4. 协议/数据格式对齐
| 对齐点 | 规范 |
|---|---|
| 文件识别 | 前 16MB MD5 + 文件名;有 hash 用 hashAndFileName,否则 fileNameOnly |
| 匹配 | POST /api/v2/match → matches[];按置信/用户选择写入 dandanEpisodeId |
| 弹幕 | GET /api/v2/comment/{id}?withRelated=true |
| 鉴权 | X-AppId / X-Timestamp / X-Signature(现有算法保留 + 单测向量) |
| XML | <d p="time,mode,size,color,timestamp,pool,uid,rowid">text</d>;round-trip 测试 |
| 降级 | 无凭证/失败 → 本地 XML 仍可用 |
环境变量沿用:OPEN_DANMAKU_APP_ID / OPEN_DANMAKU_APP_SECRET / OPEN_DANMAKU_API_BASE。
5. 页面与交互
路由不变;只增强 /watch、/danmaku 等组件。
候选集匹配与手选
- 打开
/watch:无dandanEpisodeId→ 自动 match → 高置信绑定(source=auto)→ 拉弹幕 - 未匹配/低置信 → 「重新匹配」→ 候选列表(标题、集数、类型、置信)→ 点选绑定(
source=manual)→ 立即拉弹幕 - 已绑定可「更换匹配」;弹幕集绑定与 Bangumi 绑定相互独立
弹幕持久化与自动关联
- 导入 XML → 服务端
danmaku_docs(source=local-xml)(≤20MB)→ 刷新/换端可恢复 - 播放优先级:本地已导入 > 开放网络
- 浏览/扫描发现同目录
同名.xml→ 登记local-xml(正文按需从 WebDAV 拉取)→ 播放页提示「发现同名弹幕」
关联弹幕与渲染体验
withRelated并入;过滤面板可显示本集/关联计数- 继续用 artplayer-plugin-danmuku;
danmaku_prefs服务端同步(开关/透明度/密度),localStorage 仅作无网络回退 - 密度
thinDanmaku仍在客户端
弹幕过滤与屏蔽
- 播放页过滤面板:关键词、类型(滚动/顶部/底部)、防重叠(插件已有)
- 设置页:默认值 + 屏蔽词编辑 →
danmaku_prefs - 过滤在 XML → comments 之后、喂播放器之前执行(
filterComments纯函数)
6. 错误处理
| 场景 | 策略 |
|---|---|
| 无开放网络凭证 | 明确提示「未配置弹幕网络」;本地 XML 全功能可用 |
| match 401/签名失败 | Toast 鉴权失败;不重试风暴 |
| match 无候选/低置信 | 进手选候选面板,不静默 |
| comment 失败 | 保留绑定;可重试;本地 XML 优先 |
| XML 解析失败 | 导入报错拒绝;自动关联静默跳过 |
| prefs 失败 | 回退 localStorage 会话级;不阻断播放 |
| 权限 | docs / prefs / 绑定均强制 userId 隔离 |
7. 测试与验证
@app/danmaku 单测:签名向量、16MB MD5、XML round-trip、filterComments、候选排序;match/comment 用 fixture 锁字段名。
service 测:绑定持久化、导入落库、prefs upsert、缓存键(path:size → episodeId)切换。
真实链路清单(验收)
- 配置凭证 → 未绑定视频自动匹配 → 弹幕上屏
- 低置信 → 手选 → 绑定刷新仍有效
- 导入 XML → 刷新/换端登录仍在
- 同目录同名 XML 自动关联
- 关联弹幕计入;屏蔽词/类型即时生效且持久
- 断网/无凭证降级:可播、本地 XML 可用
命令:yarn typecheck · yarn lint · yarn test · yarn build:web
8. 风险与开放问题
- 凭证:当前
.env的OPEN_DANMAKU_APP_ID为空。真实链路需提供 AppId/AppSecret,或先用OPEN_DANMAKU_API_BASE指向 mock 跑通再切官方。 - 开放 API 字段:实现时以
doc.dandanplay.com/open实测为准;fixture 与适配器单点收口在@app/danmaku。 - 同目录 XML 自动关联:避免扫描时全量拉 WebDAV 正文;按需拉取 + 缓存。
- 协议包勿膨胀:若未来要多端复用再抽;本规格不做远程访问协议。
9. 批准记录
| 节 | 结果 |
|---|---|
| §1 架构与包边界 | OK(方案 B) |
| §2 数据模型 + 协议/数据格式 | OK |
| §3 页面与交互(四块) | OK |
| §4 错误处理 + 测试验证 | OK |
| 验收口径 | 真实链路可跑通 |
| 范围 | 协议/数据兼容 + 核心四块全选 |