系统架构
MemShare 采用移动端 + API 中心 + 专用语音服务 + 异步 Worker 的分层架构。用户请求走同步 HTTP;耗时转写走异步队列(基于数据库状态轮询)。
架构总览
分层说明
客户端(native)
- 面向 iOS / Android,使用 Expo Router 组织页面
- 通过
better-auth与 API 完成登录与会话管理 - 本地处理:视频选片、封面生成、音频提取(
@memshare/expo-media-processor-module)、文件指纹计算 - 播放层支持本地文件与 YouTube(WebView),字幕与播放进度同步
API 网关(api)
- 统一入口,默认监听
:8080 - 路由前缀
/api,OpenAPI 文档位于/api/doc,Swagger UI 位于/api/ui - 职责:
- 用户鉴权(
/api/auth/*) - 媒体库 CRUD(
/api/media) - 字幕分页读取(
/api/media/{id}/subtitles/{language}) - Worker 专用接口(
/api/worker/*,X-Worker-Key鉴权)
- 用户鉴权(
- 持久化:PostgreSQL(Prisma ORM)
- 对象存储:Cloudflare R2 或兼容 S3 的 MinIO
语音服务(speech)
- 独立 Python 服务,默认监听
:8000 - 无状态:接收音频文件,通过 SSE 流式返回转写与分析结果
- 不直接访问数据库;由 Worker 消费 SSE 并回写 API
任务处理器(worker)
- 长轮询 API 的
/api/worker/jobs/next - 两类任务:
youtube_download(下载音频)与transcribe(转写) - 转写时调用 Speech SSE,每收到一段即
POST回 API 增量写入 - 与 API、Speech 解耦,可水平扩展多个 Worker 实例(任务认领通过数据库乐观锁)
数据流原则
| 路径 | 模式 | 说明 |
|---|---|---|
| 用户 → API | 同步 REST | 登录、列表、详情、上传预签名 |
| 用户 → R2 | 直传 | 音频不经 API 中转,降低带宽压力 |
| Worker → Speech | SSE 长连接 | 转写可能持续数分钟,流式消费 |
| Worker → API | REST 回调 | 状态更新、字幕片段追加 |
| App → API | 轮询 | 处理中的媒体每 5s 拉取字幕 |
去重与共享
系统维护两层媒体概念:
Media:全局媒体库,按fileHash(本地文件)或youtubeVideoId(YouTube)去重UserMedia:用户个人收藏,可覆盖标题、打标签
字幕(Subtitle)按 fileHash 关联。当新用户添加已有指纹的媒体时,attachSubtitlesByFileHash 会自动挂载历史字幕并将 subtitleStatus 置为 success,跳过 Worker 队列。
安全边界
- 用户接口:Session / Cookie,经
requireAuth中间件保护 - Worker 接口:共享密钥
WORKER_API_KEY,请求头X-Worker-Key - Speech 服务:内网部署,不对外暴露;仅 Worker 调用
- R2 访问:预签名 URL,限时读写