StreamHall - 搭建你自己的直播间,从推流到观众统计一站式搞定

StreamHall - 搭建你自己的直播间,从推流到观众统计一站式搞定
Stardream起因
之前逛到一个叫 AniLive 放映室 的 nsy 直播转播站,界面设计及功能实现我很喜欢 - 干净的卡片布局、暗色主题、多直播源管理,感觉刚好是想要的形态。可惜 ta 没有开源,也没有找到类似的替代品。
既然没有现成的,那就自己造一个。StreamHall 的界面风格参考了 AniLive 放映室的简洁直播列表形态,但前后端均为独立实现,不共享任何原始代码,并在此基础上扩展了观看统计、Telegram 推送、推流配置辅助等符合我个人使用的功能。
这也是我第一个完全通过 Vibe Coding 完成的项目。
功能概览
StreamHall 是一个自托管的直播站点系统,核心目标是自己搭、自己管、数据全掌握。
演示站点:StarLive
主要功能:
- 公开直播列表 - 直播 / 存档双 Tab,支持密码保护,站点标题、简介、导航链接、页脚内容均可在后台自定义,内置中英双语界面
- 播放器 - 基于 ArtPlayer,支持 HLS、FLV、MPEG-DASH;可配置 AES-128 密钥覆盖、DASH ClearKey,以及 Widevine / FairPlay DRM 授权播放,播放器会根据浏览器环境自动选择合适的 DRM 方案
- 管理后台 - 直播的增删改查、启用/禁用、拖拽排序,多播放源管理;中英双语后台
- 观看统计 - 实时在线人数、今日/历史观看量、独立访客、平均时长、设备/浏览器/OS/地理分布看板,支持导出 CSV。该功能会记录访问时间、设备类型、浏览器、操作系统、IP / 地理信息等数据,公开部署时建议在站点说明或隐私政策中明确告知
- Telegram 推送 - 可按直播单独配置,开播/关播自动发通知,消息模板支持自定义变量;多视角直播下每个视角上线都会独立推送,短时间内一起开播的视角会合并成一条消息,RTMP 短暂断线重连也不会误触发关播 / 开播通知
- 本地文件推流 / VOD - 内置文件浏览器,可直接选择本地视频文件或整个文件夹通过 RTMP 推流;也可将视频文件直接发布为存档直播源,带签名 URL 和 HTTP Range 支持(可拖拽进度条)
- RTMP 推流辅助 - 配合 SRS 容器,支持隐藏公开 HLS 路由(
/h/<slug>/...),公开地址无法反推出真实推流码 - HLS 反代 - 带签名验证的代理路由,解决外链 HLS 的跨域问题,提供完整代理 / 仅 Manifest / 直连等多种模式;针对依赖签名 Cookie 鉴权的 CDN(如 CloudFront)还可配置上游 Cookie,由服务端在转发时附带,Cookie 仅存于服务端、不会暴露在播放地址中。该功能只负责 manifest / segment 转发,不绕过 DRM、不提取密钥,也不会替代合法的 License Server 授权
- DRM 辅助配置 - 后台可自动识别部分 DASH Widevine / HLS FairPlay manifest 中的 License URL、PSSH、Certificate URL 等信息,减少手动抓包配置成本
- API 密钥 - 可在后台生成 Bearer Token,程序化访问所有管理和统计接口
技术栈
- 后端:单文件 Python(
server.py),无任何 Web 框架,基于标准库的ThreadingHTTPServer。每个请求独立线程,每个线程独立 PostgreSQL 连接。 - 数据库:PostgreSQL 16,唯一的外部运行时依赖是
psycopg3(连接驱动)。 - 前端:纯原生 HTML + CSS + JS,不依赖任何前端框架,三个页面(首页、播放器、管理后台)各自独立。
- 部署:Docker Compose,四个容器:应用本体、PostgreSQL、SRS(RTMP)、Nginx(HLS 代理)。
部署方式
只需 Docker Compose,大约两分钟可以跑起来。
第一步:下载配置文件
curl -LO https://git.stdm.moe/Stardream/StreamHall/raw/branch/main/docker-compose.yml |
第二步:修改必填项
打开 docker-compose.yml,把 SECRET_KEY 和 POSTGRES_PASSWORD 改成强随机值,这两个字段直接关系到会话安全和数据库访问权限,不能留默认值。
第三步:启动
docker compose up -d |
第四步:获取初始密码
docker logs streamhall |
在日志里找到这一行:
StreamHall initial admin password: <random-password> |
初始密码只打印一次,不会以明文形式落库。启动后访问 http://HOST:8085/admin 登录,进后台后在网站设置 → 安全设置里改掉密码。
各服务地址
| 地址 | 说明 |
|---|---|
http://HOST:8085/ |
公开直播列表 |
http://HOST:8085/admin |
管理后台 |
http://HOST:18088/ |
SRS HTTP 播放 |
http://HOST:8889/ |
Nginx HLS 代理 |
日后更新
docker compose pull && docker compose up -d |
几个值得说的设计细节
隐藏 HLS 路由
如果你用 RTMP 推流(比如 OBS),推流码是私密的,但如果直接把 SRS 的 HLS 地址暴露给观众,地址里会直接包含推流码(/live/<stream-key>.m3u8),任何人都能猜到推流码。
StreamHall 的做法是:在后台为每个推流码生成一个 HMAC 签名的 slug,公开播放地址变成 /h/<slug>/index.m3u8,由 Nginx 将请求反代到 StreamHall,StreamHall 验证签名后再转发给 SRS。观众拿到的地址无法反推出真实推流码。
这里也顺便说一下“隐藏链接”和“带宽压力”的取舍:如果外链 HLS 走 StreamHall 的完整代理,那么观众请求 manifest、分片、key 等内容都会经过自己的服务器,源地址隐藏效果更好,但本机也要承担观看流量。相反,如果让播放器直连源站,观众流量基本不经过自己的机器,但浏览器 Network 里最终还是能看到源站媒体地址。
所以 StreamHall 把 HLS 代理定位为“兼容性工具”:主要用来解决 CORS、HLS key、跨域播放失败等问题,而不是默认防盗链方案。目前每个播放源都可以单独选择代理模式:
- 自动 / 完整代理 - manifest、分片、key 全部经过 StreamHall,源地址隐藏效果最好,但本机要承担观看流量;
- 仅 Manifest - 只代理入口 m3u8,分片改写为直连源站地址,在“隐藏入口链接”和“不吃本机带宽”之间折中;
- 直连 - 完全不经过代理,浏览器直接访问源站。
对于依赖签名 Cookie 鉴权的源(如 SPWN 用的 CloudFront),完整代理模式下还可以配置「上游 Cookie」,由服务端在转发 manifest 和分片时附带鉴权 Cookie。Cookie 只存在服务端,并以不可逆的签名 token 形式嵌入代理地址,观众无法从播放链接里还原出原始 Cookie。为了减少代理转发的延迟,对外链的上游请求还做了 HTTP 连接复用,避免每个分片都重新握手 TLS。
DRM 播放支持
从 v1.2.0 开始,StreamHall 支持为同一个播放源配置多组 DRM 授权信息。目前主要支持 Widevine 和 FairPlay:桌面 Chrome / Edge / Firefox 以及 Android 外部浏览器优先使用 Widevine,Safari / iOS 则优先使用 FairPlay,同一个“视角”也可以分别配置 Widevine 用的 MPD 链接和 FairPlay 用的 HLS 链接,让 Windows / Android 和 iOS / Safari 各走最合适的播放链路。
后台也加入了 DRM 自动识别。填入 DASH MPD 或 HLS manifest 后,StreamHall 会尽量从 manifest 中提取 Widevine License URL、PSSH,或 FairPlay Certificate URL / License URL。不是所有平台都会把授权信息完整写在 manifest 里,所以自动识别不能保证 100% 命中,但对 Brightcove 这类常见链路已经能省掉不少手动查找步骤。
需要强调的是,StreamHall 只负责把播放地址、License URL、Certificate URL 等合法授权信息交给浏览器的 EME 播放链路,不绕过 DRM,不提取密钥,也不解密内容。
实际接入 FairPlay 时还有个小坑:桌面端的 Widevine 请求比较容易在浏览器开发者工具里看到,但手机 Safari 走 FairPlay,必须找到对应的 FairPlay Certificate URL 和 License URL。我最后的做法是在手机端安装一个能提供类似 Safari DevTools 能力的调试 App,然后在已经授权、能够正常播放的页面里执行下面这段代码,把相关资源请求筛出来:
performance.getEntriesByType('resource') |
对于 Brightcove 这类播放链路,筛出来的资源里通常可以看到类似 /cert/fp、fairplay_app_cert、/lic/fp、/license/v1/fairplay/ 的请求,分别对应 FairPlay Certificate URL 和 FairPlay License URL。把这两个地址填进 StreamHall 同一个播放源的 FairPlay 配置里,iPhone / Safari 就能走 FairPlay 播放链路。
这些授权地址里的 token / cookies 往往有有效期,过期后需要从播放页重新获取。
对相关平台的实际验证
- ニコニコ生放送
Live
Archive
- Stagecrowd
Live
Archive
- Streaming+
Live
Archive
- SPWN
Live
Archive
- ZAIKO
Live
Archive
- PIA LIVE STREAM
Live
Archive
- Z-aN
Live
Archive
- ASOBI STAGE
Live
Archive
多语言支持
前端所有用户可见的文字都走 i18n 翻译表,包括服务端返回的错误信息 - 后端只返回错误码字符串(如 stream_not_found),前端根据当前语言翻译成对应文字,不会出现中文界面里突然蹦出一条英文报错的情况。
开源地址
项目代码托管在 GayHub 和 自建的 Gitea 上,基于 AGPL-3.0 协议开源,允许自由使用、修改和自部署,但若将修改版对外分发或作为托管服务提供,须以相同协议公开源码。








