feat(transcriber): добавлен прогресс транскрипции, исправлена формулировка статуса
- Зачем:
- при длительной транскрипции пользователь видел только спиннер без информации
о ходе обработки; формулировка "Загружаю модель" путала с загрузкой из сети.
- Что:
- спиннер показывает позицию и длительность: "Транскрибирую... 05:32 / 15:52 [87 сегм.]".
- статус инициализации изменён на "Инициализирую модель на cuda/cpu...".
- VAD-фильтр протестирован и отклонён: ухудшает сегментацию для совещаний
(315-321 сегмент вместо 235 без VAD).
- Проверка:
- uv run pytest -v (52 passed, 1 skipped).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,7 @@ def transcribe(
|
|||||||
lang_arg = language if language and language != "auto" else None
|
lang_arg = language if language and language != "auto" else None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_notify_status(on_status, f"Загружаю модель на {device}...")
|
_notify_status(on_status, f"Инициализирую модель на {device}...")
|
||||||
model = _create_model(model_name, device, compute_type)
|
model = _create_model(model_name, device, compute_type)
|
||||||
except (RuntimeError, ValueError) as exc:
|
except (RuntimeError, ValueError) as exc:
|
||||||
if device != "cpu" and _is_cuda_error(exc):
|
if device != "cpu" and _is_cuda_error(exc):
|
||||||
@@ -78,14 +78,14 @@ def transcribe(
|
|||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
actual_device = "cpu"
|
actual_device = "cpu"
|
||||||
_notify_status(on_status, "Загружаю модель на cpu...")
|
_notify_status(on_status, "Инициализирую модель на cpu...")
|
||||||
model = _create_model(model_name, "cpu", compute_type)
|
model = _create_model(model_name, "cpu", compute_type)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_notify_status(on_status, "Транскрибирую...")
|
_notify_status(on_status, "Транскрибирую...")
|
||||||
segments, info = _run_transcription(model, file_path, lang_arg, on_segment)
|
segments, info = _run_transcription(model, file_path, lang_arg, on_segment, on_status)
|
||||||
except (RuntimeError, ValueError) as exc:
|
except (RuntimeError, ValueError) as exc:
|
||||||
if actual_device != "cpu" and _is_cuda_error(exc):
|
if actual_device != "cpu" and _is_cuda_error(exc):
|
||||||
if strict_device:
|
if strict_device:
|
||||||
@@ -96,10 +96,10 @@ def transcribe(
|
|||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
actual_device = "cpu"
|
actual_device = "cpu"
|
||||||
_notify_status(on_status, "Загружаю модель на cpu...")
|
_notify_status(on_status, "Инициализирую модель на cpu...")
|
||||||
model = _create_model(model_name, "cpu", compute_type)
|
model = _create_model(model_name, "cpu", compute_type)
|
||||||
_notify_status(on_status, "Транскрибирую...")
|
_notify_status(on_status, "Транскрибирую...")
|
||||||
segments, info = _run_transcription(model, file_path, lang_arg, on_segment)
|
segments, info = _run_transcription(model, file_path, lang_arg, on_segment, on_status)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -139,15 +139,21 @@ def ensure_model_available(
|
|||||||
return str(downloaded_path)
|
return str(downloaded_path)
|
||||||
|
|
||||||
|
|
||||||
def _run_transcription(model, file_path, lang_arg, on_segment):
|
def _run_transcription(model, file_path, lang_arg, on_segment, on_status=None):
|
||||||
"""Run model.transcribe and iterate segments. Returns (segments, info)."""
|
"""Run model.transcribe and iterate segments. Returns (segments, info)."""
|
||||||
segment_generator, info = model.transcribe(str(file_path), language=lang_arg)
|
segment_generator, info = model.transcribe(str(file_path), language=lang_arg)
|
||||||
|
total_duration = info.duration
|
||||||
segments: list[Segment] = []
|
segments: list[Segment] = []
|
||||||
for raw_seg in segment_generator:
|
for raw_seg in segment_generator:
|
||||||
seg = Segment(start=raw_seg.start, end=raw_seg.end, text=raw_seg.text)
|
seg = Segment(start=raw_seg.start, end=raw_seg.end, text=raw_seg.text)
|
||||||
if on_segment is not None:
|
if on_segment is not None:
|
||||||
on_segment(seg)
|
on_segment(seg)
|
||||||
segments.append(seg)
|
segments.append(seg)
|
||||||
|
_notify_status(
|
||||||
|
on_status,
|
||||||
|
f"Транскрибирую... {_fmt_time(seg.end)} / {_fmt_time(total_duration)}"
|
||||||
|
f" [{len(segments)} сегм.]",
|
||||||
|
)
|
||||||
return segments, info
|
return segments, info
|
||||||
|
|
||||||
|
|
||||||
@@ -174,6 +180,12 @@ def _is_missing_socksio_error(exc: BaseException) -> bool:
|
|||||||
return "socks proxy" in msg and "socksio" in msg
|
return "socks proxy" in msg and "socksio" in msg
|
||||||
|
|
||||||
|
|
||||||
|
def _fmt_time(seconds: float) -> str:
|
||||||
|
m, s = divmod(int(seconds), 60)
|
||||||
|
h, m = divmod(m, 60)
|
||||||
|
return f"{h}:{m:02d}:{s:02d}" if h else f"{m:02d}:{s:02d}"
|
||||||
|
|
||||||
|
|
||||||
def _notify_status(on_status: Callable[[str], None] | None, message: str) -> None:
|
def _notify_status(on_status: Callable[[str], None] | None, message: str) -> None:
|
||||||
if on_status is not None:
|
if on_status is not None:
|
||||||
on_status(message)
|
on_status(message)
|
||||||
|
|||||||
@@ -247,8 +247,9 @@ def test_transcribe_reports_status_transitions(mock_model_cls):
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert statuses == [
|
assert statuses == [
|
||||||
"Загружаю модель на cpu...",
|
"Инициализирую модель на cpu...",
|
||||||
"Транскрибирую...",
|
"Транскрибирую...",
|
||||||
|
"Транскрибирую... 00:04 / 01:00 [1 сегм.]",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user