6.6 KiB
6.6 KiB
弹弹play 弹幕获取 + 刮削能力打通 — 设计规格
- 日期:2026-09-26
- 状态:设计已定(用户未在线,按最佳判断推进,详见批准记录)
- 上游规格:
2026-09-25-dandanplay-web-design.md(开放弹幕网络中继 + Bangumi 刮削已批准)
1. 问题
上一版实现后能力不可用/不完整,用户反馈「没有实现接入弹弹play 的弹幕获取和刮削能力」:
- 弹幕获取实际拿不到:
danmaku.service.ts客户端已写(match/签名/16MB head MD5/24h 缓存),但- 根
.env中OPEN_DANMAKU_APP_ID/APP_SECRET为空 → 运行时恒走「未配置开放网络」降级,零弹幕; - match 请求的
fileName传了含扩展名的完整文件名,官方 swagger 明确要求「不包含文件夹名称和扩展名」,降低识别率; - HTTP 非 200 /
errorCode≠0全部吞成 null,排障无从下手; - match 成功得到的
episodeId不落库,每次播放都重新 16MB 读流 + match。
- 根
- 刮削没有弹弹play 源:只有 Bangumi 文件名搜索;match 响应中的
animeTitle/episodeTitle/imageUrl/animeId元数据被丢弃。
2. 方案选型
| 方案 | 说明 | 结论 |
|---|---|---|
| A. 双源互补(推荐) | dandanplay 做「文件级识别 + 弹幕身份」源,Bangumi 保留做「文件名级刮削」源;识别结果落库复用 | ✅ 采纳 |
| B. 全面换 dandanplay | 扫描期每文件要读 16MB 算 hash,大库不可行;凭据未申请时连展示元数据都没有 | ❌ |
| C. 只修弹幕、刮削维持现状 | 不满足「刮削能力」诉求 | ❌ |
凭据配置:维持 env-only(OPEN_DANMAKU_APP_ID/SECRET/API_BASE),不做 DB 存凭据的设置 UI(YAGNI,单实例自托管);弹幕设置页明示配置状态,消除「静默降级」体感。
3. 官方 API 事实依据(2026-09-26 swagger 实读,api.dandanplay.net/swagger/v2/swagger.json)
- 签名:
X-Signature = base64(sha256(AppId + Timestamp + Path + AppSecret)),头X-AppId/X-Timestamp/X-Signature(与现实现一致)。 POST /api/v2/match:请求{fileName(无扩展名), fileHash(前16MB MD5,可空), fileSize, matchMode(hashAndFileName|fileNameOnly|hashOnly)};响应MatchResponseV2 = ResponseBase + {isMatched, matches: MatchResultV2[]};MatchResultV2 = {episodeId, animeId, animeTitle, episodeTitle, type, typeDescription, shift, imageUrl};ResponseBase = {errorCode, success, errorMessage}。GET /api/v2/comment/{episodeId}?withRelated=true:响应{count, comments: [{cid, p: "time,mode,color,uid", m}]}(与现 XML 转换兼容)。GET /api/v2/bangumi/bgmtv/{bgmtvSubjectId}:Bangumi subjectId → 弹弹play 番剧详情(BangumiDetails.episodes[] = {episodeId, episodeTitle, episodeNumber(短标题可排序), airDate}),免 hash 桥。
4. 设计
4.1 数据模型(@app/models)
media_items 新增一列:
| 字段 | 说明 |
|---|---|
dandanplayEpisodeId |
integer 可空;弹弹play 弹幕库编号(识别成功后落库,弹幕拉取直达) |
matchedHash(原预留列)开始写入:成功 hash 识别时存「前 16MB MD5」。迁移:yarn db:generate + yarn db:push。
4.2 服务层(@app/trpc)
danmaku.service = 开放 API 客户端单点(scrape 复用其导出):
matchFileName(path):去扩展名修fileName(新增,含点文件名边界)。openMatch(fileName, size, hash)→{ok, errorCode, errorMessage, isMatched, candidates: MatchResultV2[]}:非 200/errorCode≠0 透出errorMessage,不再吞 null。openCommentXml(episodeId)→ XML(原逻辑拆出)。remoteFileHash()导出供 scrape 用;openHeaders恒带Content-Type。fetch()流程改为:- 无凭据 →
source:"none"+ 明确文案(含「需配置 OPEN_DANMAKU_APP_ID/SECRET」); item.dandanplayEpisodeId已存在 → 跳过 match 直接 comment;- 否则 hash + match;精确命中(isMatched)时
setDanmakuMatch落库 episodeId+matchedHash(仅身份,不动标题/海报——改标题属刮削语义,由识别入口显式做)。
- 无凭据 →
scrape.service.matchDandanplay(userId, mediaItemId)(新增,手动入口):
- 无凭据 → 报错提示配置;已有
dandanplayEpisodeId且未要求重识别 → 直接返回已识别信息; - hash + match:
isMatched或有候选 → 取首条,写dandanplayEpisodeId/matchedHash/title=animeTitle/posterUrl=imageUrl/scrapeStatus=ok; - match 未中且
bangumiId存在 → bgmtv 桥:pickDandanEpisode(episodes, epNumber)按集数择集,只补dandanplayEpisodeId; - 输出
DanmakuMatchOutput {ok, matched, message?, result?},UI toast 展示。
pickDandanEpisode 为纯函数:episodeNumber 数值相等优先,否则同位序回退(第 N 条)。
4.3 tRPC 路由
media.scrapeDandanMatch(mutation):入参{mediaItemId},调scrapeService.matchDandanplay。media.danmakuGetSettings:响应增加openNetworkConfigured: boolean(其余仍为本地偏移默认值,持久化仍走 localStorage,不在本次范围)。media.danmakuFetch行为如 4.2,签名不变。
4.4 前端(@app/web + @app/i18n)
- 库页匹配弹窗顶部加「弹弹play 文件识别」按钮:调
scrapeDandanMatch,成功 toast 展示animeTitle/episodeTitle,失败透出 message;弹窗仍保留 Bangumi 搜索绑定。 - 弹幕设置页顶部状态条:「开放弹幕网络:已配置 / 未配置(服务端 .env 设置 OPEN_DANMAKU_APP_ID/APP_SECRET 后重启)」。
- i18n:zh-CN 权威,en 同构新增 ~8 key。
4.5 错误处理
| 场景 | 策略 |
|---|---|
| 凭据未配置 | 明确文案 + 设置页状态条;不阻断播放/本地 XML |
| match/comment HTTP 错误或 errorCode≠0 | message 透传 errorMessage;返回 source:"none" |
| hash 读流失败 | 降级 fileNameOnly 匹配 |
| 识别无候选 | matched=false + 文案;Bangumi 已绑走桥接兜底 |
| 桥接无对应集数 | 只提示,不写库 |
4.6 测试与验证
- 纯函数单测(沿用 vitest 模式):
matchFileName(去扩展名/点文件名)、match 响应解析(isMatched/候选/errorCode)、pickDandanEpisode(集数命中/回退)。 - 命令:
yarn workspace @app/trpc test、yarn typecheck、yarn lint、yarn build:web。 - 真实弹幕需官方凭据:设置页状态条 + 文档说明申请入口,凭据就绪后播放页冒烟即可验证。
5. 不做(本次)
弹幕设置服务端持久化 · 弹幕发送(POST comment)· match 候选多选 UI · shift 弹幕偏移应用 · chConvert 简繁参数 · 扫描期批量 hash 识别 · DB 存凭据。
6. 批准记录
用户提出能力缺口后未在线答复澄清(问询超时),按最佳判断采纳方案 A 推进;如与预期不符,数据模型/service 边界已隔离,可低成本调整。