Події Socket.io
Основний канал зв'язку. Стан радіо роздається тільки тут — через REST його отримати не можна.
Підключення
import { io } from 'socket.io-client';
const socket = io('http://localhost:3001', { withCredentials: true });Походження клієнта має бути в CLIENT_ORIGIN на сервері, інакше з'єднання не встановиться.
Що приходить одразу
Ці події сервер надсилає сам, без запиту з боку клієнта:
sync— повний стан радіо;radio_hosts_mode— чи ввімкнено режим живих ведучих;usersUpdate— активні слухачі;radio_hosts_online— лише коли режим ведучих увімкнено.
Щоб отримати artToken, клієнт має надіслати listener_init — див. Свій клієнт за 15 хвилин.
Колонка «Кому надсилається»
| Значення | Зміст |
|---|---|
усім | широкомовно всім підключеним |
одному сокету | адресно конкретному клієнту |
кімната … | лише учасникам кімнати, напр. живим ведучим |
Події з кімнат ваш клієнт не отримає, поки не приєднається до відповідної ролі — для звичайного слухача це нормально.
Службові події
У переліку немає вбудованих подій Socket.io — connect, disconnect, connect_error тощо. Вони працюють як завжди й описані в документації самої Socket.io; протоколу цього радіо вони не стосуються.
З тієї ж причини відфільтровано message і error: у коді вони належать UDP-сокету WebRTC-моніторингу, який теж названо socket, а не з'єднанню Socket.io.
Стан опису
Перелік генерується з коду, тож він завжди повний. Описи payload-ів пишуться вручну — зараз описано всі події.
Щоб змінити опис, відредагуйте reference/annotations/events.mjs і перезапустіть npm run extract. Якщо описати подію, якої в коді немає, перевірка впаде — так ловляться перейменування.
Клієнт → сервер: 42 подій. Сервер → клієнт: 37 подій. Описано детально: 79 з 79.
Клієнт → сервер
Події, які надсилає клієнт.
| Подія | Опис |
|---|---|
admin_active | Підвищує з’єднання до адмінського й підтверджує сесію. |
admin_add_song | Додає трек із бібліотеки в чергу. |
admin_go_live | Виводить адміна в ефір як ведучого. |
admin_guest_action | Схвалити або відхилити заявку гостя. |
admin_insert_song_group | Вставляє в чергу наперед задану групу пісень. |
admin_leave_live | Завершує ефір ведучого. |
admin_remove_song | Прибирає трек із черги за позицією. |
admin_skip_song | Перемикає поточний трек. |
admin_suggestion_action | Ухвалює або відхиляє замовлення слухача. |
get_queue | Сторінка майбутньої черги. |
guest_check_ban | Чи заблоковано цю IP-адресу для участі в ефірі. |
guest_connect | Під’єднання гостя до ефіру після схвалення. |
guest_leave_live | Гість залишає ефір. |
guest_request | Заявка слухача на участь в ефірі. |
host_audio_chunk | Порція звуку з мікрофона для підмішування в ефір. |
host_get_background_music_list | Сторінка доступної фонової музики. |
host_guest_kick | Вилучити гостя з ефіру. |
host_guest_mute | Вимкнути мікрофон гостю. |
host_mic_gain | Змінює гучність свого мікрофона в міксі. |
host_mic_toggle | Вмикає або вимикає мікрофон у ефірі. |
host_pause_queue | Ставить чергу треків на паузу — режим «просто розмова». |
host_resume_queue | Повертає програвання черги. |
host_set_background_music | Обирає трек, який гратиме під час паузи черги. |
listener_init | Реєструє з’єднання як слухача і просить видати art-токен. |
moderator_ban_ip | Заблокувати IP-адресу вручну. |
moderator_ban_participant | Заблокувати учасника ефіру за IP. |
moderator_deactivate_guest_code | Вимкнути чинний код доступу. |
moderator_generate_guest_code | Створити код доступу для спецгостя. |
moderator_get_banlist | Список заблокованих IP-адрес. |
moderator_get_guest_code | Поточний код доступу для спецгостя. |
moderator_get_live_roster | Хто зараз в ефірі. |
moderator_kick | Вилучити учасника з ефіру. |
moderator_mute | Вимкнути мікрофон будь-якому учаснику ефіру. |
moderator_regenerate_guest_code | Замінити чинний код доступу на новий. |
moderator_unban_ip | Зняти блокування з IP-адреси. |
monitor_answer | WebRTC-відповідь для персонального моніторингу ведучого. |
monitor_ice_candidate | ICE-кандидат для каналу моніторингу. |
search_queue | Пошук по майбутній черзі. |
special_guest_connect | Вхід в ефір за одноразовим кодом, без черги. |
stream_get_seek | Запитує поточну позицію спільного потоку (лише stream-режим). |
stream_ping | Вимірює затримку до сервера. |
suggest_song | Замовити пісню з бібліотеки в ефір. |
Сервер → клієнт
Події, на які клієнт має підписатися.
| Подія | Кому надсилається | Опис |
|---|---|---|
admin_authorized | одному сокету | Помічника активовано: тимчасовий пароль замінено на власний. |
admin_confirmed | одному сокету | Сесію підтверджено; містить актуальні права. |
admin_error | одному сокету | Помилка дії в адмінці. |
admin_online | усім | Чи є онлайн адмін, здатний ухвалити замовлення. |
admin_success | одному сокету | Успішне завершення дії в адмінці. |
audit_new_entry | усім | Новий запис у журналі аудиту. |
background_music_now_playing | кімната LIVE_HOSTS_ROOM | Який трек зараз заповнює паузу. |
background_music_selection_changed | кімната LIVE_HOSTS_ROOM | Ведучий обрав інший фоновий трек. |
background_music_updated | усім | Бібліотеку фонової музики змінено. |
donation_result | одному сокету | Підсумок донат-замовлення: пісню додано до черги чи ні. |
force_logout | одному сокету | Сесію припинено ззовні. |
guest_code_updated | одному сокету | Код доступу для спецгостів змінився. |
guest_force_disconnect | одному сокету | Участь гостя завершено ззовні. |
guest_pending_status | кімната LIVE_HOSTS_ROOM | Стан заявки гостя, поки він чекає. |
guest_queue_update | одному сокету | Черга заявок від гостей. |
guest_request_result | одному сокету | Рішення за заявкою гостя. |
host_force_disconnect | одному сокету | Ефір ведучого завершено ззовні. |
host_queue_pause_state | одному сокету, кімната LIVE_HOSTS_ROOM | Чергу поставлено на паузу, відновлено, або спробу паузи відхилено. |
jingles_updated | усім | Набір джинглів змінився. |
library_updated | усім | Бібліотека змінилася: трек додано, відредаговано, переміщено або видалено. |
listener_uid | одному сокету | Відповідь на listener_init: ідентичність слухача і art-токен. |
live_hosts_roster | одному сокету, кімната LIVE_HOSTS_ROOM | Поточний склад ефіру: ведучі й гості. |
phrases_updated | усім | Набір фраз змінився. |
privileges_updated | одному сокету | Права цього адміна змінили просто зараз. |
queue_updated | усім | Черга змінилася. |
radio_hosts_mode | одному сокету | Чи зібрано сервер із підтримкою живих ведучих. |
radio_hosts_online | усім, одному сокету | Чи є зараз хтось в ефірі. |
stream_chat_mode_end | усім | Розмова завершилася, черга поновлюється. |
stream_chat_mode_start | усім | Ведучі поставили чергу на паузу — почалася розмова. |
stream_jingle_end | усім | Джингл завершився. |
stream_jingle_start | усім | Почався джингл між піснями. |
stream_track_start | усім | У спільному потоці почався новий трек. |
suggest_cooldown_update | одному сокету | Скільки секунд лишилось до можливості замовити наступну пісню. |
suggestion_result | одному сокету | Рішення адміна щодо вашого замовлення. |
suggestions_update | одному сокету | Поточний список замовлень від слухачів. |
sync | усім, одному сокету | Повний стан радіо. Основна подія протоколу. |
usersUpdate | усім, одному сокету | Список активних слухачів для показу в шапці. |
Деталі
admin_active
adminПідвищує з’єднання до адмінського й підтверджує сесію.
Payload: token?: string — JWT; якщо не передати, береться з куки adminToken
Обов’язковий перший крок адмінського клієнта: доти сокет вважається звичайним слухачем і всі адмінські події ігноруються мовчки. У відповідь приходить admin_confirmed або admin_error. Привілеї перечитуються з бази, а не беруться з токена, тож зміна прав діє без релогіну. Попередня сесія того самого адміна автоматично відключається — одночасно живе лише одне з’єднання на обліковий запис.
admin_add_song
adminДодає трек із бібліотеки в чергу.
Payload:
{
id: 'day/artist - title.mp3',
title: 'Title', artist: 'Artist',
orderType: 'lastinline' | 'donated',
}Потребує привілею queue_manage. donated ставить трек попереду звичайних замовлень. Результат приходить подіями admin_success або admin_error, а не колбеком. Між додаваннями діє кулдаун; сервер також відмовить, якщо той самий трек уже стоїть поруч у черзі або якщо черга не вміщується до перемикання режиму.
admin_go_live
hostВиводить адміна в ефір як ведучого.
Payload: без даних (перший аргумент ігнорується)
Відповідь у колбеку:
{
ok: true,
queuePaused: false,
hosts: [ /* поточний склад ефіру */ ],
pendingGuests: [ /* черга заявок гостей */ ],
backgroundMusicMode: 'random' | 'hostChoice',
selectedBackgroundMusicId: null,
}
{ error: { uk: '…', en: '…' } }Потребує привілею radio_host і RADIO_HOSTS_MODE=true на сервері. Кількість місць в ефірі обмежена MAX_LIVE_HOST_SLOTS — при заповненні повертається помилка. Повторний виклик уже живим ведучим просто підтверджує стан. Після успіху сокет приєднується до кімнати ведучих і починає отримувати її події.
admin_guest_action
hostСхвалити або відхилити заявку гостя.
Payload:
{ uid: '…', action: 'accept' | 'reject' }Відповідь у колбеку:
{ ok: true } або { error: … }Потребує radio_host. Якщо місце зайняли між заявкою й рішенням, гість отримає відмову з reason: "room_full".
admin_insert_song_group
adminВставляє в чергу наперед задану групу пісень.
Payload:
{ groupId: string }Потребує обох привілеїв — queue_manage і settings_groups, — так само як POST /api/admin/song-groups/:groupId/insert. Групи налаштовуються окремо через /api/admin/song-groups. Має довший кулдаун, ніж поодинокі треки.
admin_leave_live
hostЗавершує ефір ведучого.
Payload: без аргументів
Місце звільняється одразу. Те саме відбувається при розриві з’єднання.
admin_remove_song
adminПрибирає трек із черги за позицією.
Payload: position: number — зсув від поточного треку, як у search_queue
Потребує queue_manage. Має власний кулдаун — при надто частих викликах повертається помилка з кількістю секунд очікування.
admin_skip_song
adminПеремикає поточний трек.
Payload: без аргументів
Потребує queue_manage. У stream-режимі сервер відмовить, якщо саме грає джингл або чергу поставлено на паузу ведучим. Кулдаун спільний із додаванням треків.
admin_suggestion_action
adminУхвалює або відхиляє замовлення слухача.
Payload:
{ uid: '…', action: 'add' | 'reject' }Потребує queue_manage. add ставить трек як lastinline. Слухач отримує suggestion_result. Якщо замовлення вже згасло за таймером, повертається помилка «не знайдено».
get_queue
adminСторінка майбутньої черги.
Payload:
{ offset?: number = 0, limit?: number = 10 }Відповідь у колбеку:
{
items: [{ id, title, artist, orderType }],
total: 42, // усього треків попереду
}Відповідь приходить у колбеку. Поточний трек у вибірку не входить.
guest_check_ban
guestЧи заблоковано цю IP-адресу для участі в ефірі.
Payload: без даних
Відповідь у колбеку:
{ banned: boolean }Варто викликати до показу форми заявки, щоб не давати марних надій.
guest_connect
guestПід’єднання гостя до ефіру після схвалення.
Payload: без даних
Відповідь у колбеку:
{ ok: true, role: '…', nickname: '…', expiresAt: 1754899200000 }Спрацює лише якщо сесію вже підтверджено ведучим і в ефірі є вільне місце. expiresAt — час примусового завершення; тривалість задається в налаштуваннях радіо. Далі гість користується тими самими подіями мікрофона, що й ведучий.
guest_leave_live
guestГість залишає ефір.
Payload: без аргументів
guest_request
guestЗаявка слухача на участь в ефірі.
Payload:
{ nickname: string }Відповідь у колбеку:
{ ok: true }
{ error: … , secsLeft?: number }Заявка потрапляє в чергу до ведучих; рішення приходить подією guest_request_result. Адмін подати заявку не може. Діє кулдаун і перевірка бану за IP.
host_audio_chunk
hostПорція звуку з мікрофона для підмішування в ефір.
Payload: Buffer / ArrayBuffer — сирі аудіодані
Приймається лише коли мікрофон увімкнено через host_mic_toggle — інакше фрагменти мовчки відкидаються. Це найгарячіший канал протоколу: надсилається безперервно, поки ведучий говорить.
host_get_background_music_list
hostСторінка доступної фонової музики.
Payload:
{ offset?: number = 0, limit?: number = 5 }Відповідь у колбеку:
{ items: [ … ], total: 12, mode: 'day' }Список залежить від поточного режиму радіо. limit обмежений 50. Не-ведучому повертається порожній список.
host_guest_kick
hostВилучити гостя з ефіру.
Payload:
{ targetId: string }Відповідь у колбеку:
{ ok: true } або { error: … }host_guest_mute
hostВимкнути мікрофон гостю.
Payload:
{ targetId: string, muted: boolean }Відповідь у колбеку:
{ ok: true } або { error: … }host_mic_gain
hostЗмінює гучність свого мікрофона в міксі.
Payload:
{ gain: number }host_mic_toggle
hostВмикає або вимикає мікрофон у ефірі.
Payload:
{ on: boolean }Доки мікрофон вимкнено, сервер відкидає аудіо-фрагменти від цього сокета. Працює і для ведучих, і для гостей.
host_pause_queue
hostСтавить чергу треків на паузу — режим «просто розмова».
Payload: без аргументів
Доступно лише ведучому, не гостю. Тишу заповнює фонова музика, а в події sync у всіх слухачів title стає "Just chatting".
host_resume_queue
hostПовертає програвання черги.
Payload: без аргументів
host_set_background_music
hostОбирає трек, який гратиме під час паузи черги.
Payload:
{ trackId: string | null }Відповідь у колбеку:
{ ok: true } або { error: … }null повертає випадковий вибір. Має сенс лише коли backgroundMusicMode дорівнює hostChoice.
listener_init
listenerРеєструє з’єднання як слухача і просить видати art-токен.
Payload: без аргументів
Сервер не видає токен сам — поки клієнт не надішле цю подію, у відповідь не прийде listener_uid, і доступ до артів, текстів та аудіо буде закритий. Адмінські з’єднання цю подію ігнорують.
moderator_ban_ip
adminЗаблокувати IP-адресу вручну.
Payload:
{ ip: string, nickname?: string }Відповідь у колбеку:
{ ok: true, entry: { … } } або { error: … }moderator_ban_participant
adminЗаблокувати учасника ефіру за IP.
Payload:
{ targetId: string }Відповідь у колбеку:
{ ok: true } або { error: … }Працює лише для гостей: заблокувати ведучого не можна. Потребує SQL-провайдера.
moderator_deactivate_guest_code
adminВимкнути чинний код доступу.
Payload: без даних
Відповідь у колбеку:
{ ok: true }moderator_generate_guest_code
adminСтворити код доступу для спецгостя.
Payload:
{ ttlHours?: number }Відповідь у колбеку:
{ ok: true, code: "…", expiresAt: 1754899200000 }Якщо активний код уже є, повертається помилка — спершу вимкніть його або скористайтеся moderator_regenerate_guest_code. Код дозволяє зайти в ефір без черги через special_guest_connect.
moderator_get_banlist
adminСписок заблокованих IP-адрес.
Payload:
{ offset?: number = 0, limit?: number = 10 }Відповідь у колбеку:
{ list: [ … ], total: 7 }Потребує DATA_PROVIDER=sql — інакше список недоступний.
moderator_get_guest_code
adminПоточний код доступу для спецгостя.
Payload: без даних
Відповідь у колбеку:
{ code: "…" } — або { code: null }, якщо активного коду немаєmoderator_get_live_roster
adminХто зараз в ефірі.
Payload: без даних
Відповідь у колбеку:
{ roster: [ … ], hostsOnline: true }Без привілею повертається порожній склад, а не помилка.
moderator_kick
adminВилучити учасника з ефіру.
Payload:
{ targetId: string }Відповідь у колбеку:
{ ok: true } або { error: … }Учасник отримає host_force_disconnect або guest_force_disconnect.
moderator_mute
adminВимкнути мікрофон будь-якому учаснику ефіру.
Payload:
{ targetId: string, muted: boolean }Відповідь у колбеку:
{ ok: true } або { error: … }На відміну від host_guest_mute, діє і на ведучих.
moderator_regenerate_guest_code
adminЗамінити чинний код доступу на новий.
Payload:
{ ttlHours?: number }Відповідь у колбеку:
{ ok: true, code: "…", expiresAt: … }Старий код одразу перестає діяти.
moderator_unban_ip
adminЗняти блокування з IP-адреси.
Payload:
{ ip: string }Відповідь у колбеку:
{ ok: true } або { error: … }monitor_answer
hostWebRTC-відповідь для персонального моніторингу ведучого.
Payload:
{ sdp: … }Особистий канал прослуховування ефіру з мінімальною затримкою — окремо від спільного MP3-потоку, який іде із запізненням. Потребує відкритих UDP-портів HOST_MONITOR_ICE_PORT_MIN–MAX.
monitor_ice_candidate
hostICE-кандидат для каналу моніторингу.
Payload: об’єкт кандидата WebRTC
search_queue
adminПошук по майбутній черзі.
Payload:
{ query?: string }Відповідь у колбеку:
[{ id, title, artist, orderType, position }]position — це зсув від поточного треку, і саме його очікує admin_remove_song. Порожній запит повертає всю чергу.
special_guest_connect
guestВхід в ефір за одноразовим кодом, без черги.
Payload:
{ code: string, nickname: string }Відповідь у колбеку:
{ ok: true, … } або { error: … }Код видає модератор. Кількість спроб обмежена; протухлий або вимкнений код дає помилку.
stream_get_seek
listenerЗапитує поточну позицію спільного потоку (лише stream-режим).
Payload: clientTs: number — локальний час клієнта
Відповідь у колбеку:
{ seek: 87.2, duration: 214.5, trackId: 'day/artist - title.mp3' }Потрібно лише щоб показати таймер в інтерфейсі: саме звучання у stream-режимі синхронне за побудовою. Поза stream-режимом сервер не відповідає взагалі.
stream_ping
listenerВимірює затримку до сервера.
Payload: clientTs: number
Відповідь у колбеку:
{ serverTs: number, clientTs: number }Сервер повертає ваш clientTs без змін — різниця часу туди й назад дає оцінку затримки для точнішого позиціювання.
suggest_song
listenerЗамовити пісню з бібліотеки в ефір.
Payload:
// об’єкт пісні з GET /api/library
{ id: 'day/artist - title.mp3', title: 'Title', artist: 'Artist' }Відповідь у колбеку:
{ ok: true }
{ error: 'cooldown', secsLeft: 240 }
{ error: 'no_admin' } // немає онлайн-адміна з правом на чергу
{ error: 'no_uid' } // не надіслано listener_initВикористовує підтверджувальний колбек — другим аргументом emit. Коди помилок тут — звичайні рядки, а не локалізовані об’єкти, на відміну від решти API. Кулдаун — 5 хвилин на uid; замовлення живе 5 хвилин і згасає, якщо адмін не відповів.
admin_authorized
adminПомічника активовано: тимчасовий пароль замінено на власний.
До цього моменту обліковий запис має authorized: false і нічого не може.
admin_confirmed
adminСесію підтверджено; містить актуальні права.
Payload:
{
role: 'super_admin' | 'admin',
privileges: ['queue_manage', '…'],
authorized: true, // false = помічник ще не активував себе
}Будуйте інтерфейс за цим списком, а не за тим, що було в токені. authorized: false означає, що помічник має спершу задати собі пароль.
admin_error
adminПомилка дії в адмінці.
Payload: рядок JSON з локалізованим об’єктом усередині
Payload — це рядок, а не об’єкт: його треба спершу JSON.parse, а вже потім вибрати мову. Те саме стосується admin_success.
admin_online
listenerЧи є онлайн адмін, здатний ухвалити замовлення.
Payload: boolean
Йде всім слухачам. false означає, що suggest_song поверне помилку no_admin, тож кнопку замовлення варто ховати.
admin_success
adminУспішне завершення дії в адмінці.
Payload: рядок JSON з локалізованим об’єктом усередині
Часто містить поле code для машинної обробки. Див. admin_error.
audit_new_entry
adminНовий запис у журналі аудиту.
Payload: об’єкт запису — той самий формат, що й у GET /api/admin/audit
Дозволяє дописувати журнал наживо, не перезапитуючи весь список. Необов’язковий data.details містить зміни «Було / Стало» та контекст: формат і типи подій.
background_music_now_playing
hostЯкий трек зараз заповнює паузу.
Payload:
{ trackId: '…', filename: '…' } // обидва null, коли фонова музика зупиненаbackground_music_selection_changed
hostВедучий обрав інший фоновий трек.
Синхронізує вибір між кількома ведучими в ефірі, щоб усі бачили те саме.
background_music_updated
adminБібліотеку фонової музики змінено.
Сигнал перечитати список через host_get_background_music_list.
donation_result
listenerПідсумок донат-замовлення: пісню додано до черги чи ні.
Payload:
{ donationId, accepted: true, tier: 2 | null, song: { title, artist } }
{ donationId, accepted: false, reason: 'payment_failed' | 'no_slot' }Приходить лише сокету, чий listener_uid збігається з донатером (обидва виводяться з IP). no_slot означає, що оплату підтверджено, але вставити пісню в чергу не вдалося (наприклад, не лишилось часу до зміни режиму) — донат позначається paid_unqueued і лишається видимим адміну в історії донатів.
force_logout
adminСесію припинено ззовні.
Payload:
{ reason: 'admin_deleted' }Обліковий запис видалено або відкликано. Клієнт має очистити стан і повернути користувача на екран входу.
guest_code_updated
adminКод доступу для спецгостів змінився.
Сигнал іншим адмінам оновити показаний код — щоб двоє модераторів не диктували різні коди.
guest_force_disconnect
guestУчасть гостя завершено ззовні.
Причини: вилучення, бан або вичерпаний ліміт часу з expiresAt. Клієнт має зупинити мікрофон і прибрати інтерфейс ефіру.
guest_pending_status
guestСтан заявки гостя, поки він чекає.
guest_queue_update
hostЧерга заявок від гостей.
Надсилається адресно лише адмінам з привілеєм radio_host.
guest_request_result
guestРішення за заявкою гостя.
Payload:
{ accepted: true, nickname: '…' }
{ accepted: false, auto: false, reason: 'room_full' }reason: "room_full" означає, що місце зайняли, поки заявка чекала. Після accepted: true клієнт має надіслати guest_connect.
host_force_disconnect
hostЕфір ведучого завершено ззовні.
Приходить при вилученні модератором або примусовому завершенні сесії. Клієнт має прибрати інтерфейс ефіру й зупинити захоплення мікрофона.
host_queue_pause_state
hostЧергу поставлено на паузу, відновлено, або спробу паузи відхилено.
Payload:
{ paused: boolean }
{ paused: false, denied: true, reason: 'donatedInQueue' } // лише тому, хто намагався поставити паузуУспішна зміна йде в кімнату ведучих. Відмова (черга містить донатну пісню) надсилається лише сокету, що її ініціював. Слухачі бачать сам стан паузи через sync.
jingles_updated
adminНабір джинглів змінився.
Сигнал перечитати список. Джингли доступні лише в конфігурації cloud + sql + stream.
library_updated
adminБібліотека змінилася: трек додано, відредаговано, переміщено або видалено.
Payload: без даних — це лише сигнал перечитати
Йде всім підключеним, не лише адмінам. Сигнал не каже, що саме змінилося, тож у відповідь просто перезапитайте потрібний список — /api/admin/songs для адмінки або /api/library для слухача. Надсилається з кількох місць: після завантаження, збереження в редакторі, зміни режиму треку та масових операцій.
listener_uid
listenerВідповідь на listener_init: ідентичність слухача і art-токен.
Payload:
{
uid: '…', // виводиться з IP, клієнт його не обирає
artToken: '…',
artTokenExpiresIn: 3600, // секунди
cooldownSecsLeft: 0, // до наступного замовлення пісні
}Слухачі за спільним NAT отримають однаковий uid, а отже спільний токен і спільний кулдаун замовлень.
live_hosts_roster
hostПоточний склад ефіру: ведучі й гості.
Надсилається в кімнату ведучих при кожній зміні складу. Звичайні слухачі цієї події не отримують.
phrases_updated
adminНабір фраз змінився.
Сигнал перечитати список. На відміну від джинглів, для фраз немає stream_phrase_start/stream_phrase_end: фраза мікшується у пісню, а не замінює її, тож метадані треку для слухачів не змінюються.
privileges_updated
adminПрава цього адміна змінили просто зараз.
Payload:
{ privileges: ['queue_manage', '…'], authorized: true }Приходить адресно тому адміну, чиї права змінив супер-адмін. Саме ця подія робить зміну прав чинною без повторного входу — перебудуйте інтерфейс за новим списком.
queue_updated
adminЧерга змінилася.
Широкомовний сигнал перечитати чергу через get_queue. Сам стан ефіру слухачі й далі отримують подією sync.
radio_hosts_mode
listenerЧи зібрано сервер із підтримкою живих ведучих.
Payload: boolean
Надсилається одразу після підключення, без запиту.
radio_hosts_online
listenerЧи є зараз хтось в ефірі.
Payload: boolean
Надсилається лише коли radio_hosts_mode увімкнено.
stream_chat_mode_end
listenerРозмова завершилася, черга поновлюється.
stream_chat_mode_start
listenerВедучі поставили чергу на паузу — почалася розмова.
Payload:
{ serverTs: … }Дублює те, що видно в sync за title: "Just chatting", але приходить миттєво, а не в межах двох секунд.
stream_jingle_end
listenerДжингл завершився.
stream_jingle_start
listenerПочався джингл між піснями.
Payload:
{ jingleId: '…', serverTs: … }Час сховати метадані треку: зараз звучить не пісня.
stream_track_start
listenerУ спільному потоці почався новий трек.
Payload:
{ trackId: '…', duration: 214.5, serverTs: 1754899200000 }serverTs — момент початку за годинником сервера. Разом із власним вимірюванням затримки через stream_ping це дає точну позицію в треку.
suggest_cooldown_update
listenerСкільки секунд лишилось до можливості замовити наступну пісню.
Payload: number — секунди, 0 якщо можна замовляти
Надсилається всім з’єднанням з тим самим uid, тож кілька вкладок бачать однаковий відлік.
suggestion_result
listenerРішення адміна щодо вашого замовлення.
Payload:
{ accepted: true, auto: false, song: { title, artist, … } }auto: true означає, що замовлення згасло за таймером, а не було відхилене вручну. Приходить лише тому сокету, що замовляв.
suggestions_update
adminПоточний список замовлень від слухачів.
Payload:
[{ uid: '…', song: { id, title, artist }, addedAt: 1754899200000 }]Надсилається лише адмінам з привілеєм queue_manage — при вході, при кожній зміні списку та при переході на денний режим, коли всі замовлення скидаються.
sync
listenerПовний стан радіо. Основна подія протоколу.
Payload:
{
track: 'day/artist - title.mp3', // null під час перемикання режиму
title: 'Title', artist: 'Artist', album: 'Album', year: 2020,
duration: 214.5,
seek: 87.2, // позиція на момент serverTimeMs
isPlaying: true,
playlist: [ /* до 10 наступних треків */ ],
currentIndex: 3, totalTracks: 42,
mode: 'day', // 'day' | 'night'
pendingModeSwitch: null,
skipCooldownSecsLeft: 0,
uiSettings: { /* брендинг */ },
serverTimeMs: 1754899200000,
dayStartHour: 6, nightStartHour: 0,
}Надсилається кожні 2 секунди всім, а також одразу після підключення. Коли сервер перемикає режим, приходить скорочена форма з isPreparing: true і track: null. Коли ведучі ставлять чергу на паузу, title стає "Just chatting", а artist порожніє.
usersUpdate
listenerСписок активних слухачів для показу в шапці.
Payload:
[{ name: 'Ім’я', img: 'file.png', color: '#a3f01c' }]Слухачі анонімні: сервер видає кожному з’єднанню випадкове ім’я з набору «олігархів» і випадковий колір. Ім’я приходить уже локалізованим.