Local push & file browser - File browser with breadcrumb nav, search, directory memory, .. row, and hidden/system folder filtering (./@/#) - Color-coded file extension tags; file sizes shown inline - Per-file push modal with random stream key generator and responsive width - Folder multi-file push modal: independent stream keys per file, batch start/stop, inline live-dot status with real-time duration - Push status inline in file rows replacing top push-jobs area; job detail modal with copy/stop/add-stream actions - /h/<slug> HLS proxy route registered automatically on push start - Folder push and publish-archive recurse subfolders via os.walk - "Add to existing stream" dropdown at file, folder, and job modal entries - Stream editor supports prefilling multiple source links via links array - list_folder_videos API returns playable files with signed URLs VOD / video serving - /video/<token>/<payload> endpoint with HMAC-signed URLs and HTTP Range support (206 Partial Content, seek-capable) - Publish-archive button on file rows and folder rows Admin UX - Replace all 18 native alert() with themed Toast notifications (success/error/info/warn, 3.5s auto-dismiss, dark mode aware) - Replace all 3 native confirm() with custom modal (showConfirm) - Custom overlay scrollbar for admin.html and index.html: no layout shift, theme-colored, auto-hides after 1.5s, drag-supported - background-attachment: fixed on admin and index body backgrounds - Drag handle for viewport config rows in stream editor - Pagination and real-time search for hidden push address mapping table - Pagination for stream analytics detail table with SSE-safe page state - Stream picker search placeholder i18n - Lang toggle button title/aria-label i18n - View URL hash renamed: push -> local, obs -> remote Index (public) page - Load more: viewport-aware initial batch calculated from .stream-switch bottom position; ghost-style button; card entrance animation with 50ms per-card stagger on load-more click only Infrastructure - Dockerfile: install ffmpeg; separate requirements COPY for layer cache - docker-compose.yml: add RTMP_HOST, VIDEOS_DIRS env vars, videos volume - README: document VIDEOS_DIRS mount methods, password reset procedure Fixes - action=add 500 error: psycopg3 dict_row does not support int subscript - Lang toggle button title/aria-label missing i18n keys - API Keys list not re-rendering on language switch - Admin stream count per tab showing combined LIVE+ARCHIVE total - Em dash in api.new_token_hint replaced with hyphen
12 KiB
StreamHall
Self-hosted live stream hall - public list, player, admin panel, analytics & push notifications
English · 简体中文
- ✨ Features
- 🚀 Quick Start
- 💻 Local Development
- ⚙️ Configuration
- 🐳 Services
- 🔌 Stream Push
- 📡 API
- 🤝 Contributing
- 📝 License
✨ Features
- Public stream list - Live and archive tabs, password-protected streams, custom site branding, bilingual UI (Chinese / English) with per-language site description
- Player - HLS, FLV, MPEG-DASH playback via ArtPlayer; AES-128 key override and DASH ClearKey support
- Admin panel - Add, edit, reorder, enable/disable streams; manage sources; drag-and-drop ordering
- Viewer analytics - Session tracking, unique visitors, peak concurrent viewers, average watch duration, device / browser / OS / geography breakdown, real-time dashboard, CSV export
- Telegram notifications - Per-stream push messages on stream start and stop
- Stream push - Local file browser with per-file and per-folder RTMP push management; multi-file folder push with independent stream keys; inline push status and detail modal; remote RTMP push config for external encoders; hidden HLS route proxy (
/h/<slug>) so real stream keys are never exposed publicly - VOD / file serving - Signed
/video/URLs with HTTP Range support (seek-capable); publish any local video file or folder as an archive stream directly from the file browser - HLS proxy - Signed
/proxy/hls/routes for cross-origin HLS playback - API key auth - Generate per-key tokens in the admin panel for programmatic access to all admin and analytics endpoints
🚀 Quick Start
1. Download and configure
curl -LO https://git.stdm.moe/Stardream/StreamHall/raw/branch/main/docker-compose.yml
curl -LO https://git.stdm.moe/Stardream/StreamHall/raw/branch/main/nginx-hls.conf
Important
Open
docker-compose.ymland changeSECRET_KEYandPOSTGRES_PASSWORDto strong random values before starting.
2. Start
docker compose up -d
3. Find the initial admin password
docker logs streamhall
Look for:
StreamHall initial admin password: <random-password>
4. Access
| URL | Description |
|---|---|
http://HOST:8085/ |
Public stream list |
http://HOST:8085/admin |
Admin panel |
http://HOST:18088/ |
SRS HTTP playback |
http://HOST:8889/ |
Nginx HLS proxy |
Tip
After first login, change your password under Site Settings → Security. The initial password is printed once and not stored in plaintext.
Updating to a newer version
docker compose pull && docker compose up -d
Resetting a forgotten admin password
docker exec streamhall-postgres psql -U streamhall -d streamhall \
-c "DELETE FROM site_settings WHERE key='admin_password_hash';"
docker restart streamhall
docker logs streamhall
Deleting the hash causes StreamHall to generate a new random password on the next startup and print it to the log.
💻 Local Development
Prerequisites: Python 3.12+, PostgreSQL 14+
# 1. Clone the repo
git clone https://git.stdm.moe/Stardream/StreamHall.git
cd StreamHall
# 2. Install the single runtime dependency
pip install -r requirements.txt
# 3. Export required environment variables
export SECRET_KEY=dev-secret
export DATABASE_URL=postgresql://user:pass@localhost:5432/streamhall
# 4. Start the server
python server.py
The app listens on http://localhost:8080 by default. On first startup it creates the database schema and prints a one-time admin password to stdout.
⚙️ Configuration
Set these environment variables in docker-compose.yml:
| Variable | Default | Required | Description |
|---|---|---|---|
SECRET_KEY |
change-this-secret |
Yes | HMAC signing key for sessions and stream routes |
DATABASE_URL |
see compose file | Yes | PostgreSQL connection string |
POSTGRES_PASSWORD |
see compose file | Yes | PostgreSQL password |
TZ |
UTC |
No | Container timezone, e.g. Asia/Shanghai |
SRS_HTTP_ORIGIN |
http://srs:8080 |
No | SRS HTTP playback base URL |
STREAM_PROBE_TIMEOUT |
4 |
No | Seconds before aborting a stream URL probe |
STREAM_MONITOR_INTERVAL |
10 |
No | Seconds between stream liveness checks |
TELEGRAM_TIMEOUT |
6 |
No | Seconds before aborting a Telegram API call |
RTMP_HOST |
srs |
No | Hostname of the SRS container used for local push jobs |
VIDEOS_DIRS |
(unset) | No | Comma-separated list of directories exposed in the file browser. Optionally prefix each path with a label: label:/app/path. Multiple entries: movies:/app/movies,shows:/app/shows |
Warning
Always change
SECRET_KEYandPOSTGRES_PASSWORDbefore exposing StreamHall to a network. The defaults are intentionally weak placeholders.
Mounting video directories for Local Push
Method 1 — single base directory, multiple sources as subdirectories:
Mount multiple host paths under one container base path. The file browser shows them as subdirectories.
environment:
VIDEOS_DIRS: "/app/videos"
volumes:
- ./videos:/app/videos/local
- /your/media/path:/app/videos/external
Method 2 — multiple labeled top-level directories:
Each path is listed as a separate labeled root entry in the file browser.
environment:
VIDEOS_DIRS: "/app/videos,external:/app/media/external"
volumes:
- ./videos:/app/videos
- /your/media/path:/app/media/external
🐳 Services
The default compose stack starts four containers:
| Container | Image | Port(s) | Purpose |
|---|---|---|---|
streamhall |
local build | 8085→8080 |
Python app + static frontend |
streamhall-postgres |
postgres:16-alpine |
- | PostgreSQL persistent database |
streamhall-srs |
ossrs/srs:6 |
1935, 18088→8080 |
RTMP publishing + HTTP playback |
streamhall-nginx-hls |
nginx:alpine |
8889→80 |
Clean public HLS route proxy |
Persistent data is stored in ./postgres-data/.
🔌 Stream Push
SRS handles RTMP publishing on port 1935. Configure your encoder as follows:
RTMP server: rtmp://HOST:1935/live
Stream key: your-stream-key
The admin panel's Local Push section provides a file browser for your media directory. Select any video file to push it via RTMP with a custom stream key, or select a folder to push all contained videos simultaneously with independent stream keys. Push status is shown inline per file; a detail modal gives real-time duration, copy addresses, and stop controls.
The hidden public HLS URL (/h/<slug>/...) routes through nginx on port 8889, keeping the real stream key out of the public URL.
Note
The RTMP host field accepts an optional port, e.g.
live.example.com:1935. Do not hard-code:1935if you use a non-standard port.
📡 API
Authentication
- Browser session - cookie set by
POST /api?action=login - API key - send
Authorization: Bearer <token>header, or append?api_key=<token>to any admin endpoint. Keys are managed in the admin panel under Site Settings → API Keys.
Public Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET |
/api?action=public_list |
Stream list for the public homepage |
GET |
/api?action=site_settings |
Site title, description, branding |
GET |
/api?action=get_player_data&id=<public_id> |
Player sources and metadata |
POST |
/api?action=verify_password |
Verify a stream password |
GET |
/video/<token>/<payload> |
Signed VOD endpoint with HTTP Range support |
Admin Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST |
/api?action=login |
Authenticate and start a session |
GET |
/api?action=logout |
End the current session |
GET |
/api?action=list_admin |
Full stream list |
POST |
/api?action=add |
Add a stream |
POST |
/api?action=update |
Update a stream |
POST |
/api?action=delete |
Delete a stream |
POST |
/api?action=set_stream_enabled |
Toggle stream visibility |
POST |
/api?action=reorder_streams |
Reorder within a label group |
POST |
/api?action=update_site_settings |
Save site settings |
GET |
/api?action=telegram_settings |
Read Telegram bot config |
POST |
/api?action=update_telegram_settings |
Save Telegram bot config |
POST |
/api?action=update_admin_password |
Change admin password |
GET |
/api?action=list_api_keys |
List API keys (tokens not returned) |
POST |
/api?action=create_api_key |
Create a key - token returned once |
POST |
/api?action=delete_api_key |
Revoke a key by id |
GET |
/api?action=list_pushes |
List active push jobs |
POST |
/api?action=start_push |
Start an RTMP push job for a file |
POST |
/api?action=stop_push |
Stop a push job by stream key |
GET |
/api?action=list_folder_videos |
List playable files in a directory (with signed URLs) |
Analytics Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET |
/api?action=stats_overview |
Aggregated totals |
GET |
/api?action=stats_streams |
Per-stream stats table |
GET |
/api?action=stats_timeseries |
Bucketed view counts |
GET |
/api?action=stats_geo |
Country-level visitor counts |
GET |
/api?action=stats_stream_detail&id=<id> |
Single-stream detail |
GET |
/api?action=stats_sessions_page&id=<id> |
Paginated session list |
GET |
/api?action=stats_export_csv |
Export sessions as CSV |
💡 Acknowledgements
StreamHall was inspired by AniLive, a live stream listing site that is not open-source. This project is an independent reimplementation built from scratch, sharing no code with the original site.
🤝 Contributing
Issues and pull requests are welcome. Please open an issue first for major changes so the direction can be discussed.
📝 License
Copyright © 2026 Stardream. This project is licensed under the GNU Affero General Public License v3.0.
Permissions
| ✅ | Commercial use |
| ✅ | Modification |
| ✅ | Distribution |
| ✅ | Private use |
Conditions
| ⚠️ | Disclose source - modified versions must also be open-source |
| ⚠️ | Same license - derivative works must use AGPL-3.0 |
| ⚠️ | Network use = distribution - if you run a modified version as a network service, you must make the source available to users |
| ⚠️ | State changes - modifications must be documented |
In short: you are free to use, modify, and self-host StreamHall. If you distribute or offer a modified version as a hosted service, you must publish the full source under AGPL-3.0.