Skip to content

Події Socket.io ​

Основний канал зв'язку. Стан радіо роздається тільки тут — через REST його отримати не можна.

Підключення ​

js
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_answerWebRTC-відповідь для персонального моніторингу ведучого.
monitor_ice_candidateICE-кандидат для каналу моніторингу.
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:

js
{
  id: 'day/artist - title.mp3',
  title: 'Title', artist: 'Artist',
  orderType: 'lastinline' | 'donated',
}

Потребує привілею queue_manage. donated ставить трек попереду звичайних замовлень. Результат приходить подіями admin_success або admin_error, а не колбеком. Між додаваннями діє кулдаун; сервер також відмовить, якщо той самий трек уже стоїть поруч у черзі або якщо черга не вміщується до перемикання режиму.

admin_go_live ​

host

Виводить адміна в ефір як ведучого.

Payload: без даних (перший аргумент ігнорується)

Відповідь у колбеку:

js
{
  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:

js
{ uid: '…', action: 'accept' | 'reject' }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

Потребує radio_host. Якщо місце зайняли між заявкою й рішенням, гість отримає відмову з reason: "room_full".

admin_insert_song_group ​

admin

Вставляє в чергу наперед задану групу пісень.

Payload:

js
{ 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:

js
{ uid: '…', action: 'add' | 'reject' }

Потребує queue_manage. add ставить трек як lastinline. Слухач отримує suggestion_result. Якщо замовлення вже згасло за таймером, повертається помилка «не знайдено».

get_queue ​

admin

Сторінка майбутньої черги.

Payload:

js
{ offset?: number = 0, limit?: number = 10 }

Відповідь у колбеку:

js
{
  items: [{ id, title, artist, orderType }],
  total: 42,   // усього треків попереду
}

Відповідь приходить у колбеку. Поточний трек у вибірку не входить.

guest_check_ban ​

guest

Чи заблоковано цю IP-адресу для участі в ефірі.

Payload: без даних

Відповідь у колбеку:

js
{ banned: boolean }

Варто викликати до показу форми заявки, щоб не давати марних надій.

guest_connect ​

guest

Під’єднання гостя до ефіру після схвалення.

Payload: без даних

Відповідь у колбеку:

js
{ ok: true, role: '…', nickname: '…', expiresAt: 1754899200000 }

Спрацює лише якщо сесію вже підтверджено ведучим і в ефірі є вільне місце. expiresAt — час примусового завершення; тривалість задається в налаштуваннях радіо. Далі гість користується тими самими подіями мікрофона, що й ведучий.

guest_leave_live ​

guest

Гість залишає ефір.

Payload: без аргументів

guest_request ​

guest

Заявка слухача на участь в ефірі.

Payload:

js
{ nickname: string }

Відповідь у колбеку:

js
{ 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:

js
{ offset?: number = 0, limit?: number = 5 }

Відповідь у колбеку:

js
{ items: [ … ], total: 12, mode: 'day' }

Список залежить від поточного режиму радіо. limit обмежений 50. Не-ведучому повертається порожній список.

host_guest_kick ​

host

Вилучити гостя з ефіру.

Payload:

js
{ targetId: string }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

host_guest_mute ​

host

Вимкнути мікрофон гостю.

Payload:

js
{ targetId: string, muted: boolean }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

host_mic_gain ​

host

Змінює гучність свого мікрофона в міксі.

Payload:

js
{ gain: number }

host_mic_toggle ​

host

Вмикає або вимикає мікрофон у ефірі.

Payload:

js
{ on: boolean }

Доки мікрофон вимкнено, сервер відкидає аудіо-фрагменти від цього сокета. Працює і для ведучих, і для гостей.

host_pause_queue ​

host

Ставить чергу треків на паузу — режим «просто розмова».

Payload: без аргументів

Доступно лише ведучому, не гостю. Тишу заповнює фонова музика, а в події sync у всіх слухачів title стає "Just chatting".

host_resume_queue ​

host

Повертає програвання черги.

Payload: без аргументів

host_set_background_music ​

host

Обирає трек, який гратиме під час паузи черги.

Payload:

js
{ trackId: string | null }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

null повертає випадковий вибір. Має сенс лише коли backgroundMusicMode дорівнює hostChoice.

listener_init ​

listener

Реєструє з’єднання як слухача і просить видати art-токен.

Payload: без аргументів

Сервер не видає токен сам — поки клієнт не надішле цю подію, у відповідь не прийде listener_uid, і доступ до артів, текстів та аудіо буде закритий. Адмінські з’єднання цю подію ігнорують.

moderator_ban_ip ​

admin

Заблокувати IP-адресу вручну.

Payload:

js
{ ip: string, nickname?: string }

Відповідь у колбеку:

js
{ ok: true, entry: { … } } або { error: … }

moderator_ban_participant ​

admin

Заблокувати учасника ефіру за IP.

Payload:

js
{ targetId: string }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

Працює лише для гостей: заблокувати ведучого не можна. Потребує SQL-провайдера.

moderator_deactivate_guest_code ​

admin

Вимкнути чинний код доступу.

Payload: без даних

Відповідь у колбеку:

js
{ ok: true }

moderator_generate_guest_code ​

admin

Створити код доступу для спецгостя.

Payload:

js
{ ttlHours?: number }

Відповідь у колбеку:

js
{ ok: true, code: "…", expiresAt: 1754899200000 }

Якщо активний код уже є, повертається помилка — спершу вимкніть його або скористайтеся moderator_regenerate_guest_code. Код дозволяє зайти в ефір без черги через special_guest_connect.

moderator_get_banlist ​

admin

Список заблокованих IP-адрес.

Payload:

js
{ offset?: number = 0, limit?: number = 10 }

Відповідь у колбеку:

js
{ list: [ … ], total: 7 }

Потребує DATA_PROVIDER=sql — інакше список недоступний.

moderator_get_guest_code ​

admin

Поточний код доступу для спецгостя.

Payload: без даних

Відповідь у колбеку:

js
{ code: "…" } — або { code: null }, якщо активного коду немає

moderator_get_live_roster ​

admin

Хто зараз в ефірі.

Payload: без даних

Відповідь у колбеку:

js
{ roster: [ … ], hostsOnline: true }

Без привілею повертається порожній склад, а не помилка.

moderator_kick ​

admin

Вилучити учасника з ефіру.

Payload:

js
{ targetId: string }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

Учасник отримає host_force_disconnect або guest_force_disconnect.

moderator_mute ​

admin

Вимкнути мікрофон будь-якому учаснику ефіру.

Payload:

js
{ targetId: string, muted: boolean }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

На відміну від host_guest_mute, діє і на ведучих.

moderator_regenerate_guest_code ​

admin

Замінити чинний код доступу на новий.

Payload:

js
{ ttlHours?: number }

Відповідь у колбеку:

js
{ ok: true, code: "…", expiresAt: … }

Старий код одразу перестає діяти.

moderator_unban_ip ​

admin

Зняти блокування з IP-адреси.

Payload:

js
{ ip: string }

Відповідь у колбеку:

js
{ ok: true } або { error: … }

monitor_answer ​

host

WebRTC-відповідь для персонального моніторингу ведучого.

Payload:

js
{ sdp: … }

Особистий канал прослуховування ефіру з мінімальною затримкою — окремо від спільного MP3-потоку, який іде із запізненням. Потребує відкритих UDP-портів HOST_MONITOR_ICE_PORT_MIN–MAX.

monitor_ice_candidate ​

host

ICE-кандидат для каналу моніторингу.

Payload: об’єкт кандидата WebRTC

search_queue ​

admin

Пошук по майбутній черзі.

Payload:

js
{ query?: string }

Відповідь у колбеку:

js
[{ id, title, artist, orderType, position }]

position — це зсув від поточного треку, і саме його очікує admin_remove_song. Порожній запит повертає всю чергу.

special_guest_connect ​

guest

Вхід в ефір за одноразовим кодом, без черги.

Payload:

js
{ code: string, nickname: string }

Відповідь у колбеку:

js
{ ok: true, … } або { error: … }

Код видає модератор. Кількість спроб обмежена; протухлий або вимкнений код дає помилку.

stream_get_seek ​

listener

Запитує поточну позицію спільного потоку (лише stream-режим).

Payload: clientTs: number — локальний час клієнта

Відповідь у колбеку:

js
{ seek: 87.2, duration: 214.5, trackId: 'day/artist - title.mp3' }

Потрібно лише щоб показати таймер в інтерфейсі: саме звучання у stream-режимі синхронне за побудовою. Поза stream-режимом сервер не відповідає взагалі.

stream_ping ​

listener

Вимірює затримку до сервера.

Payload: clientTs: number

Відповідь у колбеку:

js
{ serverTs: number, clientTs: number }

Сервер повертає ваш clientTs без змін — різниця часу туди й назад дає оцінку затримки для точнішого позиціювання.

suggest_song ​

listener

Замовити пісню з бібліотеки в ефір.

Payload:

js
// об’єкт пісні з GET /api/library
{ id: 'day/artist - title.mp3', title: 'Title', artist: 'Artist' }

Відповідь у колбеку:

js
{ 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:

js
{
  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:

js
{ trackId: '…', filename: '…' }   // обидва null, коли фонова музика зупинена

background_music_selection_changed ​

host

Ведучий обрав інший фоновий трек.

Синхронізує вибір між кількома ведучими в ефірі, щоб усі бачили те саме.

background_music_updated ​

admin

Бібліотеку фонової музики змінено.

Сигнал перечитати список через host_get_background_music_list.

donation_result ​

listener

Підсумок донат-замовлення: пісню додано до черги чи ні.

Payload:

js
{ 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:

js
{ 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:

js
{ 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:

js
{ 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:

js
{
  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:

js
{ 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:

js
{ serverTs: … }

Дублює те, що видно в sync за title: "Just chatting", але приходить миттєво, а не в межах двох секунд.

stream_jingle_end ​

listener

Джингл завершився.

stream_jingle_start ​

listener

Почався джингл між піснями.

Payload:

js
{ jingleId: '…', serverTs: … }

Час сховати метадані треку: зараз звучить не пісня.

stream_track_start ​

listener

У спільному потоці почався новий трек.

Payload:

js
{ trackId: '…', duration: 214.5, serverTs: 1754899200000 }

serverTs — момент початку за годинником сервера. Разом із власним вимірюванням затримки через stream_ping це дає точну позицію в треку.

suggest_cooldown_update ​

listener

Скільки секунд лишилось до можливості замовити наступну пісню.

Payload: number — секунди, 0 якщо можна замовляти

Надсилається всім з’єднанням з тим самим uid, тож кілька вкладок бачать однаковий відлік.

suggestion_result ​

listener

Рішення адміна щодо вашого замовлення.

Payload:

js
{ accepted: true, auto: false, song: { title, artist, … } }

auto: true означає, що замовлення згасло за таймером, а не було відхилене вручну. Приходить лише тому сокету, що замовляв.

suggestions_update ​

admin

Поточний список замовлень від слухачів.

Payload:

js
[{ uid: '…', song: { id, title, artist }, addedAt: 1754899200000 }]

Надсилається лише адмінам з привілеєм queue_manage — при вході, при кожній зміні списку та при переході на денний режим, коли всі замовлення скидаються.

sync ​

listener

Повний стан радіо. Основна подія протоколу.

Payload:

js
{
  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:

js
[{ name: 'Ім’я', img: 'file.png', color: '#a3f01c' }]

Слухачі анонімні: сервер видає кожному з’єднанню випадкове ім’я з набору «олігархів» і випадковий колір. Ім’я приходить уже локалізованим.