Создание онлайн-радио GrindFM: опыт развертывания AzuraCast в Яндекс Облаке

Прокомментировать Просмотры: 16

Я давно и активно играю в STALCRAFT (ныне STALZONE). Одной из самых атмосферных деталей игры является радиостанция Grind FM, ненавязчиво звучащая фоном на базах. Однако у нее есть существенный минус: зона покрытия ограничена лишь отдельными безопасными уголками. Мне же захотелось слушать этот саундтрек прямо во время боевых вылазок, рейдов и зачистки мутантов.

Изучив доступные варианты без необходимости иметь навыки системного администрирования, я остановился на оптимальном решении: арендовать облачный VPS и развернуть готовый радиосервер с удобным веб-интерфейсом.

Необходимые компоненты

  1. Виртуальный сервер (VPS) в Яндекс Облаке — базово достаточно 1 ядра, 2 ГБ ОЗУ и 20 ГБ SSD.

  2. Доменное имя (по желанию, подключаться можно напрямую по IP).

  3. Коллекция треков в формате MP3.

  4. Около двух часов свободного времени.

Шаг 1. Развертывание виртуальной машины

В консоли Яндекс Облака переходим к созданию виртуального инстанса со следующими характеристиками:

Параметр

Значение

ОС

Ubuntu 24.04 LTS

Процессор (vCPU)

4 ядра

Оперативная память

12 ГБ

Накопитель

150 ГБ SSD

SSH-ключ

Автоматическая генерация в панели

Шаг 2. Установка платформы AzuraCast

AzuraCast — это мощная панель управления веб-радиостанцией, функционирующая в изолированных контейнерах Docker.

Подключаемся к терминалу сервера через встроенный Cloud Shell:

Ставим Docker:

sudo apt update
sudo apt install curl -y
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER

Перезаходим в сессию SSH, чтобы обновились права пользователя для группы docker.

Загружаем и запускаем скрипт установки AzuraCast:

curl -s https://raw.githubusercontent.com/AzuraCast/AzuraCast/main/docker.sh > docker.sh
chmod a+x docker.sh
./docker.sh install

Отвечаем на базовые вопросы конфигуратора в терминале.

На этом инсталляция AzuraCast успешно завершена.

Шаг 3. Первичная настройка через веб-интерфейс

Открываем в браузере адрес сервера: http://51.250.51.60 (IP вашего хоста).

Создаем профиль суперадминистратора: задаем адрес почты и надежный пароль.

Затем запускаем мастер настройки новой станции и последовательно проходим по вкладкам:

Профиль

  • Название станции: Grind FM

  • Жанровая направленность: Рок, пост-панк, альтернатива

  • Часовой пояс: UTC+3

  • Отображаемая история треков: 5

  • Воспроизведение по запросу: отключено

Вещание

  • Сервер вещания: локальный Icecast

  • Остальные параметры: по умолчанию

Автодиджей

  • Движок: Liquidsoap

  • Режим: Умный (Smart Mode)

  • Функции AutoCue и выравнивание ReplayGain: включены

Прочие параметры

  • HLS-потоки: отключены

  • Заказ композиций: отключен

  • Учетные записи стримеров: отключены

Шаг 4. Загрузка медиатеки

Самый трудоемкий этап — собрать оригинальный треклист из игры. Порывшись в сообществе Steam, я нашел ссылку на Яндекс Диск со всеми необходимыми аудиозаписями.

В панели управления переходим в Медиафайлы, жмем «Загрузить» и переносим директорию с файлами MP3.

AzuraCast в автоматическом режиме прочитает ID3-теги: наименование, исполнителя и графику обложек.

Шаг 5. Формирование плейлиста

В меню Плейлисты создаем новую ротацию со следующими параметрами:

Параметр

Значение

Название

GrindFM

Источник

Стандартные треки

Тип

Общая ротация

Порядок проигрывания

В случайном порядке (Shuffle)

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

Шаг 6. Старт трансляции

Открываем вкладку Вещание и нажимаем ПЕРЕЗАПУСТИТЬ ВЕЩАНИЕ.

Буквально через минуту службы переходят в активное состояние: Icecast и Liquidsoap успешно транслируют поток.

Шаг 7. Проверка трансляции

В блоке Обзор копируем ссылку на готовый стрим.

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

Публичный веб-плеер: http://51.250.51.60/public/grindfm_radio

Шаг 8. Стилизация в духе STALCRAFT

Чтобы стандартный плеер приобрел аутентичный сталкерский колорит, был написан кастомный CSS:

  • Глубокий темный фон с радиальным свечением

  • Характерная оранжево-янтарная подсветка

  • Стилизованный скроллбар

  • Скрытие ненужных стандартных кнопок

  • Тематический фон и кастомная заглушка обложек

Код вставляется в настройках внешнего вида в поле «Пользовательский CSS».

/* GrindFM — Сталкерский стиль */
body {
    background: #0f0c08;
    background-image: 
        radial-gradient(ellipse at 50% 0%, #2a1f14 0%, #0f0c08 70%);
    color: #c4b5a0;
    font-family: 'Courier New', monospace;
}

.player-container { background: rgba(20, 15, 10, 0.85); border: 2px solid #5a3d2b; border-radius: 12px; box-shadow: 0 0 40px rgba(80, 40, 20, 0.3); backdrop-filter: blur(10px); }

.navbar, .header { background: rgba(10, 8, 6, 0.9); border-bottom: 1px solid #4a3020; box-shadow: 0 2px 20px rgba(0, 0, 0, 0.8); }

h1, h2, h3, .station-name { color: #d4c5b0; text-shadow: 0 0 20px rgba(180, 100, 50, 0.3); letter-spacing: 3px; text-transform: uppercase; }

.station-name { font-size: 2.5rem; font-weight: bold; color: #e8d5b8; text-shadow: 0 0 30px rgba(200, 120, 60, 0.4), 0 0 60px rgba(150, 80, 30, 0.2); }

.now-playing { background: rgba(30, 22, 16, 0.7); border-left: 4px solid #8B5A2B; padding: 20px; border-radius: 8px; }

.song-title { color: #e8d5b8; font-weight: bold; font-size: 1.4rem; text-shadow: 0 0 15px rgba(200, 150, 80, 0.2); }

.song-artist { color: #a8957a; font-style: italic; }

.btn, .button { background: #2a1f14; border: 1px solid #6a4d35; color: #d4c5b0; padding: 10px 25px; border-radius: 6px; text-transform: uppercase; letter-spacing: 2px; transition: all 0.3s ease; }

.btn:hover, .button:hover { background: #4a3120; border-color: #b8885a; box-shadow: 0 0 30px rgba(180, 100, 50, 0.2); color: #f0e0c8; }

.offline-text { color: #8a7a6a; font-style: italic; font-size: 1.2rem; border-left: 3px solid #5a3d2b; padding-left: 20px; background: rgba(0, 0, 0, 0.3); border-radius: 4px; }

.schedule-item { background: rgba(30, 22, 16, 0.5); border-bottom: 1px solid #3a2a1e; padding: 12px; }

.schedule-item:hover { background: rgba(50, 35, 25, 0.6); }

/ Анимированный градиент для фона / @keyframes zoneGlow { 0% { opacity: 0.3; } 50% { opacity: 0.6; } 100% { opacity: 0.3; } }

.glow-border { border: 1px solid #6a4d35; animation: zoneGlow 3s ease-in-out infinite; }

/ Стиль для ссылок / a { color: #b8885a; text-decoration: none; border-bottom: 1px dashed #4a3020; transition: color 0.3s; }

a:hover { color: #d4a87a; border-bottom-color: #b8885a; }

/ Стиль для изображений-заглушек / .default-album-art { filter: sepia(0.3) brightness(0.7) contrast(1.2); border: 2px solid #4a3020; border-radius: 8px; }

/ Полоса прокрутки в стиле сталкер / ::-webkit-scrollbar { width: 10px; background: #1a1410; }

::-webkit-scrollbar-thumb { background: #4a3525; border-radius: 5px; }

::-webkit-scrollbar-thumb:hover { background: #6a4d35; }

/ Кнопка play / .play-btn { background: #3a2a1e; border: 2px solid #6a4d35; color: #e8d5b8; width: 60px; height: 60px; border-radius: 50%; font-size: 24px; transition: all 0.3s; box-shadow: 0 0 30px rgba(180, 100, 50, 0.2); }

.play-btn:hover { background: #5a3d2b; border-color: #b8885a; box-shadow: 0 0 50px rgba(200, 120, 60, 0.3); transform: scale(1.05); }

/ Дополнительный текст в стиле радио Зоны / .radio-frequency { color: #6a5a4a; font-size: 0.8rem; letter-spacing: 6px; text-transform: uppercase; border-top: 1px solid #3a2a1e; padding-top: 10px; }

/ ============================================= / / 🚫 СКРЫВАЕМ КНОПКУ "ПЛЕЙЛИСТ" (точный класс) / / ============================================= /

a.btn.btn-link.text-secondary[href*="playlist"] { display: none !important; }

Пользовательский JavaScript для добавления интерактивных элементов:

// GrindFM — Сталкерский JS
document.addEventListener('DOMContentLoaded', function() {
console.log('☢️ GrindFM — Радио Зоны запущено');

// Добавляем радио-частоту в заголовок
const header = document.querySelector('.station-name');
if (header) {
    const freq = document.createElement('span');
    freq.className="radio-frequency";
    freq.textContent="🔴 103.7 FM — Зона";
    freq.style.cssText = `
        display: block;
        font-size: 0.8rem;
        color: #6a5a4a;
        letter-spacing: 6px;
        text-transform: uppercase;
        border-top: 1px solid #3a2a1e;
        padding-top: 10px;
        margin-top: 8px;
    `;
    header.appendChild(freq);
}

// Добавляем сталкерскую приписку к статусу "оффлайн"
const offlineText = document.querySelector('.offline-text, .station-offline');
if (offlineText) {
    const stalkerMsg = document.createElement('div');
    stalkerMsg.style.cssText = `
        font-size: 0.9rem;
        color: #8a7a6a;
        margin-top: 10px;
        border-left: 3px solid #5a3d2b;
        padding-left: 20px;
        font-style: italic;
        background: rgba(0, 0, 0, 0.3);
        border-radius: 4px;
        padding: 12px;
    `;
    stalkerMsg.textContent="☢️ "Тихо в эфире. Жди сигнала."";
    offlineText.parentNode.insertBefore(stalkerMsg, offlineText.nextSibling);
}

// Меняем заголовок вкладки
document.title="☢️ GrindFM — Радио Зоны";

// Добавляем иконку радио (если есть)
const favicon = document.querySelector('link[rel="icon"]');
if (favicon) {
    // favicon уже есть — ничего не делаем
}

// Анимация мигания для активного статуса
const statusDot = document.querySelector('.status-dot, .live-indicator');
if (statusDot) {
    setInterval(() => {
        statusDot.style.opacity = Math.random() > 0.9 ? '0.3' : '1';
    }, 2000);
}

// Консольная заставка
console.log('%c☢️ GRIND FM — РАДИО ЗОНЫ', 'font-size: 20px; color: #e8d5b8; background: #1a1410; padding: 10px 20px; border: 2px solid #6a4d35; border-radius: 6px;');
console.log('%c"Здесь не место для слабых духом."', 'color: #8a7a6a; font-style: italic; font-size: 14px;');

// Обработка ошибок плеера с сталкерскими сообщениями
const audio = document.querySelector('audio');
if (audio) {
    audio.addEventListener('error', function(e) {
        console.log('⚠️ Потеря сигнала. Внимание, аномалия!');
    });
    audio.addEventListener('playing', function() {
        console.log('📡 Сигнал принят. Эфир активен.');
    });
    audio.addEventListener('waiting', function() {
        console.log('⏳ Буферизация... Перезагрузка в зоне.');
    });
}

console.log('✅ GrindFM — готов к работе!');

});

Скрипт был сгенерирован при помощи DeepSeek, так как я не обладаю навыками фронтенд-разработки.

Итоговые результаты

Компонент

Текущее состояние

Радиостанция

Стабильная работа 24/7

Публичная страница

http://51.250.51.60/public/grindfm_radio

Ротация музыки

Перемешивание треков без повторов чаще одного раза в 2 часа

Автодиджей

AutoDJ — автономное непрерывное вещание

Планы по улучшению проекта

  • Подключить персональный домен и настроить SSL (HTTPS)

  • Активировать интерактивный заказ треков для слушателей

  • Интегрировать тематические джинглы, реплики и шутки диджеев из игры

  • Сверстать отдельный автономный сайт для радио

Стоимость содержания

В Яндекс Облаке на старте предоставляется приветственный грант, после чего расходы составят:

  • VPS: от ~1500–2000 ₽/мес (выбранная мною усиленная сборка обходится примерно в 8800 ₽/мес)

  • Трафик: первые 100 ГБ ежемесячно бесплатно, далее — 1,53 ₽ за каждый ГБ

При нагрузке порядка 100 одновременных слушателей с качеством потока 128 кбит/с трафик полностью укладывается в бесплатную квоту.

Спасибо за прочтение! Я преданный фанат STALCRAFT и провожу в игре очень много времени. Надеюсь, разработчики оценят эту фанатскую инициативу. А если они вдруг увидят эту статью — огромная просьба: добавьте внутриигровой квест на получение плеера с наушниками, чтобы атмосферную музыку можно было слушать прямо во время рейдов!

 

Источник

Поделиться:

Похожие статьи

Поиск по играм, новостям и статьям…

Введите не менее двух символов

Введите не менее двух символов