docs: Web 版弹弹play 设计规格(方案 A + Bangumi 刮削)

This commit is contained in:
noelorin 2026-09-25 01:44:43 +08:00
parent a5ba7124dc
commit ddc89858af
1 changed files with 219 additions and 0 deletions

View File

@ -0,0 +1,219 @@
# Web 版弹弹play(媒体中心)— 设计规格
- **日期**:2026-09-25
- **状态**:已获设计批准,待实现计划(writing-plans)
- **仓库**:`app-template`(按**产品仓**维护,业务进入现有分层)
## 1. 背景与调研结论
弹弹play 官方定位是**本地视频 + 在线弹幕**的全功能播放器(非音乐播放器),能力包括智能识别、媒体库、AI 字幕、远程访问(浏览器可连 PC 内嵌 API)、媒体库可挂 SMB/WebDAV/Emby 等。
本项目要做的是:**独立 Web 应用**(方案 A),提供类似的「挂载源 → 浏览 → 播放 + 弹幕 → 媒体库/进度」体验,**不依赖**本机运行的 PC 弹弹play。
| 能力 | 决策 |
|------|------|
| 范围 | A — 独立完整 Web 应用 |
| 播放器 | ArtPlayer |
| 弹幕 | 对接弹弹play **开放弹幕网络**(服务端中继)+ 本地 XML 导入兜底 |
| 存储源 | **仅通用 WebDAV**(OpenList 使用其 WebDAV 端点;不做 OpenList 原生 API) |
| 后端 | 挂载代理 + 弹幕中继 + 媒体库/进度 **全做** |
| 刮削 | **Bangumi**:扫描自动匹配 + 手动搜索改绑(C) |
| UI | 全页面:播放 / 文件浏览 / 媒体库 / 挂载管理 / 弹幕设置 |
| 账号 | **多用户**:注册登录;每人独立库与进度 |
| 架构方案 | **A — 紧凑垂直切片 `media`**(复制 `user` 切片模式) |
| 视觉 | **shadcn/ui Dashboard** 风格(侧栏 + 卡片 + 中性灰阶),不以模板深空工业 HUD 为产品主视觉 |
| 落点 | **B — 产品化本仓**:业务进入 `app/web`、`packages/*` |
### 调研来源(Browser Use 实读)
- https://www.dandanplay.com/ — 产品能力与远程访问/WebDAV 叙述
- https://doc.dandanplay.com/open/library-api.html — PC 远程访问 REST(本项目**不**对接;仅作产品对照)
- https://doc.dandanplay.com/function/media-library.html — 媒体库信息架构参考
- https://github.com/OpenListTeam/OpenList — AList fork;本项目只用 **WebDAV** 接入方式
> 说明:会话内 IAB 不可用,调研经 `cua_repl` + `managed-chromium` 完成 SERP 与正文阅读。
## 2. 系统架构(方案 A)
```
浏览器 @app/web(React islands,shadcn dashboard)
├─ 登录 / 媒体库 / 文件浏览 / 播放(ArtPlayer+弹幕) / 挂载 / 弹幕设置
└─ 仅经 tRPC 与 /api/stream(Range)访问
│
▼
服务层 trpc + service + dao(@app/trpc · dao)
├─ mount.* WebDAV 列目录、凭据加密、测试连接
├─ stream Range 拉流代理
├─ danmaku.* 开放弹幕网络中继 + 缓存
├─ scrape.* Bangumi 搜索/绑定;扫描内自动刮削
├─ library.* 扫描入库、列表、海报
└─ playback.* 进度节流写入
│
┌─────┴──────┐
▼ ▼
Drizzle/Turso 外部:WebDAV · 弹弹play 开放弹幕网络 · Bangumi API
```
**分层约束(沿用仓库 AGENTS)**
- Schema 源:`@app/models`;查询:`@app/dao`;连接:`@app/db`
- 业务逻辑:`@app/trpc` service(调 dao);入参 Zod:`@app/types`
- UI:`@app/ui` + design tokens;页面在 `@app/web`
- **产品化本仓**:新增业务实体照 `user` 垂直切片扩展(本产品期间业务代码进入现有分层,视为对「模板不掺业务」的产品化豁免)
### 播放主路径
1. 登录 → 2. 配置 WebDAV 挂载 → 3. 浏览目录或扫描入库
4. 打开视频 → stream 代理 → ArtPlayer
5. 识别/拉弹幕(中继 + `danmaku_cache`)→ 弹幕层
6. 节流上报 `playback_progress`
### 首版不做
OpenList 原生 API · 本机弹弹play 远程 API 对接 · BT/RSS · 多刮削源 · NFO 优先 · 公网多租户硬化(SSO/套餐限流)
## 3. 数据模型
### users(已有 auth)
沿用模板用户表与会话,不重复造账号。
### mounts
| 字段 | 说明 |
|------|------|
| id | PK |
| userId | → users,级联隔离 |
| name | 展示名 |
| type | 固定 `webdav`(预留枚举) |
| baseUrl | WebDAV 根 URL(含 OpenList WebDAV 地址) |
| username | 可选 |
| secretEnc | **服务端加密**密文;API 永不回传明文 |
| rootPath | 根路径前缀 |
| enabled | 启用 |
### media_items
| 字段 | 说明 |
|------|------|
| id | PK |
| userId | 所属用户 |
| mountId | 可空(扫描所得) |
| path | 挂载内路径;`userId+path` 唯一 |
| rawName | 原始文件名 |
| title | 展示标题(可被刮削覆盖) |
| size / mime | 文件元数据 |
| bangumiId? | 刮削绑定 |
| epNumber? | 集数启发式 |
| scrapeStatus | `pending` \| `ok` \| `failed` \| `unmatched` |
| scrapedAt? | 最近刮削时间 |
| posterUrl? | 海报(Bangumi CDN 或占位) |
| matchedHash? | 弹幕匹配指纹(预留) |
| scannedAt | 入库时间 |
### playback_progress
`id · userId · mediaItemId · positionMs · durationMs · updatedAt`
唯一:`(userId, mediaItemId)`。
### danmaku_cache(全局,不绑 user)
`id · matchKey · payload/xml · source · expiresAt`
### bangumi_cache(可选,限流)
`bangumiId · payload JSON · expiresAt`
### Zod(@app/types)
- `mountCreateSchema` / `mountUpdateSchema`
- `scanMountSchema` · `playbackReportSchema`
- `danmakuQuerySchema` · `danmakuImportMetaSchema`
- `scrapeSearchSchema` · `scrapeBindSchema`
## 4. 页面与交互
### 路由(shadcn dashboard 侧栏)
| 路径 | 页面 |
|------|------|
| `/login` `/register` | 认证(登录前仅此可见) |
| `/library` | 媒体库(默认登录后):海报卡、筛选全部/未匹配、继续播放、扫描入口 |
| `/browse` | 文件浏览:选挂载 → 面包屑目录 → 行内播放/加入库 |
| `/watch` | 播放:ArtPlayer + 弹幕层 + 同目录剧集(可选)+ 弹幕开关/导入 XML |
| `/mounts` | 挂载管理:CRUD、测试连接、按挂载扫描 |
| `/danmaku` | 弹幕设置:默认开关/透明度/密度;导入说明 |
### 主流程
1. 注册/登录
2. 挂载管理添加 WebDAV → 测试 → 保存
3. 文件浏览点选播放,或「扫描」→ 刮削管线 → 媒体库
4. `/watch`:stream → 弹幕中继 → 进度保存
5. 媒体库「继续播放」带 progress 打开 `/watch`
6. 未匹配项手动 Bangumi 搜索绑定;详情可重新刮削
### 刮削管线(扫描)
1. 列目录,视频入 `media_items`
2. 文件名启发式 → Bangumi 搜索
3. 命中 → 写元数据/海报/`bangumiId`
4. 低置信 → `unmatched`,UI 手动搜绑
5. 重复扫描按指纹/缓存,避免打爆 Bangumi;**刮削失败不阻断入库**
### 视觉
- shadcn 风格组件语义:侧栏 nav、卡片、primary 按钮、Sheet 挂载表单、Dialog 确认、Toast
- 可基于 `@app/ui` 对齐 shadcn API,产品主题为中性暗色 dashboard(非工业信号黄主视觉)
- 动效克制,尊重 `prefers-reduced-motion`
## 5. 错误处理
| 场景 | 策略 |
|------|------|
| WebDAV 401/超时 | 明确文案;浏览 skeleton + 重试;引导改凭据 |
| stream 失败 | 播放器错误 + Toast;不暴露栈 |
| 弹幕网络失败 | 降级无弹幕,可播;可仅本地 XML |
| Bangumi 失败 | `scrapeStatus=failed`,可重试;不回滚文件 |
| 进度失败 | 静默重试,不打断播放 |
| 权限 | 所有 library/mount/progress 查询强制 `userId` |
## 6. 测试与验证
- types:Zod 单测
- dao/service:进度 upsert、刮削绑定、路径规范化(mock HTTP)
- tRPC:跨用户越权
- web:主路径冒烟(登录→列表→播放页);无 CI 浏览器则手动清单
- 命令:`yarn typecheck` · `yarn lint` · `yarn build:web`
- 不做重型视觉回归
## 7. 实现范围(一个计划可覆盖)
**In scope**
- media 垂直切片全栈(mount/stream/danmaku/scrape/library/playback)
- 六个 UI 面(含登录沿用)
- ArtPlayer 集成与弹幕层(开放网络 + XML 导入)
- Bangumi 自动/手动刮削
- shadcn dashboard 信息架构与主题
**Out of scope(本规格)**
见 §2「首版不做」及 §1 调研中未选路径。
## 8. 风险与开放问题(实现前可再核)
1. **弹弹play 开放弹幕网络** 端点、鉴权与条款:实现时以官方文档/实测为准,service 单点适配
2. **Bangumi API** 限流与字段:实现时核对官方 API 文档
3. **WebDAV 流**:Range、HTTPS 混合内容、大文件内存——stream 用流式管道,禁止整文件缓冲
4. 艺术墙纸/版权:用户自备片源与挂载凭据
## 9. 批准记录
| 节 | 结果 |
|----|------|
| 架构 + shadcn dashboard | OK |
| 数据模型 | OK |
| 页面与交互 + Bangumi 刮削 C | OK |
| 错误/测试/边界 | OK |
| 方案选型 | A |