A multi-platform stream monitor with plug-and-play listeners. Tracks streamers on YouTube and Twitch and emits Server-Sent Events (SSE) when something changes.
Status: alpha. Expect breaking changes.
# 1) Python 3.11+
python -V
# 2) Install
pip install -e .
# 3) Configure (optional)
cp src/LiveMood/config.toml{,.local}
# edit src/LiveMood/config.toml or point database_path elsewhere
# 4) Run
python -m LiveMood.server
# or: uvicorn LiveMood.server:app --port 7070The SSE endpoint is available at:
GET /sse/listen?listener_id=<any-ASCII-id-1..32>
POST /streamers— body:{ "url": "<channel url>" }→ follows a streamerDELETE /streamers— body:{ "identity": "<url|uid|id>" }→ unfollowGET /streamers— optionalidentity→ list streamers / get oneDELETE /streams— remove all streams (maintenance)
SSE payload example:
{
"streamer": { "...": "peewee model->dict" },
"streams": [
{
"stream_id": "abc123",
"title": "Example Stream",
"status": "is_live",
"ext": { "view_count": 420, "start_time": "2025-08-09T12:34:56+00:00" }
}
]
}src/LiveMood/config.toml:
[app]
database_path = "~/.local/state/LiveMood/streamers.db"
sleep_interval = 300
adapter_retry_count = 3
rate_limit_timeout = 20
follower_limit = 50
listener_queue_limit = 100
enable_debug = true
port = 7070- The database path now expands
~and ensures parent directories exist. - SSE sends automatic heartbeats every ~15s (via
sse-starlettedefaults).
By default (database_path = "auto"), LiveMood stores the SQLite database in an OS‑appropriate location:
- Windows:
%LOCALAPPDATA%\LiveMood\streamers.db - macOS:
~/Library/Application Support/LiveMood/streamers.db - Linux/BSD:
$XDG_STATE_HOME/LiveMood/streamers.dbor~/.local/state/LiveMood/streamers.db
Override this in src/LiveMood/config.toml by setting an absolute path or ~/....
- Format:
black• Lint:ruff• Types:mypy• Tests:pytest -m unit - CI runs on GitHub Actions with a minimal pipeline.
- Adapters live in
src/LiveMood/adapters/. To add a platform, export:IDENTITY = ("Name", r"<url regex>")get_streamer(url: str) -> dictget_streams(streamer: dict) -> list[dict]
- The Twitch adapter uses public GraphQL endpoints and may hit rate limits.
- The YouTube adapter rotates between a small set of
WEBclient versions. If InnerTube changes, scraping can break.
AGPL-3.0