Локальная транскрипция подкаста через whisper.cpp
Что это?
Локальный инструмент для транскрипции аудиофайлов подкаста через whisper.cpp. Устанавливается на MacOS через Homebrew и добавляется как команда subs в ~/bin.
Идея простая: в терминале набираю subs и путь к файлу, нажимаю Enter, и рядом с аудио появляются сразу три файла:
*.txt— чистый текст без тайм-кодов*.vtt— субтитры WebVTT с тайм-кодами*.srt— субтитры SRT с тайм-кодами
Это нужно не для публикации субтитров слушателям, а для себя и «машины»: поиск по транскрипциям, навигация по выпуску, дальнейшая чистка текста через ИИ и архив подкаста.
Требования
- MacOS с Homebrew
- FFmpeg
whisper.cpp(через brew)- Модель Whisper для
whisper.cpp(в этом варианте:large-v3)
Установка
Установить FFmpeg и whisper.cpp:
1brew install ffmpeg whisper-cppПроверить, что ffmpeg доступен:
1ffmpeg -versionПроверить, что whisper CLI доступен (имя бинарника может отличаться, скрипт это учитывает):
1command -v whisper-cli || command -v whisper-cpp || command -v whisperМодель: Whisper large-v3
whisper.cpp использует отдельные файлы модели. Скачиваю large-v3 один раз и храню локально.
Создать папку и скачать модель:
1mkdir -p ~/models/whisper
2cd ~/models/whisper
3curl -L -o ggml-large-v3.bin https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.binПроверить, что файл на месте:
1ls -lh ~/models/whisper/ggml-large-v3.binНастройка: команда subs в ~/bin
Папка ~/bin
Создай папку для личных команд, если её ещё нет:
1mkdir -p ~/binСкрипт ~/bin/subs
Создай файл:
1nano ~/bin/subsВставь код:
1#!/usr/bin/env zsh
2set -euo pipefail
3
4MODEL="${MODEL:-$HOME/models/whisper/ggml-large-v3.bin}"
5LANG_ASR="${LANG_ASR:-ru}"
6
7# Находим CLI whisper.cpp
8if command -v whisper-cli >/dev/null 2>&1; then
9 WHISPER_BIN="whisper-cli"
10elif command -v whisper-cpp >/dev/null 2>&1; then
11 WHISPER_BIN="whisper-cpp"
12elif command -v whisper >/dev/null 2>&1; then
13 WHISPER_BIN="whisper"
14else
15 echo "Не найден whisper CLI. Установи: brew install whisper-cpp"
16 exit 1
17fi
18
19command -v ffmpeg >/dev/null 2>&1 || {
20 echo "Не найден ffmpeg. Установи: brew install ffmpeg"
21 exit 1
22}
23
24[[ -f "$MODEL" ]] || {
25 echo "Не найдена модель: $MODEL"
26 exit 1
27}
28
29cmd_name="$(basename "$0")"
30
31if [[ $# -lt 1 ]]; then
32 echo "Использование: ${cmd_name} <путь_к_файлу_или_url>"
33 exit 1
34fi
35
36INPUT="$1"
37TMPROOT="$(mktemp -d)"
38cleanup() {
39 rm -rf "$TMPROOT"
40}
41trap cleanup EXIT
42
43if [[ "$INPUT" == http://* || "$INPUT" == https://* ]]; then
44 filename="$(basename "${INPUT%%\?*}")"
45 [[ -n "$filename" ]] || filename="audio"
46 curl -L --fail "$INPUT" -o "$TMPROOT/$filename"
47 INPUT="$TMPROOT/$filename"
48 OUTDIR="$PWD"
49 OUTBASE="$OUTDIR/${filename%.*}"
50else
51 INPUT="${INPUT#file://}"
52
53 if [[ ! -f "$INPUT" ]]; then
54 echo "Не файл: $INPUT"
55 exit 1
56 fi
57
58 OUTDIR="$(cd "$(dirname "$INPUT")" && pwd)"
59 base="$(basename "$INPUT")"
60 OUTBASE="$OUTDIR/${base%.*}"
61fi
62
63# Приводим аудио к стабильному входу для whisper.cpp
64WAVTMP="$TMPROOT/input.wav"
65ffmpeg -hide_banner -loglevel error -y -i "$INPUT" -ar 16000 -ac 1 -c:a pcm_s16le "$WAVTMP"
66
67# Один запуск, сразу три формата:
68# TXT — без тайм-кодов
69# VTT — с тайм-кодами
70# SRT — с тайм-кодами
71"$WHISPER_BIN" \
72 -m "$MODEL" \
73 -f "$WAVTMP" \
74 -l "$LANG_ASR" \
75 -otxt -ovtt -osrt \
76 -of "$OUTBASE"
77
78echo "Готово:"
79echo " ${OUTBASE}.txt"
80echo " ${OUTBASE}.vtt"
81echo " ${OUTBASE}.srt"Сделай файл исполняемым:
1chmod +x ~/bin/subsPATH: чтобы subs работала без полного пути
Если ~/bin ещё не добавлен в PATH, открой ~/.zshrc:
1nano ~/.zshrcДобавь в конец:
1export PATH="$HOME/bin:$PATH"Применить изменения:
1source ~/.zshrcПроверить, что команда доступна:
1type subsИспользование
TXT, VTT и SRT рядом с файлом
1subs /путь/к/эпизоду.mp3Результат: рядом появятся три файла:
эпизоду.txtэпизоду.vttэпизоду.srt
Поддерживаются любые форматы, которые открывает FFmpeg: mp3, wav, m4a, flac и т.д.
URL, если нужно
Команда принимает URL, скачивает файл во временную папку и кладёт результат в текущую папку терминала:
1subs https://example.com/episode.mp3Язык распознавания
По умолчанию используется ru. Переопределение через переменную окружения:
1LANG_ASR=en subs /путь/к/файлу.mp3Примечания
txtсохраняется без тайм-кодов и подходит для чтения, поиска и дальнейшей обработки через ИИ.vttиsrtпочти одинаковы по содержанию, но отличаются форматом.vttначинается со строкиWEBVTT, аsrtиспользует нумерацию блоков и запятые в тайм-кодах.- Тайм-коды в
vttиsrtсегментные, то есть фразами, а не по словам. Для навигации по подкасту этого достаточно. - Используется модель
large-v3. Она тяжёлая, но обычно даёт более чистую транскрипцию. - Скрипт всегда делает временный WAV 16 kHz mono. Это повышает стабильность обработки на длинных записях.
- Все три файла создаются за один запуск Whisper. Это почти не увеличивает время по сравнению с генерацией одного формата.
Диагностика
- «Не найден whisper CLI»: проверь
brew install whisper-cppиcommand -v whisper-cli. - «Не найдена модель»: проверь путь
~/models/whisper/ggml-large-v3.bin. - «Команда не находится»: проверь PATH в
~/.zshrcи выполниsource ~/.zshrc. - Если распознавание идёт не на том языке, явно задай
LANG_ASR=ru. - Если на длинной записи появляются повторы одной и той же фразы, это может быть не ошибка скрипта, а особенность самого Whisper на конкретном фрагменте аудио.