Антон Малявский Антон Малявский

Локальная транскрипция подкаста через whisper.cpp

4 марта 2026 г.

Что это?  Ссылка на этот раздел

Локальный инструмент для транскрипции аудиофайлов через whisper.cpp. Устанавливается на MacOS через Homebrew и добавляется как команда voice в ~/bin.

Идея простая: в терминале набираю voice и путь к одному или нескольким файлам, нажимаю Enter, и рядом с каждым исходным аудиофайлом появляется сразу несколько файлов:

  • *.txt — чистый текст без тайм-кодов
  • *.md — копия того же текста в формате Markdown
  • *.vtt — субтитры WebVTT с тайм-кодами
  • *.srt — субтитры SRT с тайм-кодами

Это подходит не только для подкаста, но и вообще для любой речи: диктофонных записей, лекций, встреч, совещаний, голосовых заметок и других аудиофайлов. Такой набор нужен не столько для публикации субтитров, сколько для себя и «машины»: для поиска по тексту, навигации по записи, дальнейшей чистки через ИИ и сохранения в архив.

Требования  Ссылка на этот раздел

  • MacOS с Homebrew
  • FFmpeg
  • whisper.cpp (через brew)
  • локальная модель Whisper для whisper.cpp

Установка  Ссылка на этот раздел

Установить FFmpeg и whisper.cpp:

BASH
1brew install ffmpeg whisper-cpp

Проверить, что ffmpeg доступен:

BASH
1ffmpeg -version

Проверить, что whisper CLI доступен:

BASH
1command -v whisper-cli || command -v whisper-cpp || command -v whisper

Модель Whisper  Ссылка на этот раздел

whisper.cpp использует отдельные файлы моделей. Модель скачивается один раз и хранится локально.

В этом варианте по умолчанию используется medium:

BASH
1mkdir -p ~/models/whisper
2cd ~/models/whisper
3curl -L -o ggml-medium.bin https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin

Проверить, что файл на месте:

BASH
1ls -lh ~/models/whisper/ggml-medium.bin

При необходимости можно скачать и другие модели, например large-v3, и запускать их вручную через переменную MODEL.

Настройка: команда voice в ~/bin  Ссылка на этот раздел

Папка ~/bin  Ссылка на этот раздел

Создай папку для личных команд, если её ещё нет:

BASH
1mkdir -p ~/bin

Скрипт ~/bin/voice  Ссылка на этот раздел

Создай файл:

BASH
1nano ~/bin/voice

Вставь код:

ZSH
  1#!/usr/bin/env zsh
  2set -u
  3set -o pipefail
  4
  5MODEL="${MODEL:-$HOME/models/whisper/ggml-medium.bin}"
  6LANG_ASR="${LANG_ASR:-ru}"
  7
  8# Находим CLI whisper.cpp
  9if command -v whisper-cli >/dev/null 2>&1; then
 10  WHISPER_BIN="whisper-cli"
 11elif command -v whisper-cpp >/dev/null 2>&1; then
 12  WHISPER_BIN="whisper-cpp"
 13elif command -v whisper >/dev/null 2>&1; then
 14  WHISPER_BIN="whisper"
 15else
 16  echo "Не найден whisper CLI. Установи: brew install whisper-cpp"
 17  exit 1
 18fi
 19
 20command -v ffmpeg >/dev/null 2>&1 || {
 21  echo "Не найден ffmpeg. Установи: brew install ffmpeg"
 22  exit 1
 23}
 24
 25[[ -f "$MODEL" ]] || {
 26  echo "Не найдена модель: $MODEL"
 27  exit 1
 28}
 29
 30cmd_name="$(basename "$0")"
 31
 32if [[ $# -lt 1 ]]; then
 33  echo "Использование: ${cmd_name} <путь_к_файлу_или_url> [ещё файлы или url...]"
 34  exit 1
 35fi
 36
 37process_one() {
 38  local INPUT="$1"
 39  local TMPROOT filename OUTDIR OUTBASE base WAVTMP
 40
 41  TMPROOT="$(mktemp -d)"
 42
 43  cleanup_one() {
 44    rm -rf "$TMPROOT"
 45  }
 46  trap cleanup_one RETURN
 47
 48  if [[ "$INPUT" == http://* || "$INPUT" == https://* ]]; then
 49    filename="$(basename "${INPUT%%\?*}")"
 50    [[ -n "$filename" ]] || filename="audio"
 51    curl -L --fail "$INPUT" -o "$TMPROOT/$filename" || {
 52      echo "Ошибка загрузки URL: $INPUT"
 53      return 1
 54    }
 55    INPUT="$TMPROOT/$filename"
 56    OUTDIR="$PWD"
 57    OUTBASE="$OUTDIR/${filename%.*}"
 58  else
 59    INPUT="${INPUT#file://}"
 60
 61    if [[ ! -f "$INPUT" ]]; then
 62      echo "Не файл: $INPUT"
 63      return 1
 64    fi
 65
 66    OUTDIR="$(cd "$(dirname "$INPUT")" && pwd)"
 67    base="$(basename "$INPUT")"
 68    OUTBASE="$OUTDIR/${base%.*}"
 69  fi
 70
 71  echo
 72  echo "[voice] Обработка: $INPUT"
 73
 74  WAVTMP="$TMPROOT/input.wav"
 75  ffmpeg -hide_banner -loglevel error -y -i "$INPUT" -ar 16000 -ac 1 -c:a pcm_s16le "$WAVTMP" || {
 76    echo "[voice] Ошибка FFmpeg: $INPUT"
 77    return 1
 78  }
 79
 80  "$WHISPER_BIN" \
 81    -m "$MODEL" \
 82    -f "$WAVTMP" \
 83    -l "$LANG_ASR" \
 84    -otxt -ovtt -osrt \
 85    -of "$OUTBASE" || {
 86      echo "[voice] Ошибка whisper.cpp: $INPUT"
 87      return 1
 88    }
 89
 90  if [[ -f "${OUTBASE}.txt" ]]; then
 91    cp -f "${OUTBASE}.txt" "${OUTBASE}.md" || {
 92      echo "[voice] Не удалось создать Markdown-копию: ${OUTBASE}.md"
 93      return 1
 94    }
 95  else
 96    echo "[voice] Не найден TXT-файл: ${OUTBASE}.txt"
 97    return 1
 98  fi
 99
100  echo "[voice] Готово:"
101  echo "  ${OUTBASE}.txt"
102  echo "  ${OUTBASE}.md"
103  echo "  ${OUTBASE}.vtt"
104  echo "  ${OUTBASE}.srt"
105
106  return 0
107}
108
109ok_count=0
110fail_count=0
111
112for item in "$@"; do
113  if process_one "$item"; then
114    ok_count=$((ok_count + 1))
115  else
116    fail_count=$((fail_count + 1))
117  fi
118done
119
120echo
121echo "[voice] Завершено. Успешно: ${ok_count}, с ошибками: ${fail_count}"
122
123[[ "$fail_count" -eq 0 ]]

Сделай файл исполняемым:

BASH
1chmod +x ~/bin/voice

PATH: чтобы voice работала без полного пути  Ссылка на этот раздел

Если ~/bin ещё не добавлен в PATH, открой ~/.zshrc:

BASH
1nano ~/.zshrc

Добавь в конец:

BASH
1export PATH="$HOME/bin:$PATH"

Применить изменения:

BASH
1source ~/.zshrc

Проверить, что команда доступна:

BASH
1type voice

Использование  Ссылка на этот раздел

Базовый запуск  Ссылка на этот раздел

BASH
1voice /путь/к/файлу.mp3

Результат: рядом с исходным файлом появятся:

  • имя.txt
  • имя.md
  • имя.vtt
  • имя.srt

Несколько файлов сразу  Ссылка на этот раздел

BASH
1voice /путь/к/первому.mp3 /путь/к/второму.wav /путь/к/третьему.m4a

Команда обработает каждый файл по очереди и создаст отдельный набор выходных файлов рядом с каждым из них.

Поддерживаются любые форматы, которые открывает FFmpeg: mp3, wav, m4a, flac и другие.

Запуск с другой моделью  Ссылка на этот раздел

Если нужно протестировать другую модель без переписывания скрипта, можно передать её через переменную MODEL:

BASH
1MODEL="$HOME/models/whisper/ggml-large-v3.bin" voice /путь/к/файлу.wav

Или явно указать medium:

BASH
1MODEL="$HOME/models/whisper/ggml-medium.bin" voice /путь/к/файлу.wav

Язык распознавания  Ссылка на этот раздел

По умолчанию используется ru. Переопределение через переменную окружения:

BASH
1LANG_ASR=en voice /путь/к/файлу.mp3

URL, если нужно  Ссылка на этот раздел

Команда принимает один или несколько URL и кладёт результат в текущую папку терминала:

BASH
1voice https://example.com/audio1.mp3 https://example.com/audio2.mp3

Примечания  Ссылка на этот раздел

  • txt сохраняется без тайм-кодов и подходит для чтения, поиска и дальнейшей обработки через ИИ.
  • md — это копия txt в Markdown-расширении. Удобно для заметок, Obsidian и дальнейшей ручной работы с текстом.
  • vtt и srt почти одинаковы по содержанию, но отличаются форматом. vtt начинается со строки WEBVTT, а srt использует нумерацию блоков и запятые в тайм-кодах.
  • Тайм-коды в vtt и srt сегментные, то есть фразами, а не по словам. Для навигации по записи этого обычно достаточно.
  • Все файлы для каждого аудио создаются за один запуск Whisper. Это почти не увеличивает время по сравнению с генерацией одного формата.
  • Скрипт всегда делает временный WAV 16 kHz mono. Это повышает стабильность обработки на длинных записях.
  • В этом варианте по умолчанию используется ggml-medium.bin. На некоторых записях эта модель ведёт себя стабильнее, чем large-v3, и реже уходит в повторы.

Диагностика  Ссылка на этот раздел

  • «Не найден whisper CLI»: проверь brew install whisper-cpp и command -v whisper-cli.
  • «Не найдена модель»: проверь путь к файлу модели в переменной MODEL.
  • «Команда не находится»: проверь PATH в ~/.zshrc и выполни source ~/.zshrc.
  • Если распознавание идёт не на том языке, явно задай LANG_ASR=ru.
  • Если на длинной записи появляются повторы одной и той же фразы, это может быть не ошибка скрипта, а особенность самой модели Whisper на конкретном фрагменте аудио.
  • Если передать несколько файлов сразу, команда должна обработать их все по очереди. Если обработался только один, проверь, не был ли старый вариант скрипта всё ещё сохранён в ~/bin/voice.

См. также  Ссылка на этот раздел

Есть что сказать? Напишите мне!
Комментировать по почте
Понравилось? Подпишитесь на меня!
RSS Телеграм