Streaming platform for a particular irc channel
Find a file
2026-09-14 13:45:57 -04:00
app Initial commit 2026-09-14 13:45:57 -04:00
docker Initial commit 2026-09-14 13:45:57 -04:00
.gitignore Initial commit 2026-09-14 13:45:57 -04:00
pyproject.toml Initial commit 2026-09-14 13:45:57 -04:00
README.md Initial commit 2026-09-14 13:45:57 -04:00
uv.lock Initial commit 2026-09-14 13:45:57 -04:00
wsgi.py Initial commit 2026-09-14 13:45:57 -04:00

zoitestream

Landing page + lightweight API for stream.dumpnet.chat, backed by MediaMTX.

This is intentionally tiny: a single Flask process with a SQLite database (on a persistent volume, not container-local disk) tracking the currently active stream and stream history. MediaMTX calls this service's /hooks/* endpoints on stream start/stop (via its runOnReady/runOnNotReady path hooks), and the landing page polls /api/active and /api/history to render status. An IRC bot can call /api/creds to fetch the current publish URL to hand out to whoever wants to stream.

State persists across pod restarts/redeploys (SQLite file lives on the shared appdata hostPath PV, mounted at STATE_DB_PATH).

Design notes

  • One shared publish stream/password for everyone (a deliberate feature, not a limitation) — credentials are retrievable via /api/creds (meant for the IRC bot to call), never shown on the public landing page. The IRC channel is a high-trust environment, so this endpoint is only lightly protected (shared token), not treated as a hard secret boundary.
  • /hooks/ready and /hooks/not-ready are protected by a separate shared token (HOOK_TOKEN) so MediaMTX is the only thing that can flip stream status — rotatable independently of the creds token.
  • Publish password itself lives in MediaMTX's own internal auth config (authInternalUsers), not validated by zoitestream — zoitestream just knows the current value (via PUBLISH_PASSWORD env var, sourced from the same secret) so it can hand it back out through /api/creds.
  • No scheduling API yet — planned as a future addition, likely driven by an IRC bot, and will live in the same SQLite database.

Endpoints

  • GET / — landing page
  • GET /api/active — JSON list of currently active streams
  • GET /api/history — JSON list of recent streams (capped, most-recent-first)
  • GET /api/creds — publish URL/user/password (token-protected via CREDS_TOKEN)
  • GET /health — health check
  • POST /hooks/ready — called by MediaMTX runOnReady (form/query: path, source_type)
  • POST /hooks/not-ready — called by MediaMTX runOnNotReady (form/query: path)

Env vars

  • HOOK_TOKEN — shared secret required (as ?token= or X-Hook-Token header) on /hooks/*
  • CREDS_TOKEN — shared secret required (as ?token= or X-Creds-Token header) on /api/creds
  • PUBLISH_USER — publish username to report via /api/creds (default streamer)
  • PUBLISH_PASSWORD — publish password to report via /api/creds — must match MediaMTX's own config
  • DOMAIN — display domain shown on the landing page and in /api/creds (default stream.dumpnet.chat)
  • RTMP_PORT — RTMP port used in the URL built by /api/creds (default 1935)
  • STATE_DB_PATH — path to the SQLite file (default /data/zoitestream.db)
  • HISTORY_LIMIT — number of history entries to retain (default 50)

Local dev

uv sync
STATE_DB_PATH=./dev.db uv run flask --app wsgi:app run --debug