VIDEO_ENABLED makes the bot publish a second LiveKit track with the picture. Stoat only grants screen_share in the call token when the bot has the Video permission and the instance has video enabled, so a refusal is reported in chat and playback continues with sound alone. Two constraints shaped the pipeline, both found by testing rather than assumption: - Only progressive formats can be streamed. Separate video+audio streams make yt-dlp download both in full before muxing a single byte, and direct CDN URLs handed to ffmpeg simply hang — YouTube no longer serves them to other clients. That caps video at the 360p single file YouTube offers, and the format is checked before committing to the video path, since audio would otherwise come from the same broken pipeline. - One ffmpeg with two outputs, paced by -re: an unpaced decode races ahead of the sound and eats memory at 1.4 MB per frame. The same CDN-URL finding removes the audio seek shortcut, which resolved such a URL and would have hung the same way; seeking now decodes up to the offset like it already did behind a proxy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
112 lines
6.5 KiB
Bash
112 lines
6.5 KiB
Bash
# ---------------------------------------------------------------- Stoat ----
|
|
# Публичный адрес API вашего инстанса (тот же, что в клиенте).
|
|
# Бот ходит по нему как обычный клиент; домен резолвится в хост через
|
|
# extra_hosts в compose.yml, поэтому TLS валидный и NAT loopback не нужен.
|
|
STOAT_API_URL=https://chat.example.com/api
|
|
|
|
# Домен вашего Stoat — тот же, что в STOAT_API_URL, без схемы.
|
|
# Используется в compose.yml: контейнер резолвит его в сам хост, где внешний
|
|
# Caddy держит валидный TLS. Сам compose.yml править не нужно.
|
|
STOAT_DOMAIN=chat.example.com
|
|
|
|
# Токен бота: Settings → My Bots → создать бота → скопировать токен.
|
|
STOAT_BOT_TOKEN=
|
|
|
|
# Префикс команд в чате.
|
|
COMMAND_PREFIX=!
|
|
|
|
# Удалять сообщение с командой после её распознавания (нужно право ManageMessages).
|
|
DELETE_COMMAND_MESSAGES=true
|
|
|
|
# Имя LiveKit-ноды из Revolt.toml, секция [hosts.livekit].
|
|
# В стандартном self-hosted это "worldwide".
|
|
VOICE_NODE=worldwide
|
|
|
|
# ------------------------------------------------------------- Веб-панель ---
|
|
PORT=3005
|
|
HOST=0.0.0.0
|
|
|
|
# Адрес, по которому панель открывается в браузере (без слэша в конце).
|
|
PUBLIC_URL=https://music.example.com
|
|
|
|
# Секрет для подписи сессионных cookie. Сгенерируйте: openssl rand -hex 32
|
|
JWT_SECRET=
|
|
|
|
# Срок жизни сессии панели, часов.
|
|
SESSION_TTL_HOURS=168
|
|
|
|
# ---------------------------------------------------------------- Аудио ----
|
|
# Путь к yt-dlp. В docker-образе он уже установлен.
|
|
YTDLP_PATH=yt-dlp
|
|
|
|
# Необязательно: cookies.txt аккаунта YouTube — снимает возрастные ограничения,
|
|
# «Sign in to confirm you're not a bot» и открывает приватные/платные видео.
|
|
# Файл должен быть доступен на ЗАПИСЬ: yt-dlp обновляет в нём ротируемые куки.
|
|
# Подробности — в README, раздел «Учётка YouTube (cookies)».
|
|
# YTDLP_COOKIES=/data/cookies.txt
|
|
|
|
# Необязательно: прокси для всех запросов yt-dlp — YouTube и SoundCloud,
|
|
# поиск, метаданные и сам аудиопоток. Пригодится, когда из сети сервера
|
|
# YouTube недоступен (Psiphon, свой SOCKS и т.п.).
|
|
# Схемы: http://, https://, socks5:// и socks5h:// (socks5h резолвит DNS на
|
|
# стороне прокси — обычно нужен именно он). Можно с логином и паролем:
|
|
# socks5h://user:pass@host:1080
|
|
# YTDLP_PROXY=socks5h://127.0.0.1:1080
|
|
|
|
# Где применять прокси: all — везде (по умолчанию), search — только поиск и
|
|
# метаданные, а сам аудиопоток качать напрямую. Второй вариант выручает, когда
|
|
# выходной IP прокси ловит от YouTube «Sign in to confirm you're not a bot»,
|
|
# а без прокси скачивание работает.
|
|
# YTDLP_PROXY_SCOPE=search
|
|
#
|
|
# Внимание: 127.0.0.1 внутри контейнера — это сам контейнер. Если прокси поднят
|
|
# на хосте, используйте socks5h://host.docker.internal:1080 и добавьте в
|
|
# compose.yml к extra_hosts строку "host.docker.internal:host-gateway".
|
|
|
|
# JS-рантайм для yt-dlp: YouTube требует исполнять свой JS, иначе извлечение
|
|
# деградирует и начинаются бот-проверки. В образе уже есть Node, поэтому по
|
|
# умолчанию используется он. Пустое значение отключает флаг.
|
|
YTDLP_JS_RUNTIME=node
|
|
|
|
# Необязательно: дополнительные --extractor-args, через ";".
|
|
# Помогает, когда YouTube не отдаёт форматы серверному IP:
|
|
# YTDLP_EXTRACTOR_ARGS=youtube:player_client=default,web_safari
|
|
|
|
# Необязательно: каталог с локальной медиатекой (смонтируйте том).
|
|
# LOCAL_MEDIA_DIR=/media/music
|
|
|
|
# Показывать клип в голосовом канале как демонстрацию экрана (только YouTube).
|
|
# Требует прав Video у бота и включённого видео в конфигурации инстанса.
|
|
# YouTube отдаёт одним файлом только 360p, а стримить можно лишь такой формат —
|
|
# отсюда размер по умолчанию. Кодирование видео заметно грузит CPU сервера.
|
|
VIDEO_ENABLED=false
|
|
VIDEO_WIDTH=640
|
|
VIDEO_HEIGHT=360
|
|
VIDEO_FPS=24
|
|
|
|
DEFAULT_VOLUME=60
|
|
MAX_QUEUE_SIZE=500
|
|
|
|
# Сколько треков максимум добавит одна ссылка на плейлист или микс.
|
|
MAX_PLAYLIST_TRACKS=100
|
|
SEARCH_RESULT_LIMIT=10
|
|
|
|
# Через сколько секунд после ухода ПОСЛЕДНЕГО человека бот покидает голосовой
|
|
# канал (0 — не выходить никогда). Пока в канале кто-то есть, бот остаётся,
|
|
# даже если очередь давно закончилась.
|
|
EMPTY_TIMEOUT_SECONDS=120
|
|
|
|
# --------------------------------------------------------------- Доступ ----
|
|
# false — управлять может любой участник сервера.
|
|
# true — только владелец, ManageServer или роль DJ_ROLE_NAME.
|
|
REQUIRE_DJ_ROLE=false
|
|
DJ_ROLE_NAME=DJ
|
|
|
|
# true — позвать бота можно только в тот голосовой канал, где вы сами находитесь
|
|
# (и панель, и команды). false — разрешить выбирать канал вручную.
|
|
REQUIRE_LISTENER=true
|
|
|
|
# ----------------------------------------------------------------- Прочее ---
|
|
LOG_LEVEL=info
|
|
NODE_ENV=production
|