TuxPlayer provides one permanent MP3 stream for Twitch audio, so Music Assistant only needs a single stable URL:
http://SERVER_IP:8766/stream/
If no channel is selected, the selected channel is offline, or Streamlink/FFmpeg fails, TuxPlayer serves silence instead of returning 404. The project is designed for local Docker deployment with a simple admin UI.
Audio flow:
Twitch -> Streamlink -> FFmpeg decode -> TuxPlayer PCM/MP3 pipeline -> /stream/ -> Music Assistant
Only one Twitch source is active at a time, while all listeners share the same output stream.
- Permanent
/stream/endpoint withaudio/mpeg - Silence fallback instead of
404 - One shared Streamlink/FFmpeg pipeline for all listeners
- Idle timeout that stops the Twitch source when nobody is listening
- SQLite storage for channels and settings in
./data/tuxplayer.db - Danish admin UI with large touch-friendly controls
- Volume slider in the UI
- Optional Twitch API integration via
TWITCH_CLIENT_IDandTWITCH_CLIENT_SECRET - Optional HTTP Basic Auth for the UI and mutating API endpoints
- Docker
- Docker Compose
- A host where
network_mode: bridgeis allowed
cd /docker_data/tuxplayer
cp .env.example .env
nano .env
docker compose up -d --buildFor a guided setup, see INSTALL.md or run:
chmod +x install.sh
./install.shinstall.sh requires Bash.
Pressing Ctrl+C, sending SIGTERM, or ending stdin cancels the installer safely.
- UI: http://SERVER_IP:8766
- Health endpoint:
http://SERVER_IP:8766/health - Status API:
http://SERVER_IP:8766/api/status - Permanent stream:
http://SERVER_IP:8766/stream/
Add only this single URL:
http://SERVER_IP:8766/stream/
You do not need one URL per DJ or channel.
The project uses .env. Start from .env.example.
Important variables:
TZ=Europe/CopenhagenPUBLIC_BASE_URL=http://SERVER_IP:8766STREAM_IDLE_TIMEOUT=30STREAM_BITRATE=160kSTREAM_SAMPLE_RATE=44100STREAM_VOLUME=1.8STREAM_CHUNK_MS=50SUBSCRIBER_QUEUE_SIZE=24STREAMLINK_LIVE_EDGE=3STREAMLINK_QUALITY=bestTWITCH_CLIENT_ID=TWITCH_CLIENT_SECRET=ADMIN_USERNAME=ADMIN_PASSWORD=LOG_LEVEL=INFO
Leave both ADMIN_USERNAME and ADMIN_PASSWORD empty if you intentionally want to disable admin login. When both are set, the admin UI and protected endpoints require Basic Auth.
Passwords may include leading or trailing spaces, but a password that consists only of whitespace is rejected.
- TuxPlayer is primarily intended for use on a trusted local network.
- Port
8766should not be exposed directly to the public internet. - Configure
ADMIN_USERNAMEandADMIN_PASSWORDbefore using the service outside a private LAN. ADMIN_USERNAMEandADMIN_PASSWORDmust either both be set or both be empty.- A half-configured admin login is treated as an error and prevents startup.
- Use HTTPS and a reverse proxy if external access is required.
- The
/stream/endpoint is intentionally public on the local TuxPlayer instance so Music Assistant can consume it without login prompts. - Logs and mutating API endpoints require admin login when admin authentication is configured.
If TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET are set, TuxPlayer uses the Twitch API for live status, title, viewer count, and profile image data.
The Twitch OAuth token request is sent as form-encoded POST data. Secrets should never appear in request URLs or logs.
The installer writes dotenv values using Docker Compose-compatible single-quoted syntax. $, ${VAR}, spaces, backslashes, single quotes, double quotes, and leading or trailing spaces in secrets and passwords are preserved and verified through the same env_file mechanism that the real docker-compose.yml uses.
If they are empty, the system still works, but the UI will typically show unknown until playback is attempted or Streamlink returns an error.
Volume can be adjusted directly in the UI using the built-in slider.
If you want to tune behavior manually in .env, the most relevant values are:
STREAM_VOLUME=1.8for overall loudnessSTREAM_CHUNK_MS=50for pipeline chunk sizeSUBSCRIBER_QUEUE_SIZE=24for client bufferingSTREAMLINK_LIVE_EDGE=3for the stability/latency balanceSTREAMLINK_QUALITY=bestfor a more compatible Twitch source selection
GET /admin panelGET /stream/permanent MP3 streamGET /streamredirect to/stream/GET /healthhealthcheckGET /api/statusstream and source statusGET /api/channelslist channelsPOST /api/channelscreate channelPUT/PATCH /api/channels/<id>update channelDELETE /api/channels/<id>delete channelPOST /api/channels/<id>/selectselect active channelPOST /api/channels/<id>/favoritetoggle favorite statusPOST /api/channels/<id>/testtest channelPOST /api/stream/stopstop the Twitch sourcePOST /api/stream/restartrestart the Twitch sourceGET /api/logsread recent application logs
Local checks:
pytest
docker compose config
docker compose buildSimple runtime checks:
curl http://127.0.0.1:8766/health
curl http://127.0.0.1:8766/api/statusdocker compose ps
docker compose logs -f
docker stats tuxplayerCommon things to verify:
- the selected Twitch channel is actually live
streamlinkcan open the channel from the serverPUBLIC_BASE_URLpoints to the correct host and port.envtuning values are not too aggressive
The database is stored in:
./data/tuxplayer.db
Back up that file if you want to preserve channels and settings.
docker compose down
docker compose build --no-cache
docker compose up -dThis project is licensed under the TuxPlayer No-Resale License. See LICENSE.
.envis ignored- SQLite database files in
data/are ignored - local Codex/test artifacts are ignored
app/static/banner.pngis used at the top of this README and in the web UIinstall.shwrites.envwith0600permissionsinstall.shsupports passwords and secrets containing$,${VAR}, spaces, quotes, and backslashesinstall.shpreserves an existing.envon cancellation or input failure and cleans up temporary.env.tmp.*files- installer tests find Bash dynamically and do not rely on local machine paths
tuxplayer/
├── app/
│ ├── static/
│ └── templates/
├── data/
├── tests/
├── .dockerignore
├── .env.example
├── .gitignore
├── docker-compose.yml
├── Dockerfile
├── install.sh
├── INSTALL.md
├── LICENSE
├── README.md
└── requirements.txt
