Your streaming Swiss Army knife 🎬
One dashboard to control everything - OBS, Twitch, alerts, restream and your remote stream PC.
- Overview
- Features
- Architecture
- Quick Start
- Prerequisites
- Installation
- Configuration
- Running the Services
- Using the Web UI
- Restream Management
- Chat Commands
- Bitrate & Raid Automation
- Twitch Integration & Tokens
- Alert Sound
- Logs Viewer
- Health Indicators
- Security Recommendations
- Ip Banning
- Troubleshooting
- Extending
- Contributing
Ever tried managing a stream while your internet decides to have a bad day? Stream Control has your back.
In simple terms: A web dashboard that keeps your Twitch stream running smoothly - even when your connection isn't. It automatically switches to a "low bitrate" scene when your internet struggles, manages your restream destinations, and lets you control everything from one place.
Perfect for:
- 📡 Streamers with unstable internet
- 🎮 Mobile/IRL streamers using BELABOX or similar encoders
- 🔧 Anyone who wants full control without clicking through 5 different apps
- 🌍 Multi-platform streamers managing multiple restream endpoints
- One-click OBS control - Start/stop streaming, switch scenes
- Auto scene switching - Detects low bitrate and switches to fallback scene automatically
- Twitch integration - Update title/category, trigger raids, view live chat
- Smart alerts - Audio notifications when things go wrong (or right!)
- Alerts from StreamElements - Get notified with your alert sound when triggered by dono/subs etc
- Restream manager - Add/edit/enable/disable all your streaming destinations
- BELABOX integration - Direct control of your mobile streaming setup
- Auto-reload - Changes to restream config automatically reload nginx
- Wake-on-LAN - Turn on your streaming PC from anywhere
- Power management - Restart or shutdown your remote stream PC
- Health monitoring - See status of all services at a glance
- Auto raid-stop - Automatically end stream after raiding another channel
- Token refresh - Twitch tokens refresh automatically, no manual intervention
- Chat commands - Control your stream via Twitch chat (!start, !live, !brb, !stop)
- Bitrate monitoring - Constantly checks your connection quality
- Password protected - Secure login with hashed passwords
- IP banning - Automatic protection against brute force attempts
- Production-ready - Runs with Gunicorn behind Cloudflare Tunnel
| Component | Role |
|---|---|
app.py |
UI endpoints, token refresh, restream config generation, alerts broadcast |
stream_guard.py |
Bitrate/scene logic, raid EventSub, health server |
static/main.js |
UI interactions, polling, modals, toasts |
templates/ |
HTML + nginx Jinja2 template |
twitch_tokens.json |
Access + refresh token store (rotated automatically) |
Processes are decoupled for resilience.
Get up and running in 5 minutes:
# 1. Clone and setup
git clone https://github.com/Kimsec/Stream-Control.git
cd Stream-Control
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# 2. Configure
cp .env.example .env
nano .env # Add your credentials
# 3. Generate secure password
python generate_password_hash.py
# Add the hash to .env as LOGIN_PASSWORD_HASH
# 4. Run (development)
python app.py --dev
python stream_guard.pyOpen http://localhost:5000 and start streaming! 🎉
Required:
- 🐍 Python 3.10 or newer
- 🎥 OBS Studio
- 🔑 Twitch API credentials (get them here)
Optional (but recommended):
- 📡 nginx with RTMP module (for restreaming to multiple platforms)
- 📊 Stats endpoint (SRS/SLS/nginx stats for bitrate monitoring)
- 🔒 stunnel (for RTMPS support)
- ⚙️ systemd (for running as a service)
git clone https://github.com/Kimsec/Stream-Control.git
cd Stream-Control
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .envSee DEPLOYMENT.md for a complete production setup guide with:
- ✅ Gunicorn with threaded workers
- ✅ Systemd service installation
- ✅ Secure password hashing
- ✅ Cloudflare Tunnel integration
- ✅ Log rotation and monitoring
Key groups in .env:
| Group | Examples |
|---|---|
| Flask/Auth | FLASK_SECRET_KEY, LOGIN_PASSWORD_HASH (use python generate_password_hash.py to generate) |
| Logging | LOGLEVEL (warning/info/debug), GUNICORN_ACCESSLOG (path or /dev/null) |
| OBS | OBS_HOST, OBS_PORT, OBS_PASSWORD |
| Bitrate | STATS_URL, BITRATE_LOW_KBPS, BITRATE_HIGH_KBPS, POLL_INTERVAL_SEC, LOW_CONSEC_SAMPLES |
| Scenes | LIVE_SCENE_NAME, LOW_SCENE_NAME |
| Restream | CONFIG_PATH, NGINX_CONF_OUT |
| Mini-PC | MINI_PC_USER, MINI_PC_IP, MAC_ADDRESS |
| Twitch | TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET, TWITCH_BROADCASTER_ID, TWITCH_OAUTH_TOKEN, TWITCH_REFRESH_TOKEN, TWITCH_TOKENS_PATH |
| Raid | RAID_AUTO_STOP_ENABLED, RAID_AUTO_STOP_DELAY |
| Chat Commands | TWITCH_ADMINS, STARTING_SCENE_NAME, BRB_SCENE_NAME |
| Behavior | WAIT_FOR_STREAM_START, EXIT_WHEN_STREAM_ENDS, IDLE_WHEN_STREAM_ENDS, LIVE_SCENE_LOW_GRACE_SEC |
| Overlay | ALERTS_BASE_URL |
Tokens are auto-refreshed and persisted.
python app.py --dev
python stream_guard.pyGenerate password hash first:
python generate_password_hash.py
# Add the generated hash to .env as LOGIN_PASSWORD_HASHDeploy with Gunicorn + systemd:
# Install dependencies
pip install -r requirements.txt
# Configure logging in .env
echo "LOGLEVEL=warning" >> .env
echo "GUNICORN_ACCESSLOG=/home/kim3k/stream-control/logs/access.log" >> .env
# Create logs directory
mkdir -p logs
# Install systemd service
sudo cp stream-control.service.example /etc/systemd/system/stream-control.service
sudo systemctl daemon-reload
sudo systemctl enable stream-control
sudo systemctl start stream-controlProduction stack:
- Gunicorn with threaded workers (sync worker + 100 threads)
- Secure password hashing (Werkzeug scrypt)
- ProxyFix middleware for Cloudflare Tunnel
- Rotating access logs (10MB max)
- systemd for auto-restart on failure
See DEPLOYMENT.md for detailed production setup guide and SOLUTION.md for architecture decisions.
| Section | Purpose |
|---|---|
| Status | OBS state, scene, health dots |
| Twitch | Title/category edit, raid |
| Restream | Manage push endpoints / Restream endpoints |
| Stream-PC | Wake / reboot / shutdown |
| Bot | Control a systemd service (optional) |
| Chat | Embedded Twitch chat |
| Alerts | Sounds when visiting Website |
Toasts provide immediate feedback.
- Toggle a top Twitch player from the Chat tab by double‑clicking the Chat tab label.
- The player loads edge‑to‑edge, starts muted with autoplay, and is layered above the chat.
- Hiding the player fully unloads the iframe to stop audio and save bandwidth.
- The correct channel is resolved automatically from
/twitch/channel_infoand cached for instant display.
Note: Twitch requires the page’s hostname to be listed in the embed parent parameter; the template injects this automatically.
- While live, the OBS tab shows “Viewers: N”.
- Polling is enabled only when streaming and stops when offline to minimize load.
- Backend endpoint:
GET /twitch/stream_info→{ is_live, viewer_count, title, game_name, started_at }.
If the backend feels stuck (e.g., nginx not serving correctly or StreamGuard appears unresponsive), the Mini-PC tab includes a "Repair Backend" button. This triggers a controlled restart of critical services to recover quickly.
What it does (by default):
- Restarts nginx
- Restarts StreamGuard (the guard process handling bitrate and EventSub)
- Restarts Stunnel (RTMPS)
Notes:
- The operation is idempotent and safe to run when services are already healthy.
- Use this when health indicators show red for nginx/StreamGuard/Stunnel or the UI becomes unresponsive due to those services.
Workflow:
- Open Restream panel.
- Edit/add endpoints (checkbox = enabled).
- Save → JSON updated → nginx config rendered → nginx reloaded automatically.
- Only enabled endpoints produce
pushlines.
Chat commands are processed via the Twitch EventSub channel.chat.message subscription.
A valid user access token (loaded from twitch_tokens.json) is required and must include the chat scopes: user:read:chat and user:write:chat.
Only admins listed in TWITCH_ADMINS (comma separated, lowercase) are authorized.
Commands are CASE SENSITIVE and must match exactly:
| Command | Action |
|---|---|
!start |
Start the stream (ignored if already live) then switch to STARTING_SCENE_NAME (if set) or stay on current |
!live |
Switch to LIVE_SCENE_NAME |
!brb |
Switch to BRB_SCENE_NAME |
!fix |
Switch to BRB then back to LIVE after ~1 second |
!stop |
Stop the current stream |
Environment variables affecting commands:
TWITCH_ADMINS=admin1,admin2STARTING_SCENE_NAME=Starting soon(optional)BRB_SCENE_NAME=BRB(optional; defaults handled in code)
If scenes are missing in OBS, commands log errors but do not crash the guard.
| Feature | Behavior |
|---|---|
| Low fallback | Switch after N consecutive low samples |
| Recovery | Switch back once high threshold met |
| Raid auto-stop | Optional post-raid stream stop |
| Idle handling | Idle, continue, or exit when stream ends (configurable) |
Scene transitions also dispatch overlay alerts.
- Automatic refresh when invalid or near expiry.
- Shared file
twitch_tokens.jsonused by Stream Guard. - Health shows validity + remaining lifetime.
- Revoked tokens trigger subscription re-attempt after refresh.
Both app.py and stream_guard.py use the same token file (twitch_tokens.json, path set by TWITCH_TOKENS_PATH).
-
app.py is the ONLY process that refreshes / rotates the access + refresh tokens (writes the file).
-
stream_guard.py is read‑only: it loads the current access token to:
- Subscribe to EventSub topics (raids, chat messages)
- Send chat messages (Helix Chat API) for feedback / raid completion
- Read live stream info (Helix
streams) exposed byGET /twitch/stream_infofor the viewer counter Required scopes for full functionality (recommend granting when generating initial tokens):
-
user:read:chat
-
user:write:chat
-
channel:manage:broadcast (title/category updates)
-
channel:read:subscriptions (optional future use) If a token is revoked or expires, app.py refresh logic updates the file; guard detects validity returning to healthy automatically.
- Alerts from streamElements
- Alerts when low bitrate / Connection restored (TTS on website)
- Send:
POST /api/alert{ "type": "low"|"restored", "message": "..." } - Transport: WebSocket (stateless; waits for next event)
A built-in, mobile-friendly log viewer is available from the Mini-PC tab via the "Logs" button. It helps you inspect systemd service logs without SSH.
Features:
- Service dropdown: switch between multiple services without mixing lines. Current services:
- stream-guard.service (StreamGuard)
- chatbot.service (optional)
- nginx.service
- stunnel-kick.service
- stream-control.service (the web app itself)
- Line count selector: load last 25 (default), 50, or 100 lines.
- Follow toggle: continue streaming new lines in real time via WebSocket.
- Timestamp format: rendered in journalctl short style, e.g. "Sep 08 04:56:38:" for readability.
- Color cues: basic highlighting for ERROR/WARN/INFO/DEBUG.
- Auto-scroll, large buffer trimming, and service-isolated sessions avoid stale entries when switching.
API (optional):
- HTTP: GET
/api/logs?service=<key>&lines=<n>returns the initial batch of lines. - WS: connect to
/ws/logs?service=<key>to follow (-n 0on the backend prevents duplicate backlog). - Accepted service keys match the UI dropdown (e.g.
streamguard,chatbot,nginx,stunnel,streamcontrol).
Notes:
- Changing the selected service closes the previous follow stream and ignores stale messages by session.
- The default 25 lines load immediately on open; follow can be toggled on/off without reloading the history.
The dashboard polls a lightweight health endpoint and renders compact status dots (ok/offline/error) with labels.
Data source: GET /api/sg_status
Provided states (UI shows a dot + concise label):
- Chatbot:
chatbot_state(systemd unit state) - Nginx:
nginx_state(systemd unit state) - Stunnel:
stunnel_state(systemd unit state) - StreamGuard:
streamguard_state(systemd unit state) - ChatGuard: derived from
chat_wsandchat_subscribed(ok when both are true; showswswhen WS is up but not yet subscribed) - SLS:
sls_state(stats endpoint availability) - OBS:
obs_connected(ok when true) - Twitch Events WS:
raid_ws(EventSub WebSocket alive) - Raid AutoStop:
raid_subscribed(EventSubchannel.raidsubscription active) - Twitch Token:
token_validplustoken_expires_in(minutes shown in the label when valid)
Color coding in UI:
- ok: green dot
- offline: gray dot
- error: red dot (e.g., explicit error conditions)
Operational notes:
- StreamGuard’s EventSub client auto-retries subscription with backoff after network/token changes.
- When a token transitions from invalid to valid, a forced re-subscribe attempt is scheduled promptly.
- Reverse proxy + HTTPS
- Limit network exposure (VPN / LAN)
- Least-privilege sudo (only what’s required)
- Strong secrets (FLASK_SECRET_KEY, LOGIN_PASSWORD)
- Restrict token file permissions (600)
- Never commit
.envor live stream keys
Stream-Control now includes support for IP banning to protect against unwanted connections or abuse. When an IP is banned:
- Connections from the banned IP address are immediately blocked.
- A log entry is created to indicate that a blocked IP attempted to connect.
- Banned IPs can be managed via the
bans.jsonfile or directly through the admin control panel at/bans.
- IP banning is enabled by default.
- Ensure the
bans.jsonfile is protected against unauthorized access. - Use this feature cautiously to avoid accidentally blocking legitimate users.
- Unbanning IPs can be done easily via the admin control panel at
/bans.
| Issue | Hint |
|---|---|
| OBS “Error” | Check port/password & plugin |
| Token stays invalid | Refresh token expired → reissue |
| No raid stop | Verify raid_ws & raid_subscribed |
| Bitrate static | STATS_URL response format |
| nginx reload fails | Endpoint syntax / template values |
| Chat missing | Ensure broadcaster_name + correct parent domain |
Check logs for both processes first.
Ideas:
- More metrics
- Role-based access
- Come with suggestions
- Fork
- Branch
feat/<name> - Commit with clear messages
- Open PR (Problem / Solution / Test)
Provided “as is.” Review before exposing publicly.
Happy streaming.
