Как превратить аудиозапись в субтитры SRT с помощью Scribe и API Ofox

Распознайте аудио через Scribe в Ofox, создайте SRT по временным меткам слов и добавьте субтитры в MP4. Реальная запись, ответы API и скрипты проверки.

Рисунок печатной машинки на светлой карточке, серо-голубой фон, желтый круг и надпись Scribe: Audio to Subtitles.

Отправьте WAV- или MP3-файл на эндпоинт Ofox /v1/audio/transcriptions, запросите verbose_json и проверьте временные метки для каждого слова в ответе. Затем локально создайте файл SRT и сверьте субтитры с записью. В проверенном для этой статьи маршруте шлюза прямой запрос SRT — не тот же поддерживаемый способ работы: адаптер принимает JSON, а загружаемый конвертер создает файл субтитров на основе фактического ответа.

В нашем тесте от 10 октября 2026 года была обработана реальная аудиозапись ElevenLabs длительностью 10,00 секунды. Scribe вернул слова из сценария, хотя пунктуация отличалась, и временные метки слов от 0,14 до 9,78 секунды. Мы преобразовали эти метки в три субтитровых блока и добавили их в MP4 как выбираемую дорожку субтитров на английском языке. Это пример полного рабочего процесса, а не бенчмарк точности распознавания речи и не проверка работы с шумными совещаниями.

Файлы в комплекте и что каждый из них подтверждает

В комплекте для скачивания есть исходный аудиофайл, оригинальный JSON-ответ API, конвертер, файл SRT и MP4 с выбираемыми субтитрами. Храните файлы вместе, чтобы редактор субтитров мог установить, откуда взялась временная метка каждого блока.

Аудиопример ElevenLabs

ФайлНазначениеЧего он не подтверждает
elevenlabs.mp3Исходная аудиозапись, использованная в тестеКачество распознавания человеческой речи или речи в шуме
narration-transcript.jsonНеизмененный ответ ScribeЧто это вручную исправленная итоговая расшифровка
narration.srtСубтитры, локально сгруппированные по временным меткамЧто все целевые проигрыватели отображают SRT одинаково
subtitled-selectable.mp4Видео, аудио и дорожка субтитровЧто субтитры встроены в изображение и видны без поддержки со стороны проигрывателя

Голос в примере синтетический, а текст — оригинальный материал для этого руководства. В записи нет приватных аудиоданных клиентов. Если вы заменяете ее реальным интервью, лекцией или звонком, используйте материал, который вправе загружать. Разрешение прослушивать запись само по себе не означает разрешения отправлять ее в облачный сервис транскрибации.

1. Подготовьте запись, сохранив исходные данные

Проверенный шлюз принимает WAV и MP3. Если исходник — MP4 или файл в другом контейнере, сначала извлеките аудиокопию. Сохраните оригинальный файл и запишите команду конвертации. Если окажется, что тишина, монтаж или изменение частоты дискретизации повлияли на смещение, вы должны иметь возможность вернуться к исходной временной шкале.

ffmpeg -i original-video.mp4 -vn -c:a pcm_s16le recording.wav
ffprobe -v error -show_entries format=duration \
  -of default=noprint_wrappers=1:nokey=1 recording.wav

В результате извлечения получится несжатый аудиофайл, который может быть значительно больше сжатого исходника. Не считайте, что WAV большего размера содержит более качественную исходную речь. Выберите подходящий поддерживаемый формат для своего рабочего процесса и проверьте текущие ограничения на загрузку в аккаунте, а не исходите из предположения, что любая длинная запись поместится в один запрос.

Перед первой транскрибацией не вносите в запись лишние изменения. Если удалить вступление длительностью две секунды, позиции всех последующих субтитров относительно неизмененного видео сдвинутся. Если вы намеренно транскрибируете только фрагмент, сохраните время его начала в исходнике и прибавьте это смещение, когда будете возвращать субтитры на временную шкалу полного видео.

Если в записи несколько аудиодорожек, явно выберите нужный аудиопоток. Иначе при извлечении может быть выбрана дорожка с комментариями, переводом или без звука. Проверьте потоки входного файла и прослушайте экспортированный аудиофайл, прежде чем оплачивать транскрибацию не того материала.

2. Отправьте multipart-запрос с правильной моделью

Укажите API-ключ Ofox в переменной OFOX_API_KEY, установите Python-библиотеку requests и запустите предоставленный клиент:

python3 audio_api.py transcribe \
  --input elevenlabs.mp3 \
  --output my-transcript.json

Клиент отправляет model=elevenlabs/scribe_v2 и response_format=verbose_json. Он позволяет HTTP-библиотеке сформировать границу multipart-запроса, открывает аудиофайл в бинарном режиме, проверяет ответ и сохраняет метаданные, в том числе длительность входного файла. Клиент автоматически не повторяет POST-запрос, завершившийся ошибкой или по тайм-ауту.

Эквивалентный запрос cURL:

curl --fail-with-body --silent --show-error \
  https://api.ofox.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $OFOX_API_KEY" \
  -F 'model=elevenlabs/scribe_v2' \
  -F 'response_format=verbose_json' \
  -F 'file=@elevenlabs.mp3;type=audio/mpeg' \
  --output my-transcript.json

Используйте один клиент, а не оба, если только не собираетесь выполнить еще один платный запрос. Не добавляйте вручную Content-Type: multipart/form-data без автоматически сформированной границы. Заголовок авторизации и объявления полей multipart выполняют разные задачи: ключ передается в Authorization, а модель и файл — как поля формы.

Проверьте текущую страницу модели Scribe в Ofox, чтобы узнать, какой маршрут доступен вашему аккаунту. В документации API транскрибации ElevenLabs описан интерфейс провайдера, в котором могут быть поля, не передаваемые адаптером. Наличие возможности в API провайдера не подтверждает, что тот же параметр работает через Ofox.

3. Изучите фактический JSON, прежде чем писать конвертер

В нашем ответе есть поля text, language, duration, usage, logprobs и words. Каждый элемент в words содержит word, start и end. Код, рассчитанный на другую схему, может ожидать поле text у каждого слова или список segments; ни одно из этих предположений не должно заменять проверку фактически полученного файла.

В примере возвращенный текст начинается словами “A clear product video starts with a clear brief.” — «Четкое продуктовое видео начинается с четкого брифа». Последнее слово заканчивается на отметке 9,78 секунды, а длительность аудиоконтейнера составляет 10,00 секунды. Конец речи и конец файла не обязаны совпадать: после речи может оставаться тишина или заполнение, добавленное при кодировании.

Следующий код читает сохраненный ответ и не отправляет новый запрос к API:

import json
from pathlib import Path
result = json.loads(Path('my-transcript.json').read_text())
print(result['text'])
print(result.get('language'))
print(result.get('usage'))
for item in result.get('words', [])[:5]:
    print(item['start'], item['end'], item['word'])

Сохраните оригинальный ответ до исправления орфографии или пунктуации. Если поля words нет, обычной расшифровки недостаточно, чтобы восстановить точные временные метки субтитров. Используйте поддерживаемый ответ с временными метками или отдельный этап выравнивания. Равномерно распределить общую длительность по словам — значит придумать временные метки; это не равноценная замена.

Не определяйте личность говорящего по имени, произнесенному в тексте. Ответ в этом примере содержит временные метки слов, но не подтвержденные личности говорящих. В связанном рабочем процессе превращения записи совещания в список задач это различие также учитывается при извлечении ответственных и решений.

4. Сгруппируйте слова в удобные для чтения субтитры

Субтитровый блок должен содержать порядковый номер, время начала и конца, а также текст, удобный для чтения. Скрипт make_subtitles.py группирует фактические временные метки слов, применяя простые ограничения по числу символов и длительности блока. Он сохраняет время начала первого слова и время окончания последнего слова в каждой группе, не создавая нового выравнивания.

python3 make_subtitles.py my-transcript.json my-subtitles.srt

Для предоставленного примера первый блок выглядит так:

1
00:00:00,140 --> 00:00:02,980
A clear product video starts with a clear brief.

Следующие два блока охватывают промежутки 3,56–7,36 и 7,42–9,78 секунды. Пауза после первого предложения сохранена: между блоками не добавлены субтитры только ради непрерывности временной шкалы.

Ограничения конвертера — 58 символов и пять секунд на блок — это выбранные для короткого англоязычного примера параметры реализации, а не универсальные стандарты доступности или телевещания. Для японского, корейского и языков с другим принципом разделения слов нужны подходящие для языка правила сегментации. Скорость чтения, разбиение строк и объем текста, помещающийся на экране телефона, важнее механического повторения английского порога.

Проверьте каждую границу между блоками. Второй блок в примере заканчивается словом “and” — «и». Формально это допустимо, но может быть неудачным редакторским разрывом. Редактор может перенести слово из одного соседнего блока в другой, сохранив фактическую временную метку этого слова. Храните исправленный вариант субтитров отдельно и описывайте внесенные изменения; не перезаписывайте исходную транскрибацию, скрывая разницу.

Конвертер отклоняет отсутствующие и отрицательные временные метки, NaN и бесконечные значения, а также немонотонные значения времени начала. Это позволяет обнаружить структурные проблемы, но не все визуальные дефекты. Даже если временные метки проходят проверку схемы, субтитры могут быть слишком короткими для чтения или неудачно сформулированными.

5. Исправьте расшифровку, сохранив ее происхождение

Сверьте расшифровку с записью и утвержденными исходными материалами. В нашем синтетическом примере Scribe вернул нужные слова, но не поставил конечную пунктуацию в коротком тексте. Нормализация пунктуации — не то же самое, что ошибка в названии продукта или пропущенное отрицание; эти правки следует регистрировать отдельно.

Для реальных записей в первую очередь проверяйте имена, числа, даты, единицы измерения и фразы с «не». Именно эти детали часто влияют на то, что должен сделать зритель. Не заменяйте молча неразборчивое слово на то, которое, как подсказывает слайд презентации, должен был произнести говорящий, если он сказал что-то другое. Отметьте неопределенность или обратитесь к уполномоченному проверяющему.

Полезная таблица исправлений содержит исходную фразу, предлагаемую правку, временной интервал в аудио, причину и статус проверки. Храните аудиозапись, исходный JSON и отредактированный SRT как отдельные материалы. Если кто-то спросит, почему изменились субтитры, вы сможете указать конкретный интервал записи, а не полагаться на краткое изложение модели.

6. Добавьте субтитры в видео и проверьте дорожку

Чтобы сохранить видео и аудио без изменений и добавить в MP4 выбираемую дорожку субтитров:

ffmpeg -i narration-video.mp4 -i my-subtitles.srt \
  -map 0:v:0 -map 0:a:0 -map 1:0 \
  -c:v copy -c:a copy -c:s mov_text \
  -metadata:s:s:0 language=eng \
  -disposition:s:0 default subtitled-selectable.mp4

Укажите правильный код языка субтитров. Эта команда добавляет текстовую дорожку, а не встраивает текст в изображение. Некоторые веб-проигрыватели игнорируют эту дорожку, даже если настольный проигрыватель может ее отображать. Флаг дорожки по умолчанию задает предпочтение, но не гарантирует, что субтитры покажутся на каждой платформе.

В предоставленном десятисекундном MP4 есть видео H.264, аудио AAC и дорожка субтитров mov_text. Изображение взято из фрагмента демонстрационного видео из предыдущего руководства и объединено с реальной озвучкой. Это пример упаковки материалов, а не свидетельство того, что видео сгенерировал API.

Проверьте потоки и извлеките встроенные субтитры для сравнения:

ffprobe -v error -show_entries stream=codec_type,codec_name \
  -of json subtitled-selectable.mp4
ffmpeg -i subtitled-selectable.mp4 -map 0:s:0 \
  recovered-subtitles.srt

Чтобы встроить субтитры в изображение, используйте средство рендеринга с поддержкой субтитров и повторно закодируйте видео. Сборка FFmpeg, использованная для этого руководства, не поддерживала фильтр рендеринга субтитров, который мы пытались применить, поэтому в итоговом примере используются выбираемые субтитры. Мы не называем этот файл вариантом со встроенными субтитрами или экспортом, визуально проверенным в проигрывателе. Перед публикацией на конкретной платформе проверьте загруженный результат в предназначенном для него проигрывателе.

7. Диагностируйте сбои, не повторяя ошибочный запрос

Если получена ошибка неподдерживаемого формата, проверьте, какие входные и выходные форматы принимает шлюз. Такая ошибка не означает, что в собственном API Scribe отсутствует соответствующая функция. Конвертируйте копию файла в WAV или MP3 и повторите запрос только после устранения несоответствия.

Если ошибка 401 связана с квотой, сохраните полный текст ответа и идентификатор запроса. Более ранние вызовы в этом проекте завершались сбоем на вышестоящем маршруте, а новые запросы заработали после восстановления. Положительный баланс кошелька сам по себе не позволяет определить, какой аккаунт провайдера обработал запрос. Сохраняйте подтверждающие данные, а не отправляйте одну и ту же запись снова и снова.

Если субтитры постепенно смещаются относительно видео, сравните аудио, которое фактически транскрибировалось, со звуковой дорожкой видео. Постоянное смещение часто указывает на обрезанное вступление или начало фрагмента. Изменяющееся смещение может быть связано с разным монтажом или скоростью воспроизведения. Не исправляйте каждый блок наугад, пока не выясните, совпадают ли временные шкалы.

Если файл длинный, прежде чем делить его на части, составьте план нарезки с исходными смещениями и проверкой перекрытий между фрагментами. На границах частей могут дублироваться слова, а потеря контекста способна изменить субтитры. Конвертер в этом примере предназначен для коротких файлов и не гарантирует автоматического решения задачи выравнивания длинных записей.

8. Учитывайте правильную единицу измерения и предусмотрите проверку

Расход транскрибации зависит от длительности аудио, а не от количества возвращенных слов. Проверенная длительность контейнера нашего файла составляет 10,00 секунды, а в исходном ответе API указано usage.seconds=10.083265306122449. Сохраните оба значения и не скрывайте их расхождение округлением. Не подменяйте оплачиваемую длительность входного файла временем окончания последнего произнесенного слова и не выводите из этого поля точную итоговую сумму.

Прежде чем считать субтитры готовыми, выполните пять проверок: загружена правильная запись, на отправку которой у вас есть разрешение; сохраненный JSON — успешный ответ; временные метки взяты из фактических слов; отредактированный текст проверен; целевой проигрыватель отображает нужную версию субтитров. Комплект для скачивания позволяет проверить вызов API, конвертацию и контейнер. Воспроизведение для аудитории — отдельная проверка перед публикацией в вашей среде.

Часто задаваемые вопросы

Можно ли запросить SRT напрямую у этого эндпоинта Ofox?
Проверенный здесь маршрут принимает JSON или подробный JSON. В этом руководстве временные метки слов из ответа преобразуются локально: мы не предполагаем, что вариант SRT из API провайдера передается через адаптер.
Что делать, если в ответе есть текст, но нет массива words?
Сохраните расшифровку, но не придумывайте временные метки слов. Чтобы создать субтитры с временными метками, получите поддерживаемый ответ с временными метками или выполните отдельное выравнивание.
Встроены ли эти субтитры в изображение?
Нет. В предоставленном MP4 есть выбираемая дорожка субтитров mov_text. Поддержка проигрывателей различается; чтобы встроить субтитры в изображение, нужно отрисовать текст непосредственно на кадрах видео.
Подтверждает ли этот тест точность Scribe при транскрибации совещаний?
Нет. Это короткая, чистая синтетическая озвучка. В реальных совещаниях есть перекрывающаяся речь, шум, имена и неоднозначность при определении говорящих; для них нужны отдельная оценка и процесс исправления.