docs: Web 版弹弹play 设计规格(方案 A + Bangumi 刮削)
This commit is contained in:
parent
a5ba7124dc
commit
ddc89858af
|
|
@ -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 |
|
||||||
Loading…
Reference in New Issue