Яндекс.Облако
3541

Озвучить статьи на vc.ru

Менеджер по продукту «Комитета» рассказывает, чего это стоило.

В закладки
Аудио

Аудиоверсию этой статьи можно включить, нажав на кнопку «Аудио» в начале статьи.

Филипп Концаренко, менеджер по продукту в «Комитете»

Как устроены наши сайты

Наши сайты — vc.ru, DTF и TJ — работают на одной платформе. Мы называем её «Основа». Единая платформа удобна с точки зрения разработки — наша команда работает над кодом одновременно для всех проектов. Кроме того, эксклюзивное решение под vc.ru можно быстро масштабировать на остальные площадки. К примеру, раздел «Вакансии» мы разрабатывали сначала на vc.ru, а потом за пару дней запустили его ещё и на DTF.

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

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

Изобретаем механизм озвучки

Одним из таких экспериментов стала интеграция с «Яндекс.Облаком». Компания объявила об открытии API для озвучки текста (SpeechKit), и мы решили его попробовать на статьях.

Эта задача не потребовала сложной разработки: мы довольно быстро выстроили несколько процессов в логическую цепочку и выделили их в новый модуль.

Почему отдельный модуль? Во-первых, чтобы распределить нагрузку. Конвертация текста в аудио происходит в фоне, когда для этого есть свободные ресурсы сервера. Модуль получает запрос на озвучку, обрабатывает его и через какое-то время отправляет ответ со звуковой дорожкой.

Во-вторых, отдельный модуль — это универсальное решение, которым может пользоваться любой другой модуль «Основы». Если нужно озвучить ленту новостей или лучшие комментарии — это можно сделать простой отправкой запроса от модуля «Комментарии» или «Статья». При желании даже «Вакансии» могут запросить аудиоверсию объявлений.

У SpeechKit «Яндекса» опубликовано подробное описание API, поэтому всё подключение обошлось без общения с техподдержкой: прочитали, разработали, протестировали, оплатили, подключили. Первую рабочую версию получили уже через три дня.

А теперь подробнее про то, как мы озвучиваем статьи при помощи «Яндекс.Облака».

Из символа в звук: процесс изнутри

1. Формируем список статей для озвучки

Каждые 30 минут специальный алгоритм «Основы» собирает список из десяти наиболее популярных на текущий момент статей на vc.ru, TJ и DTF. Он учитывает множество параметров: на популярность влияет и количество прочтений, и оценки, и комментарии. Ещё мы смотрим на статус статьи: вносились ли в неё изменения и озвучивалась ли она раньше — это помогает в случаях, когда автор дополнил текст уже после озвучки.

2. Для каждой статьи из списка выбираем только текст

Справиться с этой задачей помогает модуль «Редактор», который отвечает за написание статей. Он сохраняет каждую заметку на vc.ru, TJ или DTF не как HTML-код, а как набор текстовых и медийных блоков, в которых содержание отделено от технических атрибутов.

Проще говоря, статья выглядит примерно так: в первом блоке — текст, во втором блоке — ссылка на картинку и подпись к ней. Достаточно попросить скрипт отфильтровать и выгрузить только текстовые блоки, и мы получим основное содержание материала.

3. Собираем все данные в «Гутенберг»

Полученные тексты отправляем на специальный внутренний сервис — мы называем этот модуль «Гутенбергом».

Он собирает все запросы на озвучку текстов с наших сайтов в единый поток, разбивает его на небольшие куски — по 5 тысяч символов, как этого требует API, добавляет параметр — озвучивать женским голосом или мужским и с какой скоростью. Далее выстраивается очередь из запросов для отправки в «Яндекс.Облако».

4. Отправляем запрос в «Яндекс.Облако»

В ответ «Яндекс.Облако» присылает нам файлы в формате OGG. Каждый из них — это озвучка фрагмента длиной не более 5 тысяч символов.

Таким образом, если статья состоит из двух тысяч символов, мы получим один OGG-файл. Для лонгрида в 24 тысячи символов — пять OGG-файлов.

5. Создаём MP3-файл и загружаем его на сервер

Для длинных статей «Гутенберг» сначала соединяет несколько OGG-файлов в один и генерирует на его основе MP3. Все популярные статьи получают свой MP3-файл.

Он загружается на наш сервер, а в параметр статьи добавляется дата его генерации и ссылка. Когда пользователь откроет статью, «Основа» увидит ссылку на аудиоверсию и отобразит иконку с наушниками.

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

Что получилось

Мы рассматривали новую функцию как удобный инструмент для потребления контента и не ставили каких-либо продуктовых метрик.

Но результат приятно удивил. Выросла лояльность пользователей приложения. Заметно выросло количество положительных отзывов в App Store и Google Play — читатели благодарили за появившуюся возможность прослушивать статьи.

Кроме того, мы начали приучать читателей к новому каналу получения информации, который из года в год становится популярнее и качественнее.

А ещё мы делаем подкасты на «Яндекс.Музыке» из RSS-ленты с озвученными статьями.

Никита Ткачёв, менеджер по развитию бизнеса в «Яндекс.Облаке»

Самый популярный сценарий использования SpeechKit — это голосовые роботы в телефонии. Но мы с интересом смотрим и за развитием других вариантов. К примеру, владельцы колл-центров строят на базе речевых технологий тренажёры для своих операторов, либо просто распознают их диалоги для проверки. Кто-то распознаёт речь в видео и быстро понимает, о чём оно. А стримеры озвучивают зрительские послания, чтобы во время трансляций не отвлекаться на текст.

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

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

Правда, среди доступных голосов нет Алисы, голосового ассистента «Яндекса»: её голос — эксклюзив для наших сервисов, хотя она тоже работает на базе SpeechKit. Но если попросить её озвучить актуальные новости, то она передаст слово своим коллегам — Джейн, Захару и другим голосам, которые можно подключить.

Сейчас главное направление нашей работы — «очеловечить» речь, на данный момент она легко отличима от живой. На это есть несколько причин: холодная механическая подача, ровный ритм, монотонные паузы. Рецепт один: улучшить разметку датасета — материала для работы нейросети.

Чем больше метрик мы будем использовать для дикторских записей, тем качественнее робот будет подражать человеческой речи. Если раньше мы выделяли фонемы и паузы, то теперь мы переходим к работе с достаточно тонкими материями — показываем роботу, где диктор говорит вдохновлённо, где его голос идёт «на повышение», ускоряется, становится мрачным или весёлым.

Мы поставили эксперимент: дали партнёрам возможность менять эмоциональную окраску голоса на раздражённую или радостную, и он оказался удачным.

Сейчас мы обкатываем синтез на принципиально новой архитектуре и результаты нас очень радуют: синтезированная речь стала более приятной на слух — модель учится имитировать человеческое дыхание и паузы.

Из существующих недостатков синтеза речи, можно выделить классическую контекстную ловушку: иногда робот не понимает, речь идёт о зАмке или замкЕ — и здесь ему нужна помощь проставленным ударением.

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

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

В этом помогают специальные теги внутри текста (SSML) — единственные кусочки кода, которые робот не станет произносить. С их помощью можно даже вручную управлять стилем речи — менять произношение, удлинять паузы или расшифровывать аббревиатуры. К примеру, произность vc.ru как «ви-си-ру».

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

<speak>Эй, секундочку<break time="1s"/> Что вы делаете?</speak>

Разработчикам и продуктовым командам, как правило, не составляет труда разобраться с API самостоятельно. Бизнесу это сложнее, поэтому мы работаем с партнёрами, которые берут эти заботы на себя — к примеру, с командой Speechki, которая поставила озвучку контента на поток.

К нам часто приходят запросы на запись индивидуального голоса. Пока мы не берём такие проекты — слишком большие накладные расходы на запись диктора, обработку датасета и контроль качества. Но в этом направлении есть сдвиги, и вскоре мы сможем реализовывать такие проекты за разумные деньги.

1 рубль 71 копейка
Стоимость озвучки этой статьи — в ней 9360 символов
{ "author_name": "Яндекс.Облако", "author_type": "editor", "tags": [], "comments": 23, "likes": 32, "favorites": 15, "is_advertisement": false, "subsite_label": "yandex.cloud", "id": 80606, "is_wide": true, "is_ugc": false, "date": "Mon, 09 Sep 2019 13:15:44 +0300", "is_special": false }
0
{ "id": 80606, "author_id": 341856, "diff_limit": 1000, "urls": {"diff":"\/comments\/80606\/get","add":"\/comments\/80606\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/80606"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 341856, "last_count_and_date": null }
23 комментария
Популярные
По порядку
Написать комментарий...
30

молодцы, но слушать это конечно же невозможно

Ответить
1

Почему, я норм слушаю.

Ответить
10

Вот как только там будет мужик из донатов — возможно, буду пользоваться.

Ответить
1

Твердыйзнак твердыйзнак твердыйзнак ц ц ц ц ц ц ц ц ц

Ответить
0

«Ау́дио»

Ладно Siri коверкает ударения, иностранка всё таки.
А в яндексе какие будут оправдания?

Ответить
11

Да не будет оправданий. Сегодня технология позволяет ударение легко исправить, а завтра и ошибки не будет.

Ответить
21

А я ведь завтра проверю.

Ответить
0

Это по следам дубляжа Дискавери: крепИм, кашИца, битОй и недавно я пополнил коллекцию словом соплО.

Ответить
3

надо добавить сколько времени на прослушивание/чтение как медиуме

Ответить
2

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

Ответить
2

На скорости 1.5 получше!

Ответить
2

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

Ответить
1

Да, это уже широкое поле для кастомизации - темп, паузы и т.д

Ответить
1

Всё время спотыкается на «Яндекс.Облако», считает точку окончанием предложения. Иногда «е» звучит как «ё». SpeechKit иногда звучит как «СпичКыт». TJ как «ТИ». MP3 иногда как «эмпэтрё».

Но в целом согласен — результат приятно удивил.

Ответить
0

Ну и, конечно, что-то из этого можно решить через <sub> и <phoneme> на стороне VC.

Ответить
0

А как к вам на озвучку устроиться? 😁 Очень интересный опыт был бы, и для практики бы пошло))

Ответить
2

Назовите вашу цену

Ответить
0

:DD Зарплата мечты

Ответить
0

не работает. айпад про ios 13.1 beta

Ответить
0

Спасибо

Ответить
0

Тезисно: не по себе так как всегда читал статьи , а тут за тебя читают(как говорится разрыв шаблона). Сложно пока воспринимать.
Плюс в том что я слушаю и уже пишу ответ в комментарий.
Резюмируя , пока сыровато , но у вас все получится. Начните с малых статей , в виде сводки за день, тезисное информирование.

Ответить
0

Очень непривычно и медленно, неэффективно тратится время слушателя, лучше бы кто-нибудь делал аудио-обзоры на статьи "выжимку", суть статьи. Удобно слушать в машине или на пробежке, если что-то очень интересное, можно перечитать на досуге.

Ответить
0

нельзя было сразу в статье написать "ЗП разрабам - 600к за 3 недели работы, аренда серверов - 15к, затраты в сумме - 764050р 54коп, а то какие-то выводы, подробности. Времени читать нет!

Ответить
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovx", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "disable": true, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ] { "page_type": "default" }