Metadata-Version: 2.4
Name: it-healer-transcribe
Version: 0.1.0
Summary: Локальная транскрибация видео/аудио с определением говорящих (mlx-whisper + sherpa-onnx), без отправки данных наружу
Author: IT Healer
License: MIT
Project-URL: Homepage, https://it-healer.com
Project-URL: Telegram, https://t.me/biodynamist
Keywords: whisper,transcription,diarization,mlx,speech-to-text,subtitles,srt
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mlx-whisper
Requires-Dist: sherpa-onnx
Requires-Dist: soundfile
Dynamic: license-file

# it-healer-transcribe — транскрибация видео/аудио локальным ИИ

Скрипт рекурсивно проходит по папке с видео/аудио (или принимает один файл),
извлекает аудиодорожку (ffmpeg) и транскрибирует её локально через
[mlx-whisper](https://github.com/ml-explore/mlx-examples/tree/main/whisper)
(без отправки данных куда-либо наружу). По умолчанию побайтово одинаковые
дубликаты обрабатываются один раз; результат — обычный текст или субтитры
с таймкодами (.srt) — на выбор. По умолчанию также определяются говорящие
(диаризация) — реплики размечаются как «Спикер 1» / «Спикер 2» и т.д.

## Требования

- macOS на Apple Silicon (использует MLX/Metal-ускорение)
- Установленный `ffmpeg` (`brew install ffmpeg`)
- Python 3

## Установка

Проще всего — из PyPI:

```bash
pip install it-healer-transcribe
```

Появится команда `it-healer-transcribe` в PATH. Дальше в примерах ниже
`.venv/bin/python3 transcribe.py` можно заменить на `it-healer-transcribe`.

### Установка из исходников (для разработки)

Выполняется в папке со скриптом:

```bash
python3 -m venv .venv
.venv/bin/pip install -e .
```

`sherpa-onnx` и `soundfile` (нужны только для определения говорящих —
диаризации, включена по умолчанию) ставятся автоматически вместе с пакетом.
Если диаризация не нужна, всегда можно запускать с `--no-diarize`.

## Запуск

```bash
.venv/bin/python3 transcribe.py <источник> <папка_для_результата>
# или, если пакет установлен из PyPI:
it-healer-transcribe <источник> <папка_для_результата>
```

- **`<источник>`** — либо папка с видео/аудио (обходится **рекурсивно**, вместе со всеми вложенными подпапками), либо путь к одному файлу.
- **`<папка_для_результата>`** — куда складывать аудио и транскрипты. Может ещё не существовать — создастся автоматически.

Пример на папку:

```bash
.venv/bin/python3 transcribe.py ~/Movies/RawFootage ~/Movies/Transcribed
```

Пример на один файл:

```bash
.venv/bin/python3 transcribe.py ~/Documents/запись.wav ~/Downloads/Практикум
```

Структура результата (для папки-источника повторяет её вложенность; для одного файла — просто один файл в `audio/` и один в `transcripts/`):

```
~/Movies/Transcribed/
  audio/
    trip2024/IMG_0001.wav
    trip2024/IMG_0002.wav
  transcripts/
    trip2024/IMG_0001.txt
    trip2024/IMG_0002.txt
```

Пример содержимого `transcripts/.../IMG_0001.txt` (с диаризацией, включена по умолчанию):

```
Спикер 1: Как вы себя чувствуете сегодня?

Спикер 2: В целом неплохо, но есть напряжение в плечах.

Спикер 1: Давайте с этим поработаем.
```

Поддерживаемые форматы:
- видео: `.mp4 .mov .mkv .avi .m4v .wmv .flv .webm`
- аудио: `.wav .mp3 .m4a .aac .flac .ogg`

## Дополнительные параметры

```bash
.venv/bin/python3 transcribe.py <источник> <результат> --language en --model mlx-community/whisper-medium-mlx --format srt --no-dedupe --speakers 3
```

- `--language` — язык речи (по умолчанию `ru`). Явное указание языка точнее и быстрее автоопределения.
- `--model` — модель whisper с Hugging Face (по умолчанию `mlx-community/whisper-small-mlx`). Варианты по возрастанию точности и требований к памяти/времени: `whisper-tiny-mlx`, `whisper-base-mlx`, `whisper-small-mlx`, `whisper-medium-mlx`, `whisper-large-v3-mlx` (полное имя репозитория — `mlx-community/<название>`).
- `--format` — формат результата: `txt` (сплошной текст, по умолчанию) или `srt` (субтитры с таймкодами, совместимые с плеерами и видеоредакторами).
- `--no-dedupe` — не проверять файлы на побайтовые дубликаты (пропускает хеширование). Полезно, когда источник — сетевой/внешний диск и подсчёт хешей заметно всё замедляет, либо когда заведомо известно, что дублей нет.
- `--no-diarize` — отключить определение говорящих и вернуть старое поведение: сплошной текст/субтитры без меток «Спикер N». Полезно, если в записи один говорящий, или не установлены `sherpa-onnx`/`soundfile`.
- `--speakers N` — ожидаемое число говорящих (по умолчанию `2`, т.к. это самый частый случай — ведущий/терапевт + клиент). Укажите точное число, если оно другое, либо `-1` для автоопределения (менее надёжно).

При первом использовании новой модели она скачивается с Hugging Face (нужен интернет один раз, дальше берётся из кеша).

## Определение говорящих (диаризация)

Включено по умолчанию. При первом запуске (без `--no-diarize`) скрипт
скачивает две небольшие ONNX-модели проекта
[sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx) — сегментацию речи и
голосовые эмбеддинги — в `~/.cache/transcribe-diarization/`. Это открытый
проект **без torch и без Hugging Face аккаунта/токена** — модели лежат
обычными файлами на GitHub Releases.

Как это работает: whisper-сегменты сопоставляются со временными интервалами
речи из диаризации (по максимальному перекрытию), и соседние сегменты одного
говорящего объединяются в абзац с меткой `Спикер N:` (для `.srt` метка
`[Спикер N]` добавляется в начало каждой субтитровой реплики, тайминги не
меняются).

Ограничения:
- Метки «Спикер 1» / «Спикер 2» **не связаны между разными файлами** — это отдельная нумерация в каждом файле, не постоянная идентичность одного и того же человека. Определение конкретного человека по имени/голосу — отдельная, значительно более сложная задача (голосовые профили), в скрипте не реализована.
- Диаризация занимает дополнительное время на файл (иногда сравнимое с самой транскрибацией или больше).
- Если реальное число говорящих отличается от `--speakers`, качество разделения падает — подбирайте значение под конкретную запись.

## Повторный запуск / докидывание новых файлов

Скрипт можно безопасно запускать повторно на той же паре папок:
- уже готовые транскрипты (`transcripts/...`) не перезаписываются — такие файлы пропускаются;
- если аудио уже извлечено (`audio/...`), но транскрипт не готов, повторное извлечение аудио пропускается;
- новые/недостающие файлы обрабатываются, остальные не трогаются.

Это удобно, если в исходную папку со временем добавляются новые видео/аудио —
достаточно перезапустить ту же команду.

## Дубликаты и ошибки

- По умолчанию, если несколько файлов в разных подпапках побайтово идентичны — обрабатывается только один, остальные логируются как пропущенные дубликаты. Отключается через `--no-dedupe`.
- Пустые/битые файлы (например, 0 байт) не считаются дубликатами «на глазок» — они честно пытаются обработаться и логируются как ошибка, если ffmpeg не может их прочитать. Обработка остальных файлов при этом продолжается.
- В конце работы выводится итог: сколько обработано, сколько дублей пропущено, сколько ошибок (с расшифровкой).

## Запуск в фоне на много часов

Для больших папок (десятки/сотни гигабайт видео) обработка может занимать
много часов. Запускайте через `nohup`, чтобы процесс продолжался и после
закрытия терминала:

```bash
nohup .venv/bin/python3 transcribe.py <источник> <результат> > <результат>/transcribe.log 2>&1 &
```

Прогресс — по количеству файлов в `<результат>/transcripts/` и по логу
(`tail -f <результат>/transcribe.log`).

## Лицензия

MIT — см. файл [LICENSE](LICENSE).

## Автор

**IT Healer** — [it-healer.com](https://it-healer.com) · Telegram: [@biodynamist](https://t.me/biodynamist)
