- 选择文件夹/单文件 → SenseVoice 转录 → LLM 修正 → 多路线翻译 → 双语 SRT - 路线:直译中文 / 英转中 / 仅转录 - 并发流水线:转录(可调) + LLM 并发(可调),降噪拆锁并发 - 断点续跑:.subtitle-work/ 中间产物,三阶段独立续跑 + 翻译段级续跑 - 去重:history 跟随视频文件夹,防重复任务保护 - 实时进度:SSE 推送 + 耗时显示 + 子任务状态 + 重新生成按钮 - 时间戳调优:VAD silence_schedule + noisereduce 降噪 + 完整性校验
125 lines
6.2 KiB
Markdown
125 lines
6.2 KiB
Markdown
# 字幕生成系统 subtitle-studio — 架构设计
|
||
|
||
> 设计日期:2026-08-15 | 架构:纯 Windows 本地,自包含 | 目标:文件夹/单文件 → 多路线字幕
|
||
|
||
## 1. 总体架构
|
||
|
||
```
|
||
┌─────────────────────────── Windows 本机 ───────────────────────────┐
|
||
│ │
|
||
│ [Web 前端] 浏览器页面 │
|
||
│ - 选择文件夹 / 单个文件 │
|
||
│ - 选择翻译路线(直译 / 英转中 / 仅转录) │
|
||
│ - 实时查看任务进度(SSE 推送) │
|
||
│ - 已完成视频列表 + 字幕文件下载/打开 │
|
||
│ │ HTTP/SSE │
|
||
│ [FastAPI 后端] :8788 │
|
||
│ - 任务管理器(队列 + 并发控制,默认 1 任务串行防 GPU 争抢) │
|
||
│ - 进度状态机:pending → transcribing → fixing → translating → srt → done │
|
||
│ │ │
|
||
│ [管道核心] subsudio.pipeline │
|
||
│ - transcribe.py SenseVoice + fsmn-vad(GPU,16kHz wav) │
|
||
│ - fix.py LLM 分块并发修正日文/韩文转录 │
|
||
│ - translate.py LLM 全文翻译(路线1 直译 / 路线2 英转中) │
|
||
│ - srt.py 生成 .ja.zh.srt / .zh.srt / .zh.en.srt │
|
||
│ │ │
|
||
│ [运行时] py312_cuda(conda) + OCG Router (192.168.1.246:19878) │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 2. 设计决策
|
||
|
||
### D1. 复用 py312_cuda 环境,不新建 venv
|
||
- 转录需要 faster-whisper/funasr/torch-CUDA,已全部就绪于 `D:\ProgramData\anaconda3\envs\py312_cuda`
|
||
- 后端 FastAPI/uvicorn 也装这个环境(检查缺包再补)
|
||
- 避免重复装 CUDA 依赖(虚拟环境铁律:不污染系统,但可复用专用环境)
|
||
|
||
### D2. LLM 走 OCG Router(本地中转,自动选 key)
|
||
- base_url `http://192.168.1.246:19878/v1`,apiKey `ocg-router-local`
|
||
- 模型 `deepseek-v4-flash`(1M 上下文,全文一次请求)
|
||
- 关键经验:**不指定 max_tokens**(推理模型会烧光截断);全文翻译 1746 段 105 秒
|
||
|
||
### D3. 翻译路线(用户可选)
|
||
| 路线 | 流程 | 产物 |
|
||
|------|------|------|
|
||
| direct (默认) | 转录 → LLM修正 → 直译中文 | .ja.zh.srt + .zh.srt |
|
||
| via_en | 转录 → LLM修正 → 英转中 | .r2.zh.en.srt + .r2.zh.srt |
|
||
| transcribe_only | 只转录,不翻译 | _transcript.json |
|
||
|
||
### D4. 任务队列串行执行
|
||
- GPU 转录是重资源操作,一次只跑 1 个任务
|
||
- 翻译阶段可并发分块(fix 用 4 worker)
|
||
- 进度通过 SSE 实时推送到前端
|
||
|
||
### D5. 输出目录
|
||
- 默认:字幕文件生成到**视频同目录**(PotPlayer 自动加载)
|
||
- 同时复制一份到 `output/<task_id>/` 便于管理/下载
|
||
|
||
## 3. 目录布局
|
||
|
||
```
|
||
subtitle-studio/
|
||
├── src/substudio/
|
||
│ ├── __init__.py
|
||
│ ├── main.py # FastAPI 入口(uvicorn)
|
||
│ ├── taskmanager.py # 任务队列 + 状态机 + SSE 广播
|
||
│ ├── pipeline/
|
||
│ │ ├── __init__.py
|
||
│ │ ├── transcribe.py # SenseVoice 转录(复用技能逻辑)
|
||
│ │ ├── fix.py # LLM 分块并发修正
|
||
│ │ ├── translate.py # LLM 全文翻译(路线选择)
|
||
│ │ └── srt.py # SRT 生成
|
||
│ ├── config.py # 路径/模型/API 配置
|
||
│ └── llm.py # OCG Router 客户端(流式兼容)
|
||
├── templates/index.html # 前端页面
|
||
├── static/app.js # 前端逻辑
|
||
├── output/ # 任务输出
|
||
├── scripts/start.bat # 启动脚本
|
||
└── README.md
|
||
```
|
||
|
||
## 4. 进度状态机
|
||
|
||
```
|
||
pending → transcribing → fixing → translating → srt → done
|
||
└────── error(失败即停,可重试)
|
||
```
|
||
|
||
每阶段有 `progress`(0-100)和 `message`(如 "转录中 320/1746 段")
|
||
SSE 事件:`task_update` 推送 `{task_id, status, progress, message}`
|
||
|
||
## 5. 关键技术点
|
||
|
||
### 5.1 转录(复用已验证逻辑)
|
||
- ffmpeg 提取 16kHz 单声道 wav
|
||
- **降噪(默认开启)**:noisereduce 频谱门控,prop_decrease=0.7(0.9 过度会字间隙),实测 RMS 降 64%,句子边界更清晰
|
||
- **fsmn-vad 切段(时间戳精准关键)**:
|
||
- `max_single_segment_time` 参数**无效**(实测)
|
||
- 必须传自定义 `silence_schedule`:`[(8000,500),(12000,300),(20000,200),(inf,100)]`
|
||
- 效果:段长从默认 15.67s → 4.64s 上限,平均 1.59s,时间戳严重错位修复
|
||
- 根因:默认 schedule 产生长段 → 只能按字符比例估算句内时间 → 字幕与说话时间对不上
|
||
- SenseVoiceSmall 逐段转录,rich_transcription_postprocess 清理
|
||
- 输出句子级时间戳 JSON
|
||
|
||
### 5.2 修正(分块并发)
|
||
- 按字符量分块(块边界=段边界,不切断句子)
|
||
- 每块一个 worker 进程并发(4 worker)
|
||
- prompt:结合上下文修正 ASR 错误,保留 [idx]
|
||
|
||
### 5.3 翻译(全文一次请求)
|
||
- **不指定 max_tokens**(1M 上下文)
|
||
- 全文 30K 字符一次请求,1746 段 105 秒
|
||
- 输出 [idx] 行式解析
|
||
|
||
### 5.4 OCG Router 兼容
|
||
- 兼容 SSE 流式返回(data: {...} 解析)
|
||
- 有限重试(3 次退避,不疯狂重试)
|
||
- 失败即停 + 进度保存(断点续传)
|
||
|
||
## 6. 安全与资源
|
||
|
||
- 无 Docker 操作,纯本地文件处理
|
||
- 只读视频,写入同目录 SRT + output/
|
||
- 一次一个任务(GPU 转录),翻译并发仅限 LLM API
|
||
- 不碰系统环境(复用 py312_cuda 专用环境)
|