# 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 |