presentation-display
Сервис для профессионального показа презентаций на конференциях. Управляет отображением контента на нескольких экранах (проектор, экран докладчика) и интегрируется с reception-conference по WebSocket-протоколу.
Быстрый старт
# Сборка
go build -o display ./cmd/display
# Запуск (флаги)
./display \
--reception-base-url=https://reception.example.com \
--service-token=<hex-token> \
--venue-id=<uuid-зала> \
--operator-password=<пароль-пульта>
# или через .env
export RECEPTION_BASE_URL=https://reception.example.com
export SERVICE_TOKEN=<hex-token>
export VENUE_ID=<uuid-зала>
export OPERATOR_PASSWORD=<пароль-пульта>
./display
Открыть в браузере:
http://localhost:3000/projector/ — проектор
http://localhost:3000/speaker/ — экран докладчика
http://localhost:3000/operator/ — пульт оператора
Звук в BREAK: браузер блокирует автоматическое воспроизведение со звуком без действия пользователя.
Чтобы включить звук, достаточно один раз кликнуть по странице проектора или экрана докладчика.
При загрузке страницы видео запускается с нулевой громкостью, после клика громкость восстанавливается.
Конфигурация
Обязательные параметры
| Флаг |
Переменная окружения |
Описание |
--reception-base-url |
RECEPTION_BASE_URL |
Адрес сервера reception-conference |
--service-token |
SERVICE_TOKEN |
ServiceToken для аутентификации (hex) |
--venue-id |
VENUE_ID |
UUID зала |
--operator-password |
OPERATOR_PASSWORD |
Пароль для доступа к пульту оператора |
Опциональные параметры
| Флаг |
Переменная окружения |
По умолчанию |
Описание |
--prefix |
PREFIX |
"" |
URL path prefix (например /display) |
--listen-addr |
LISTEN_ADDR |
:3000 |
Адрес и порт HTTP-сервера |
--reconnect-interval |
RECONNECT_INTERVAL |
5 |
Интервал реконнекта к RC (сек) |
--max-retries |
MAX_RETRIES |
0 |
Лимит попыток реконнекта к RC (0 = без лимита) |
--file-retry-interval |
FILE_RETRY_INTERVAL |
5 |
Интервал повторной загрузки ZIP (сек) |
--cleanup-delay |
CLEANUP_DELAY |
30 |
Задержка удаления показанных слайдов (мин) |
--default-duration |
DEFAULT_DURATION |
15 |
Длительность доклада по умолчанию (мин) |
--break-image-duration |
BREAK_IMAGE_DURATION |
10 |
Длительность показа картинки в режиме BREAK (сек) |
--transition-mode |
TRANSITION_MODE |
instant |
Режим переходов: instant / crossfade |
--crossfade-duration |
CROSSFADE_DURATION |
300 |
Длительность crossfade (мс) |
--log-level |
LOG_LEVEL |
off |
Уровень логирования: off / debug / info / warn / error |
--log-file |
LOG_FILE |
"" |
Путь к лог-файлу (stderr если пусто) |
--data-dir |
DATA_DIR |
./data |
Корневая папка для данных |
--config |
CONFIG_FILE |
"" |
Путь к YAML/JSON файлу конфигурации |
Приоритет
CLI-флаги > переменные окружения > config-файл > значения по умолчанию.
HTTP-эндпоинты
WebSocket
| Метод |
Путь |
Описание |
GET |
/ws?role={role} |
WebSocket-хаб для клиентов (projector / speaker / operator) |
Статика
| Метод |
Путь |
Описание |
GET |
/projector/ |
Клиент проектора (vanilla JS) |
GET |
/speaker/ |
Экран докладчика (vanilla JS) |
GET |
/operator/ |
Пульт оператора (Angular) |
GET |
/sw.js |
Service Worker |
Слайды
| Метод |
Путь |
Описание |
GET |
/slides/{participant_id}/{filename} |
Раздача изображений слайдов |
Логотип
| Метод |
Путь |
Описание |
GET |
/logo |
Показ загруженного логотипа |
POST |
/api/logo |
Загрузка логотипа (multipart, поле file) |
Джинглы (BREAK)
| Метод |
Путь |
Описание |
GET |
/breaks/{filename} |
Раздача файлов джинглов |
POST |
/api/breaks/upload |
Загрузка джингла (multipart, поле file) |
GET |
/api/breaks/list |
Список джинглов |
DELETE |
/api/breaks/{filename} |
Удаление джингла |
POST |
/api/breaks/reorder |
Изменение порядка плейлиста (JSON-массив имён) |
GET |
/api/breaks/volume |
Получить громкость |
POST |
/api/breaks/volume |
Установить громкость ({"volume": 50}) |
Таймер
| Метод |
Путь |
Описание |
GET |
/api/timer/template |
Получить активный HTML-шаблон таймера |
GET |
/api/timer/template/list |
Список загруженных шаблонов |
POST |
/api/timer/template/upload |
Загрузить HTML-шаблон (multipart, поле file) |
POST |
/api/timer/template/select |
Выбрать активный шаблон ({"name": "..."}) |
Preview
| Метод |
Путь |
Описание |
GET |
/preview/{participant_id}?base_url=...&token=... |
Предпросмотр слайдов участника |
Мониторинг
| Метод |
Путь |
Описание |
GET |
/health |
Health-check (ok) |
GET |
/metrics |
Prometheus-метрики |
URL Prefix
Все маршруты монтируются под опциональный префикс (--prefix). Например, при --prefix=/display:
/display/ws → WS-хаб
/display/operator/ → пульт оператора
Клиенты получают префикс через runtime-подстановку в HTML: {{PREFIX}}, {{WS_URL}}, {{BASE_HREF}}.
Метрики Prometheus
Доступны по /metrics:
| Метрика |
Тип |
Описание |
display_clients_connected |
Gauge |
Клиенты по ролям (label: role) |
display_current_mode |
Gauge |
0=IDLE, 1=BREAK, 2=PRESENTATION |
display_slides_total |
Counter |
Всего показано слайдов |
display_break_duration_seconds |
Counter |
Время в BREAK |
display_presentation_duration_seconds |
Counter |
Время в PRESENTATION |
display_errors_total |
Counter |
Ошибки по кодам (label: code) |
Протокол с reception-conference
Подключение
GET /api/v1/display/ws
Authorization: Bearer <service-token>
После upgrade сервер отправляет welcome:
{"type":"welcome","venue_id":"...","current_state":{"mode":"idle","participant_id":"...","name":"...","topic":"...","duration_minutes":15}}
reception не отправляет current_slide, total_slides, timer_remaining,
slides и другие локально управляемые поля — display хранит их самостоятельно.
Команды от RC
| Тип |
Описание |
presentation_start |
Начать доклад (participant_id, name, topic, duration_minutes). total_slides не приходит — display берёт из ZIP |
presentation_stop |
Остановить доклад |
queue_next |
Предзагрузить следующего участника (participant_id, name, topic, duration_minutes) |
pong |
Ответ на ping |
Команды от display
| Тип |
Описание |
ping |
Keepalive (каждые 30 сек) |
error |
Ошибка выполнения (code, message) |
Внутренний WebSocket-протокол (хаб ↔ клиенты)
Подключение
GET {prefix}/ws?role=projector|speaker|operator
После upgrade сервер отправляет current_state с полным состоянием.
Команды сервера → клиент
| Тип |
Описание |
current_state |
Текущее состояние (mode, слайд, таймер, плейлист) |
presentation_started |
Начат доклад (participant_id, name, topic, total_slides, slide_base_url) |
presentation_stopped |
Доклад завершён |
slide_changed |
Смена слайда (current_slide, total_slides, annotation) |
show_idle |
Показать IDLE-заставку |
show_break |
Показать BREAK-плейлист |
play_break |
Воспроизвести конкретный файл из BREAK-плейлиста (filename, url, media_type, volume, duration_sec) |
volume_changed |
Изменение громкости (volume) |
logo_changed |
Логотип обновлён — перезапросить /logo |
timer_update |
Обновление таймера (remaining_seconds, paused) |
display_error |
Ошибка (code, message) |
Команды клиент → сервер
| Тип |
Описание |
auth |
Аутентификация оператора ({"password":"..."}) |
next_slide |
Следующий слайд |
prev_slide |
Предыдущий слайд |
set_mode |
Смена режима ({"mode":"idle"|"break"}) |
pause_timer |
Пауза/возобновление таймера ({"paused":true|false}) |
break_ended |
Уведомление, что воспроизведение брейка завершено ({"filename":"..."}) |
set_volume |
Установка громкости ({"volume":50}) |
Сборка
# Сервер
go build -o display ./cmd/display
# Angular-клиент оператора
cd web/operator && npm i && npm run build
Тестирование
go test ./...
go test -tags=integration ./...
Docker
docker build -t display .
docker run -p 3000:3000 \
-e RECEPTION_BASE_URL=... \
-e SERVICE_TOKEN=... \
-e VENUE_ID=... \
-e OPERATOR_PASSWORD=... \
display
Смотрите также: