Initial commit: audio2text 双语字幕生成服务

- 音频/视频转双语(英/中)SRT 字幕,Docker 容器化,CPU 开发/GPU 生产同一份代码
- faster-whisper ASR(词级时间戳) + 断句时间戳重算 + NLLB 翻译(模型不共驻)
- 分片上传(断点续传) + SQLite 持久化 + 主页/历史/日志页面
- 历史页文件名搜索;缓存定时清理(默认保留7天,可配置)
- 双 Dockerfile(cpu/gpu) + setup/start/stop 脚本
This commit is contained in:
2026-07-06 06:54:19 +00:00
commit 00e2a95fb7
44 changed files with 4110 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
"""翻译服务NLLB-200英译中。
通过 model_manager 加载,确保 ASR 已卸载、翻译器独占显存,从而可用大 batch_size。
按字幕条目批量翻译,保留索引对应。
"""
from __future__ import annotations
import logging
from ..config import get_settings
from .model_manager import get_model_manager
from .types import Subtitle
logger = logging.getLogger("audio2text.translate")
def translate(subtitles: list[Subtitle]) -> list[str]:
"""批量翻译英文字幕为中文。
Args:
subtitles: 断句后的英文字幕条目
Returns:
list[str],与 subtitles 等长、顺序对应的中文译文。
单条翻译失败时该位置回退为原英文。
"""
if not subtitles:
return []
s = get_settings().translation
pipe = get_model_manager().get_translator()
batch = s.batch_size
max_len = s.max_length
# 取纯文本(去掉折行),避免翻译把换行符当语义
texts = [sub.text.replace("\n", " ").strip() for sub in subtitles]
logger.debug("开始翻译 %d 条字幕batch_size=%d...", len(texts), batch)
results: list[str] = []
for i in range(0, len(texts), batch):
chunk = texts[i:i + batch]
try:
out = pipe(chunk, max_length=max_len)
for item in out:
# pipeline 返回 [{"translation_text": "..."}]
results.append(item.get("translation_text", "").strip())
except Exception as exc: # pragma: no cover
logger.warning("%d-%d 批翻译失败,逐条重试:%s", i, i + len(chunk), exc)
for t in chunk:
try:
out = pipe([t], max_length=max_len)
results.append(out[0].get("translation_text", "").strip())
except Exception:
results.append(t) # 回退原文
if (i // batch + 1) % 5 == 0:
logger.debug("已翻译 %d/%d 条。", min(i + len(chunk), len(texts)), len(texts))
logger.debug("翻译完成:%d 条。", len(results))
return results