60 lines
3 KiB
Markdown
60 lines
3 KiB
Markdown
# zoitestream
|
|
|
|
Landing page + lightweight API for `stream.dumpnet.chat`, backed by
|
|
[MediaMTX](https://github.com/bluenviron/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
|
|
```
|