Скрипт на ruby, который с помощью яндекс-сервиса Yandex SpeechKit распознает текст в видео-файле (длинные аудио)
Начну с того, что я совсем недавно начала погружаться в IT в целом и Ruby в частности, и это задание мне выдали в качестве тестового для получения места на стажировке. Заранее скажу, что тут еще есть что приглаживать и улучшать, но в целом код работает.
Однако, возможно, мой опыт может быть для кого то полезен, так что представляю вашему вниманию подробное описание создания этого скрипта. ВАЖНО: Моя операционная система Fedora 32, так же я использую заранее установленный в систему bundler. Так что если вы тоже используете linux-подобные системы, читаем далее.
Суть задания: есть видеофайл в формате mp4 нужно написать скрипт на чистом ruby, который будет конвертировать этот файл в аудио, отправлять его в сервис яндекса Yandex SpeechKit и получив ответ, создавать текстовый файл.
Перед началом работы стоит внимательно изучить документацию Яндекса, на предмет подводных камней и таких нюансов, как читаемый яндексом формат аудио (а их, к слову, всего два: OggOpus и LPCM).
Подготовительный этап:
Теперь можно перейти к составлению плана работы:
-
Перевод файла из формата mp4 в аудио с помощью утилиты ffmpeg
-
Отправить получившийся файл в бакет Yandex Service Object
-
Отправить полученный ответ с адресом файла в бакете на SpeechKit
-
Получить ответ и преобразовать его в текстовый файл
Далее будем двигаться по пунктам с пояснениями интересных (и не всегда очевидных мест)
1. Перевод файла из формата mp4 в аудио с помощью утилиты ffmpeg
Для форматирования видео файла устанавливаем в нашу систему ffmpeg
sudo dnf install ffmpeg
И, если вы этого еще не сделали, кладем требующий форматирования видеофайл в папку нашего небольшого проекта (в моём случае это будет test_task)
В этой же папке создаем рубишный файлик (например, run.rb), в котором будем писать скрипт:
touch run.rb
В скрипте нам необходимо вызывать команды оболочки изнутри программы ruby (bash-команды, такие как: system, exec, popen, ` `) Мельком ознакомиться с этими командами можно здесь (https://www.rubyguides.com/2018/12/ruby-system/)
Я решила использовать заключение в ` `:
`ffmpeg -i test.mp4 -vn -acodec libopus audio.ogg`
Где:
test.mp4 – название и формат форматируемого видеофайла.
Флажок ‘-vn’ используется, чтобы указать, что нам нужен именно аудио формат (без видео).
Библиотеку libopus мы используем, так как для SpeechKit нужен именно OggOpus формат.
audio.ogg – название для нового аудиофайла, который мы получим в результате форматирования и его формат (в нашем случае ogg)
Теперь, если запустить наш файл в консоли, мы должны получить на выходе аудиофайл в нужном нам формате.
2. Отправить получившийся файл в бакет Yandex Service Object
Теперь можно временно закоментировать написанную ранее строку и перейти к разработке следующей части скрипта.
Согласно документации яндекса, мы не можем загрузить в сервис непосредственно файл, нам нужно использовать ссылку на загруженный в Yandex Object Storage (яндекс хранилище) объект.
Так как Yandex Object Storage имеет HTTP API, совместимый с Amazon S3, мы можем воспользоваться инструментами, созданными для работы с объектными хранилищами Amazon S3.
Для Amazon S3 реализован руби гем aws-sdk-s3, мы можем применить его для загрузки нашего файла в Yandex Object Storage.
Устанавливаем гем aws-sdk-s3. Создаём в папке с тестовым заданием Gemfile и в нем прописываем:
source 'https://rubygems.org'
gem 'aws-sdk-s3'
gem ‘aws-sdk-s3’ Далее в консоли запускаем команду:
bundle install
И подключаем этот гем в наш файлик run.rb, прописывая в нём:
require 'aws-sdk-s3'
Как пример кода использовала эту статью.
На этом этапе нам понадобится создать API-ключ в яндексе и сохранить его.
Важный момент: создать нужно именно статический ключ доступа для Object Storage и Message Queue.
Так как, светить свои приватные ключи всему интернету – дурной тон, воспользуемся гемом dotenv. Этот гем нужен нам для введения переменных окружения, их мы заботливо поместим в файлик .env , который никому не покажем.
Устанавливаем гем, записываем его в Gemfile:
gem 'dotenv'
И запускаем команды в терминале:
bundle install
Теперь инклюдим его в наш файлик:
require 'dotenv/load'
И создаем в нашей папке со скриптом файлик .env с переменными, в которые заносим ключ и идентификатор:
AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXX
Далее просто меняем пример из статьи выше под наш случай. Cначала прописываем конфигурацию для aws, указывая регион и ключ с идентификатором:
Aws.config.update(
region: 'ru-central1',
credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
)
Добавляем эндпоинт яндекс хранилище:
region: 'ru-central1',
credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'],
ENV['AWS_SECRET_ACCESS_KEY'])
s3 = Aws::S3::Client.new(endpoint: "https://storage.yandexcloud.net")
И, наконец, открываем и отправляем отформатированный ранее аудиофайл в созданный нами бакет. (Здесь, разумеется можно обойтись без вывода результата(puts pp), но так нагляднее)
File.open('audio.ogg', 'r') do |file|
pp = s3.put_object({
bucket: 'teststask',
key: 'audio.ogg',
body: file
})
puts pp
end
Теперь мы можем проверить работу кода запустив run.rb и обновив страницу бакета (в нем должен появится новый файл).
3. Отправить полученный ответ с адресом файла в бакете на SpeechKit
Для работы с http запросами в руби есть гем httparty (https://github.com/jnunemaker/httparty/blob/master/examples/basic.rb)
И примеры работы с этим гемом.
Устанавливаем гем, записываем его в Gemfile:
gem 'httparty'
И запускаем команды в терминале:
bundle install
Теперь инклюдим его в наш файлик:
require 'httparty'
Далее можно воспользоваться им в написании кода для отправки ссылки из предыдущего этапа.
Необходимо сформировать ссылку, так как реализовывать ее получение внутри скрипта не является частью задачи , то прибегнем к хитрости. Ссылка состоит из сервиса, имени бакета и имени файла, которое обычно является путем к файлу: https://storage.yandexcloud.net/<имя-бакета>/<путь-к-файлу>
Значит мы можем самостоятельно сформировать эту ссылку, так как все это у нас уже есть:
https://storage.yandexcloud.net/teststask/audio.ogg
Конечно, эту часть можно довольно легко автоматизировать, но время не ждало и задачи улучшить скрипт не стояло)) Так что здесь можете применить все свои навыки!
Далее переходим к формированию post запроса на SpeechKit.
Нам необходимо составить запрос с хедером, содержащим API-ключ для авторизации и телом, включающим нашу ссылку и язык. Подробнее можно посмотреть в документации яндекса.
Важно: здесь нам необходим другой API-ключ (API-ключ для упрощенной аутентификации вместо IAM-токена)
Положим всё это в переменную options.
options = {
headers: {"Authorization" => "Api-Key #{ENV['API_KEY']}"},
body: {
"config" => {
"specification" => {
"languageCode" => "ru-RU"
}
},
"audio" => {
"uri" => "https://storage.yandexcloud.net/teststask/audio.ogg"
}
}.to_json
}
response = HTTParty.post('https://transcribe.api.cloud.yandex.net/speech/stt/v2/longRunningRecognize', options).to_h
Хеш нам нужен для того, что бы мы могли извлечь из него необходимый нам для отправки следующего запроса идентификатор операции распознавания.
Теперь нам нужно отправить запрос на получение информации об операции.
Создаем параметр для нового запроса в котором оставляем только авторизацию:
option = {
headers: {"Authorization" => "Api-Key #{ENV['API_KEY']}"}
}
И создаём цикл, который будет проверять, закончена ли обработка аудио и, в случае положительного результата выводить ответ в виде хеша. Здесь в качестве одного из параметров мы и передаем тот самый идентификатор операции распознавания (это #{response[‘id’]} в ссылке “https://operation.api.cloud.yandex.net/operations/#{response[‘id’]}”).
Интервал в 2 секунды между запросами, необходим, чтобы система яндекса не посчитала нас вредоносной программой)
done = false
until done
yandex_answer = HTTParty.get("https://operation.api.cloud.yandex.net/operations/#{response['id']}", option).to_h
puts yandex_answer
done = yandex_answer['done']
sleep 2
end
4. Получить ответ и преобразовать его в текстовый файл
Так как здесь вступет в дело ruby, многим эта часть покажется менее интересной. Просто работаем с хешами и массивами:
yandex_array = yandex_answer["response"]["chunks"]
yandex_text = []
yandex_array.each do |elem|
yandex_text << elem["alternatives"].first["text"]
end
pp yandex_text.uniq!
Создаем через bash-команду новый файлик:
`touch test.txt`
И кладем в него полученный массив переобразуя его в текст:
File.open("test.txt", 'w') { |file| file.write(":#{yandex_text.join(' ')}") }
Итого мы имеем три файла: .env (с переменными окружения), Gemfile (с тремя гемами: httparty, aws-sdk-s3, dotenv), run.rb (с кодом).
Вуаля, у вас есть маленький скрипт для форматирования видео в текст.