feat(utils): реализованы проверки окружения (шаг 2)

- Зачем:
  - необходимы утилиты для проверки ffmpeg, определения устройства и
    валидации входного файла перед запуском транскрипции.
- Что:
  - check_ffmpeg() завершает процесс с понятным сообщением, если ffmpeg не в PATH.
  - detect_device() возвращает "cuda" при наличии nvidia-smi, иначе "cpu".
  - get_gpu_name() получает имя GPU через nvidia-smi или возвращает None.
  - validate_input_file() проверяет существование, тип и размер файла;
    неизвестное расширение — warning, не ошибка.
  - build_output_path() формирует путь <stem>-transcript.md рядом с исходником.
- Проверка:
  - uv run pytest tests/test_utils.py -v — 9 passed.
  - uv run python -c "from local_transcriber.utils import check_ffmpeg, detect_device; check_ffmpeg(); print(detect_device())"
This commit is contained in:
2026-03-17 21:51:22 +03:00
parent ade3b23301
commit 510d6ccfc9
3 changed files with 132 additions and 11 deletions
+52 -5
View File
@@ -1,21 +1,68 @@
import shutil
import subprocess
import sys
import warnings
from pathlib import Path
SUPPORTED_EXTENSIONS = {
".mp3", ".wav", ".flac", ".ogg", ".m4a", ".wma", ".aac",
".mp4", ".mkv", ".avi", ".mov", ".webm", ".ts",
}
def check_ffmpeg() -> None:
raise NotImplementedError
try:
subprocess.run(["ffmpeg", "-version"], capture_output=True, check=False)
except FileNotFoundError:
sys.exit(
"ffmpeg не найден в PATH. Установите ffmpeg:\n"
" Linux: apt install ffmpeg\n"
" Windows: winget install ffmpeg\n"
" macOS: brew install ffmpeg"
)
def detect_device(requested: str = "auto") -> str:
raise NotImplementedError
if requested != "auto":
return requested
if shutil.which("nvidia-smi") is not None:
return "cuda"
return "cpu"
def get_gpu_name() -> str | None:
raise NotImplementedError
try:
result = subprocess.run(
["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0:
name = result.stdout.strip().splitlines()[0].strip()
return name if name else None
except FileNotFoundError:
pass
return None
def validate_input_file(path: Path) -> Path:
raise NotImplementedError
if not path.exists():
raise FileNotFoundError(f"Файл не найден: {path}")
if not path.is_file():
raise ValueError(f"Путь не является файлом: {path}")
if path.stat().st_size == 0:
raise ValueError(f"Файл пустой: {path}")
if path.suffix.lower() not in SUPPORTED_EXTENSIONS:
warnings.warn(
f"Расширение '{path.suffix}' не входит в список поддерживаемых. "
"Попытка продолжить.",
stacklevel=2,
)
return path.resolve()
def build_output_path(input_path: Path, output: Path | None = None) -> Path:
raise NotImplementedError
if output is not None:
return output
return input_path.with_stem(input_path.stem + "-transcript").with_suffix(".md")