Разработка

Как мой радио-хак превратился в программируемую платформу для вещания

08 июля 2026Автор Mike K8 мин чтения

Если коротко: я начал собирать личный радиопоток, потому что хотел получать обновления по технологиям, играм и кино, прогнозы погоды, подкасты и музыку — без политики и шума. Началось всё как хак на RSS-лентах, голосах ElevenLabs, музыке из Suno, джинглах и Icecast, но постепенно превратилось в полноценную гео-распределённую радиоплатформу на Kubernetes: со своим вещанием, расписанием, воспроизведением на Android TV, рестримингом на YouTube/Twitch, ИИ-сгенерированной документацией и открытой бетой.

Пример того, что получилось:

Ещё можно послушать радиостанции, созданные другими пользователями, на Tunio Streams.

Как всё началось

Лил дождь. Я стоял на крыше и молча смотрел на город сквозь дождь. Город тонул в собственном шуме.

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

И я собрал радио для себя.

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

Так родился Tunio. Я убрал руки с клавиатуры и закурил. Начинался новый день.

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

  • Подкасты, сгенерированные по промпту. Ты задаёшь тему и инструкции — и система создаёт подкаст, без опоры на предыдущие выпуски в контексте.
Подкасты, сгенерированные по промпту
0:00
0:00
  • RSS-аудио. Я подключил популярные RSS-ленты с новостями о технологиях и играх. Система собирает посты, отсеивает рекламу, негатив и политику, делает выжимку текстов и отправляет каждую новость в ElevenLabs для озвучки. Затем всё сводится в единый аудиоблок.
RSS-аудио
0:00
0:00
  • Прогнозы погоды. Можно настроить список нужных локаций — система ежедневно берёт погоду на сегодня и завтра для каждой, генерирует голосовой сегмент через ElevenLabs и сводит всё в единый погодный блок.
Прогноз погоды
0:00
0:00
  • Джинглы и генератор джинглов. Это стало возможным благодаря связке TTS-API для генерации коротких аудиоклипов и обработки звука эффектами на базе ffmpeg.
Джинглы и генератор джинглов
0:00
0:00

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

Сочетание живых голосов ElevenLabs и свежих моделей Suno сделало весь поток гораздо естественнее. Потому что когда я только начинал воплощать идею, Suno была версии 3.5 — и, честно говоря, её едва можно было слушать, разве что инструментал, да и то не дольше пяти минут.

Я работаю системным архитектором и не хотел строить это поверх готового вещательного стека (вроде AzuraCast и подобных платформ). Мне хотелось сделать всё с нуля и заодно прокачать свои практики DevOps/SRE.

Через несколько месяцев работы у меня уже была собственная гео-распределённая инфраструктура на Kubernetes. Каждая радиостанция работает в своём изолированном кластере на отдельном железе — со своими репликами базы данных, инстансом Icecast и полной оркестрацией потока.

Сначала я начал со связки Icecast + Liquidsoap, но со временем меня перестали устраивать требования Liquidsoap к процессору. В итоге я написал лёгкий вещатель на Go, который просто поднимает FFmpeg на каждую станцию и непрерывно стримит всё в Icecast — на основе того, что ему отдаёт движок ротации станции.

Что пошло не так по пути:

Сначала я настроил систему на вещание, транскодирование и хранение всего в MP3. Но как только я взялся за рестриминг на площадки вроде YouTube и Twitch, стало ясно, что этот формат не подходит — они ждут AAC, часто в контейнере M4A.

Это сразу породило новую проблему: транскодирование в AAC в реальном времени заметно повышало нагрузку на процессор — даже для одного исходящего потока.

Контейнер M4A для AAC ещё и практичнее: он поддерживает встроенные метаданные (включая обложку) и корректно сохраняет длительность трека — в отличие от «сырых» AAC-потоков. С другой стороны, если не использовать обработку звука вроде кроссфейдов или эффектов в Liquidsoap, можно гонять всё в режиме passthrough вообще без транскодирования — и тогда нагрузка на процессор почти нулевая.

Так что отсутствие единого AAC-пайплайна стало одним из первых блокеров, когда я пытался построить лёгкий вещатель на базе ffmpeg.

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

Проблема была в том, что большинство VPS-провайдеров, которых я пробовал, были сильно переподписаны. Конкуренция на гипервизоре по сути «воровала» процессорное время — из-за этого мои процессы то и дело теряли вычислительные ресурсы. Это приводило к периодическим аудиоартефактам и нестабильности.

Конкуренция на гипервизоре воровала процессорное время
Конкуренция на гипервизоре воровала процессорное время

Как только я перешёл на выделенные серверы, проблема исчезла полностью.

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

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

Как только я начал генерировать джинглы сам, голосами ElevenLabs, большинство этих проблем просто исчезли. Я мог получить ровно то, что нужно — быстро, дёшево и без лишних согласований. Плюс это дало возможность с самого начала делать джинглы сразу на нескольких языках. Мои первые джинглы на ElevenLabs: Джингл №1, Джингл №2, Джингл 3

Джингл на ElevenLabs 1
0:00
0:00
Джингл на ElevenLabs 2
0:00
0:00
Джингл на ElevenLabs 3
0:00
0:00

А потом, пока я всё ещё строил ядро системы, случилось кое-что ещё: появился Sonnet.

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

Первые признаки успеха

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

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

Так появилось расписание для радиопотоков, а также отдельное приложение-плеер для Android TV. Оно подключается к станции по PIN-коду, играет поток и кеширует треки локально на случай обрывов интернета. Если устройство замечает, что интернет пропал, оно автоматически переключается в офлайн-режим и продолжает играть закешированное аудио, пока связь не восстановится.

И, конечно, ИИ-агенты.

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

Я даже за выходные поднял базу Qdrant и загрузил в неё все знания о системе — чтобы снизить нагрузку на поддержку и не отвечать самому на однотипные технические вопросы.

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

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