Python Livestream Toolkit provides two explicit YouTube playout modes:
1080p30The continuous terminal feed is the default container path. It navigates to STREAM_URL once and keeps Chromium and Xvfb alive while FFmpeg reconnects in process after transient output failures. The narrated runtime remains available through stream_url.py and its buffered producer-consumer pipeline.
1920x1080 at 30 FPS with H.264 High, 10 Mbps CBR, a two-second GOP, and silent AAC audioshows/*.tomlrss, webpage, json, or manual adaptersEach show profile defines:
overlay, clean-feed, and transitionBundled profile:
shows/hb_capital.toml| Backend | Best for | Notes |
|---|---|---|
playwright | stability | Default path, typically around 12 FPS |
screen | local macOS experiments | Captures an entire display via avfoundation |
virtual-screen | Docker or Linux VM | Captures only an isolated Xvfb-hosted Chromium session |
If you do not want to stream your real desktop, use virtual-screen on Linux or in the provided container image.
The runtime is split into small modules:
shows/ loads show configs, source adapters, and segment briefsllm/ builds prompts and routes script generationtts/ renders narrationbroadcast/pipeline.py prepares queued segments and local studio pagesbroadcast/streaming.py handles narrated FFmpeg playoutbroadcast/memory.py records prior coverage context per showThe dedicated continuous path is split across stream_terminal.py and the small broadcast/terminal_stream*.py modules. See docs/terminal-stream.md for its operating contract.
More detail lives in docs/architecture.md.
git clone https://github.com/SebastianBoehler/python_livestream.git
cd python_livestream
pip install -r requirements.txt
playwright install
requirements.txt includes the full local-model stack. The default container intentionally uses the lighter requirements-stream.txt instead.
Copy .env.example to .env and set the values you need.
Common settings:
STREAM_URL=https://www.hb-capital.app/livestream
YOUTUBE_STREAM_KEY_FILE=/run/secrets/youtube_stream_key
For local development only, YOUTUBE_STREAM_KEY is accepted as a fallback. Production should mount the key as a readable, tightly permissioned file and set YOUTUBE_STREAM_KEY_FILE; if the file setting is present but unreadable or empty, startup fails instead of falling back to the environment.
Notes:
SHOW_ID selects a profile in shows/SHOW_CONFIG_PATH can point to any custom TOML fileSTREAM_URL is always required by the continuous feed and by narrated profiles that reference itNEWS_SEGMENT_SECONDS is still supported as a global duration overridehb_capital uses an HB Capital scene pack with overlay desk shots, clean-feed terminal views, and branded transitionsProvider credentials depend on which services you use:
GEMINI_API_KEYXAI_API_KEYOPENROUTER_API_KEYELEVENLABS_API_KEYHF_TOKENpython stream_terminal.py
This entry point is Linux-only because it uses Xvfb and X11 capture. On macOS, use the container or a Linux VM.
For a receiver-free VM burn-in, set STREAM_OUTPUT_FILE=/tmp/terminal.flv, stop the stream after the soak window, and inspect it with ffprobe. This is mutually exclusive with STREAM_OUTPUT_URL.
python stream_url.py
SHOW_CONFIG_PATH=/absolute/path/to/my_show.toml python stream_url.py
STREAM_ORIENTATION=portrait python stream_url.py
INTER_SEGMENT_MUSIC_SECONDS=20 python stream_url.py
INTER_SEGMENT_DELAY_SECONDS is still accepted as a compatibility alias.
STREAM_CAPTURE_BACKEND=virtual-screen STREAM_FPS=25 python stream_url.py
virtual-screen is Linux-only and is the recommended mode for long-running container or VM deployment.
show_id = "my_show"
title = "Niche Desk"
tagline = "Automated coverage for one clear audience."
host_name = "Desk"
host_role = "Operator anchor"
description = "Explain what changed and why it matters."
base_prompt = "Cover the most relevant developments from the last 24 hours."
llm_system_instruction = "You are a sharp and concise anchor."
tts_voice = "Charon"
[branding]
primary_color = "#93f5d8"
accent_color = "#f6c35c"
background_start = "#04141a"
background_end = "#11172a"
card_background = "rgba(8, 18, 31, 0.78)"
text_color = "#f4f7fb"
muted_text_color = "#9bb4c8"
[studio]
label = "Live Desk"
strapline = "Source-driven coverage"
ticker_prefix = "Radar"
iframe_url = ""
layout_mode = "split"
[[sources]]
kind = "rss"
name = "Google News Topic"
url = "https://news.google.com/rss/search?q=my+topic+when:1d&hl=en-US&gl=US&ceid=US:en"
limit = 5
[[sources]]
kind = "manual"
name = "Editorial Guardrails"
text = "Stay concrete and explain implications."
[[segments]]
kind = "headline"
label = "Top Setup"
instructions = "Open with the biggest development."
duration_seconds = 180
scene_mode = "overlay"
The default Dockerfile is optimized for the continuous terminal feed on Linux hosts:
python:3.11-slim-bookworm1080p30 libx264 encoding for stable YouTube qualityBuild and run it:
docker build -t python-livestream .
docker run --env-file .env python-livestream
For persistent operation:
docker compose up -d
The included docker-compose.yml uses the dedicated virtual-screen path and restart: unless-stopped.
The narrated show remains an opt-in Compose profile:
docker compose --profile narrated up narrated
If you want a heavier local-model image for GPU-backed TTS experiments, use Dockerfile.gpu:
docker build -f Dockerfile.gpu -t python-livestream-gpu .
broadcast/ streaming, capture backends, memory, intermissions, studio pages
docs/ architecture notes
llm/ provider routing and prompt generation
shows/ show configs, source adapters, segment brief helpers
tts/ TTS backends and chunking
tests/ unit tests for core streaming behavior
Run the baseline checks before opening a PR:
python -m py_compile $(git ls-files '*.py')
python -m unittest discover -s tests -p 'test_*.py' -v
CI also builds the default Docker image to catch container regressions early.
See CONTRIBUTING.md for workflow and expectations.
In short:
If you discover a security issue, see SECURITY.md.
This project is licensed under the MIT License. See LICENSE.
52 commits
HTML
55.9%
Python
38.8%
Shell
3.0%
CSS
1.9%
Python Livestream Toolkit provides two explicit YouTube playout modes:
1080p30The continuous terminal feed is the default container path. It navigates to STREAM_URL once and keeps Chromium and Xvfb alive while FFmpeg reconnects in process after transient output failures. The narrated runtime remains available through stream_url.py and its buffered producer-consumer pipeline.
1920x1080 at 30 FPS with H.264 High, 10 Mbps CBR, a two-second GOP, and silent AAC audioshows/*.tomlrss, webpage, json, or manual adaptersEach show profile defines:
overlay, clean-feed, and transitionBundled profile:
shows/hb_capital.toml| Backend | Best for | Notes |
|---|---|---|
playwright | stability | Default path, typically around 12 FPS |
screen | local macOS experiments | Captures an entire display via avfoundation |
virtual-screen | Docker or Linux VM | Captures only an isolated Xvfb-hosted Chromium session |
If you do not want to stream your real desktop, use virtual-screen on Linux or in the provided container image.
The runtime is split into small modules:
shows/ loads show configs, source adapters, and segment briefsllm/ builds prompts and routes script generationtts/ renders narrationbroadcast/pipeline.py prepares queued segments and local studio pagesbroadcast/streaming.py handles narrated FFmpeg playoutbroadcast/memory.py records prior coverage context per showThe dedicated continuous path is split across stream_terminal.py and the small broadcast/terminal_stream*.py modules. See docs/terminal-stream.md for its operating contract.
More detail lives in docs/architecture.md.
git clone https://github.com/SebastianBoehler/python_livestream.git
cd python_livestream
pip install -r requirements.txt
playwright install
requirements.txt includes the full local-model stack. The default container intentionally uses the lighter requirements-stream.txt instead.
Copy .env.example to .env and set the values you need.
Common settings:
STREAM_URL=https://www.hb-capital.app/livestream
YOUTUBE_STREAM_KEY_FILE=/run/secrets/youtube_stream_key
For local development only, YOUTUBE_STREAM_KEY is accepted as a fallback. Production should mount the key as a readable, tightly permissioned file and set YOUTUBE_STREAM_KEY_FILE; if the file setting is present but unreadable or empty, startup fails instead of falling back to the environment.
Notes:
SHOW_ID selects a profile in shows/SHOW_CONFIG_PATH can point to any custom TOML fileSTREAM_URL is always required by the continuous feed and by narrated profiles that reference itNEWS_SEGMENT_SECONDS is still supported as a global duration overridehb_capital uses an HB Capital scene pack with overlay desk shots, clean-feed terminal views, and branded transitionsProvider credentials depend on which services you use:
GEMINI_API_KEYXAI_API_KEYOPENROUTER_API_KEYELEVENLABS_API_KEYHF_TOKENpython stream_terminal.py
This entry point is Linux-only because it uses Xvfb and X11 capture. On macOS, use the container or a Linux VM.
For a receiver-free VM burn-in, set STREAM_OUTPUT_FILE=/tmp/terminal.flv, stop the stream after the soak window, and inspect it with ffprobe. This is mutually exclusive with STREAM_OUTPUT_URL.
python stream_url.py
SHOW_CONFIG_PATH=/absolute/path/to/my_show.toml python stream_url.py
STREAM_ORIENTATION=portrait python stream_url.py
INTER_SEGMENT_MUSIC_SECONDS=20 python stream_url.py
INTER_SEGMENT_DELAY_SECONDS is still accepted as a compatibility alias.
STREAM_CAPTURE_BACKEND=virtual-screen STREAM_FPS=25 python stream_url.py
virtual-screen is Linux-only and is the recommended mode for long-running container or VM deployment.
show_id = "my_show"
title = "Niche Desk"
tagline = "Automated coverage for one clear audience."
host_name = "Desk"
host_role = "Operator anchor"
description = "Explain what changed and why it matters."
base_prompt = "Cover the most relevant developments from the last 24 hours."
llm_system_instruction = "You are a sharp and concise anchor."
tts_voice = "Charon"
[branding]
primary_color = "#93f5d8"
accent_color = "#f6c35c"
background_start = "#04141a"
background_end = "#11172a"
card_background = "rgba(8, 18, 31, 0.78)"
text_color = "#f4f7fb"
muted_text_color = "#9bb4c8"
[studio]
label = "Live Desk"
strapline = "Source-driven coverage"
ticker_prefix = "Radar"
iframe_url = ""
layout_mode = "split"
[[sources]]
kind = "rss"
name = "Google News Topic"
url = "https://news.google.com/rss/search?q=my+topic+when:1d&hl=en-US&gl=US&ceid=US:en"
limit = 5
[[sources]]
kind = "manual"
name = "Editorial Guardrails"
text = "Stay concrete and explain implications."
[[segments]]
kind = "headline"
label = "Top Setup"
instructions = "Open with the biggest development."
duration_seconds = 180
scene_mode = "overlay"
The default Dockerfile is optimized for the continuous terminal feed on Linux hosts:
python:3.11-slim-bookworm1080p30 libx264 encoding for stable YouTube qualityBuild and run it:
docker build -t python-livestream .
docker run --env-file .env python-livestream
For persistent operation:
docker compose up -d
The included docker-compose.yml uses the dedicated virtual-screen path and restart: unless-stopped.
The narrated show remains an opt-in Compose profile:
docker compose --profile narrated up narrated
If you want a heavier local-model image for GPU-backed TTS experiments, use Dockerfile.gpu:
docker build -f Dockerfile.gpu -t python-livestream-gpu .
broadcast/ streaming, capture backends, memory, intermissions, studio pages
docs/ architecture notes
llm/ provider routing and prompt generation
shows/ show configs, source adapters, segment brief helpers
tts/ TTS backends and chunking
tests/ unit tests for core streaming behavior
Run the baseline checks before opening a PR:
python -m py_compile $(git ls-files '*.py')
python -m unittest discover -s tests -p 'test_*.py' -v
CI also builds the default Docker image to catch container regressions early.
See CONTRIBUTING.md for workflow and expectations.
In short:
If you discover a security issue, see SECURITY.md.
This project is licensed under the MIT License. See LICENSE.
52 commits
HTML
55.9%
Python
38.8%
Shell
3.0%
CSS
1.9%