From a9a44b8d3a140e80a15f958464047d811fe076a4 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Wed, 18 Mar 2026 19:14:08 +0300 Subject: [PATCH] =?UTF-8?q?feat(transcriber):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D1=81=20=D1=82=D1=80=D0=B0=D0=BD=D1=81=D0=BA=D1=80=D0=B8?= =?UTF-8?q?=D0=BF=D1=86=D0=B8=D0=B8,=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D0=BE=D1=80=D0=BC=D1=83?= =?UTF-8?q?=D0=BB=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D1=83=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - при длительной транскрипции пользователь видел только спиннер без информации о ходе обработки; формулировка "Загружаю модель" путала с загрузкой из сети. - Что: - спиннер показывает позицию и длительность: "Транскрибирую... 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 --- src/local_transcriber/transcriber.py | 24 ++++++++++++++++++------ tests/test_transcriber.py | 3 ++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/local_transcriber/transcriber.py b/src/local_transcriber/transcriber.py index da7e7a0..0f55d82 100644 --- a/src/local_transcriber/transcriber.py +++ b/src/local_transcriber/transcriber.py @@ -66,7 +66,7 @@ def transcribe( lang_arg = language if language and language != "auto" else None try: - _notify_status(on_status, f"Загружаю модель на {device}...") + _notify_status(on_status, f"Инициализирую модель на {device}...") model = _create_model(model_name, device, compute_type) except (RuntimeError, ValueError) as exc: if device != "cpu" and _is_cuda_error(exc): @@ -78,14 +78,14 @@ def transcribe( stacklevel=2, ) actual_device = "cpu" - _notify_status(on_status, "Загружаю модель на cpu...") + _notify_status(on_status, "Инициализирую модель на cpu...") model = _create_model(model_name, "cpu", compute_type) else: raise try: _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: if actual_device != "cpu" and _is_cuda_error(exc): if strict_device: @@ -96,10 +96,10 @@ def transcribe( stacklevel=2, ) actual_device = "cpu" - _notify_status(on_status, "Загружаю модель на cpu...") + _notify_status(on_status, "Инициализирую модель на cpu...") model = _create_model(model_name, "cpu", compute_type) _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: raise @@ -139,15 +139,21 @@ def ensure_model_available( 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).""" segment_generator, info = model.transcribe(str(file_path), language=lang_arg) + total_duration = info.duration segments: list[Segment] = [] for raw_seg in segment_generator: seg = Segment(start=raw_seg.start, end=raw_seg.end, text=raw_seg.text) if on_segment is not None: on_segment(seg) segments.append(seg) + _notify_status( + on_status, + f"Транскрибирую... {_fmt_time(seg.end)} / {_fmt_time(total_duration)}" + f" [{len(segments)} сегм.]", + ) return segments, info @@ -174,6 +180,12 @@ def _is_missing_socksio_error(exc: BaseException) -> bool: 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: if on_status is not None: on_status(message) diff --git a/tests/test_transcriber.py b/tests/test_transcriber.py index a23d3eb..3ab7bdd 100644 --- a/tests/test_transcriber.py +++ b/tests/test_transcriber.py @@ -247,8 +247,9 @@ def test_transcribe_reports_status_transitions(mock_model_cls): ) assert statuses == [ - "Загружаю модель на cpu...", + "Инициализирую модель на cpu...", "Транскрибирую...", + "Транскрибирую... 00:04 / 01:00 [1 сегм.]", ]