feat: 进度条颗粒度优化 — ASR/翻译按批次细分进度

原来进度只在阶段结束时跳一次(ASR 5%→55%、翻译 60%→98%),长视频时
进度条卡住不动。现在每完成一个批次就更新进度。

ASR(asr_service.py):
- 用 info.duration_after_vad 算总 chunk 数(ceil(时长/30s))
- 消费生成器时按 seg.end 跨 30s chunk 边界回调 on_progress
- 30s 粒度自然节流,长视频约几十次更新

翻译(translate_service.py):
- _translate_sorted / _translate_sequential 每批完成后回调 on_progress(done, total)
- 批数循环前已知(len(batches)),每批都回调

pipeline.py:
- asr_phase / translate_phase 定义闭包回调,把 (current,total) 占比映射到
  对应进度区间(ASR 5%→55%、翻译 60%→98%),调 _set_status 写 DB(DEBUG 级)

验证(test/55.mp4, 640 条字幕):
- ASR: 5%→19.9%→23.8%→33.7%→48.6%→55% 平滑增长 
- 翻译: 20 批,65.3%→68.9%→74.2%→79.6%→84.9%→90.2%→93.8%→98% 
- 翻译卡在 60% 的 ~50s 是模型加载时间(卸载ASR+加载NLLB),属调度器层面
This commit is contained in:
audio2text dev
2026-07-06 23:18:30 +08:00
parent 5f6a242114
commit 78b87bfb24
3 changed files with 69 additions and 15 deletions

View File

@@ -14,6 +14,7 @@ from __future__ import annotations
import logging
import os
from typing import Callable
from ..config import get_settings
from .model_manager import get_model_manager
@@ -25,11 +26,15 @@ logger = logging.getLogger("audio2text.translate")
_TOKENS_PER_WORD = 1.2
def translate(subtitles: list[Subtitle]) -> list[str]:
def translate(
subtitles: list[Subtitle],
on_progress: Callable[[int, int], None] | None = None,
) -> list[str]:
"""批量翻译英文字幕为中文。
Args:
subtitles: 断句后的英文字幕条目(按时间顺序)
on_progress: 可选进度回调 (done_count, total_count),每批完成时调一次。
Returns:
list[str],与 subtitles 等长、顺序对应的中文译文。
@@ -52,9 +57,9 @@ def translate(subtitles: list[Subtitle]) -> list[str]:
sort_by_length = False
if sort_by_length:
results = _translate_sorted(pipe, texts, batch_size, max_len)
results = _translate_sorted(pipe, texts, batch_size, max_len, on_progress)
else:
results = _translate_sequential(pipe, texts, batch_size, max_len)
results = _translate_sequential(pipe, texts, batch_size, max_len, on_progress)
logger.debug("翻译完成:%d 条。", len(results))
return results
@@ -64,6 +69,7 @@ def translate(subtitles: list[Subtitle]) -> list[str]:
def _translate_sorted(
pipe, texts: list[str], batch_size: int, max_len: int,
on_progress: Callable[[int, int], None] | None = None,
) -> list[str]:
"""按长度排序后分批翻译,翻译完按原序散回。
@@ -120,7 +126,9 @@ def _translate_sorted(
for idx, zh in zip(orig_indices, translated):
results[idx] = zh
done += len(batch)
if (done // batch_size + 1) % 5 == 0:
if on_progress is not None:
on_progress(done, n)
elif (done // batch_size + 1) % 5 == 0:
logger.debug("已翻译 %d/%d 条。", done, n)
# None理论不会发生_translate_batch 保证返回等长)→ 回退原文
@@ -142,15 +150,20 @@ def _estimate_sequential_padding(texts: list[str], batch_size: int) -> int:
def _translate_sequential(
pipe, texts: list[str], batch_size: int, max_len: int,
on_progress: Callable[[int, int], None] | None = None,
) -> list[str]:
"""按原序分批翻译(旧行为,便于 A/B 对比)。"""
results: list[str] = []
for i in range(0, len(texts), batch_size):
n = len(texts)
for i in range(0, n, batch_size):
chunk = texts[i:i + batch_size]
translated = _translate_batch(pipe, chunk, max_len)
results.extend(translated)
if (i // batch_size + 1) % 5 == 0:
logger.debug("已翻译 %d/%d 条。", min(i + len(chunk), len(texts)), len(texts))
done = min(i + len(chunk), n)
if on_progress is not None:
on_progress(done, n)
elif (i // batch_size + 1) % 5 == 0:
logger.debug("已翻译 %d/%d 条。", done, n)
return results