π¨π³ δΈζη | π¬π§ English
Discover, monitor, and download X/Twitter broadcast videos from user timelines.
BroadcastX is a CLI tool that helps you:
- Scan β Find broadcast links in a user's timeline
- Download β Download broadcast videos with automatic phone-rotation correction
- Watermark β Add customizable text watermarks to videos via ffmpeg
- Monitor β Watch a profile for live broadcasts and auto-download replays
Uses Playwright browser automation to scroll through a user's X profile and intercept GraphQL API responses to extract broadcast URLs. More reliable than DOM scraping.
Downloads broadcast videos via yt-dlp and post-processes the video to correct phone orientation. Broadcasts streamed from a phone in portrait mode appear upright after processing. A .rotation.jsonl sidecar file is written alongside the video for inspection.
Continuously monitors a user's profile. When a live broadcast is detected, periodically checks its status. When the broadcast ends, automatically downloads the replay.
Adds customizable text watermarks to downloaded videos using ffmpeg's drawtext filter. The watermark text is rendered in the bottom-right corner by default, with options for font, size, opacity, color, and position (bottom-right, bottom-left, top-right, top-left).
- Python 3.11+
- yt-dlp β
brew install yt-dlp - ffmpeg β
brew install ffmpeg - Google Chrome (installed separately)
# Install BroadcastX and dependencies
uv sync
# Install Playwright's browser driver
uv run playwright install chromium# Scan a user's timeline for broadcast links
broadcastx scan @username
# Download broadcasts from scan results
broadcastx download --from output/username/broadcasts.json
# Monitor every user in broadcastx.toml
broadcastx monitorCopy broadcastx.example.toml to broadcastx.toml
in the directory where you run BroadcastX. It is discovered automatically;
use --config PATH only when the config lives elsewhere.
output_root = "output"
[monitor]
check_interval_seconds = 1800
live_interval_seconds = 300
download = true
[download]
timezone = "UTC"
datetime_format = "%Y-%m-%d_%H-%M-%S"
filename_template = "{datetime}_{broadcast_id}"
[[users]]
username = "Alice"
timezone = "Australia/Sydney"
[[users]]
username = "Bob"
timezone = "Asia/Ho_Chi_Minh"Explicit CLI options override user settings, which override command-wide
settings, which override built-in defaults. A relative output_root is
resolved from the config file's directory.
broadcastx scan @username
# Options:
# --max-scrolls 500 Safety cap for scroll actions
# --scroll-delay 2.0 Seconds between scrolls
# --idle-timeout 20.0 Stop after N seconds with no new target posts
# --output FILE Override path (default: output/<user>/broadcasts.json)
# --headless Run browser without visible windowThe scanner opens the user's X profile in Chrome, scrolls through the timeline, and intercepts API responses. Broadcast URLs are extracted from tweet cards. If you are not logged in, the browser shows the login page β log in manually, then press Enter in the terminal to continue. Your session is saved to ~/.broadcastx/chrome-profile/ for future runs.
# Single broadcast
broadcastx download https://x.com/i/broadcasts/1vAxRkBbDRzKl
# From scan results; the scanner username is preserved for routing
broadcastx download --from output/username/broadcasts.json
# Multiple concurrent downloads
broadcastx download --from output/username/broadcasts.json -p 3
# Custom output root
broadcastx download --from output/username/broadcasts.json -o ./archive
# Use Firefox cookies
broadcastx download --from output/username/broadcasts.json --browser firefox
# Verbose yt-dlp output
broadcastx download --from output/username/broadcasts.json -vBroadcastX automatically corrects phone rotation: if the broadcast carries phone-orientation metadata in the HLS stream, the video is re-encoded so it displays upright in any player.
For a direct URL, BroadcastX reads yt-dlp's uploader ID and broadcast timestamp,
then selects the matching user config and output directory automatically. If
the user cannot be identified, it uses global settings and
output/unknown/videos/.
# Monitor a single user
broadcastx monitor @username
# Monitor multiple users simultaneously (shares one Chromium profile)
broadcastx monitor @SpaceX @NASA @elonmusk
# Monitor every [[users]] entry in broadcastx.toml
broadcastx monitor
# Select one configured user while retaining that user's settings
broadcastx monitor Bob
# One-shot test cycle (no loop)
broadcastx monitor @username --once
# Use a custom output root
broadcastx monitor @username --output-dir ./archive
# Custom check intervals (seconds)
broadcastx monitor @username --check-interval 1800 --live-interval 300
# Detect only, skip download
broadcastx monitor @username --no-downloadThe monitor runs in a loop:
- Profile check (every
check-interval, default 30 min) β Opens each profile and looks for broadcast cards. - Live detection β When a candidate is found, checks whether it is currently live.
- Live check (every
live-interval, default 5 min) β Re-checks status until the broadcast ends. - Download β Downloads the replay automatically.
Multiple users share a single Chromium profile, so you only need to log in once.
Events are logged per user to output/<username>/monitor_events.json.
# Add a default "broadcastx" watermark to the bottom-right of a video
broadcastx watermark video.mp4
# Custom text, font, and size
broadcastx watermark video.mp4 --text "My Channel" --font Arial --font-size 36
# Custom opacity, color, and position
broadcastx watermark video.mp4 --opacity 0.5 --color "#FF5733" --position top-left
# Write to a new file instead of overwriting
broadcastx watermark video.mp4 --output watermarked.mp4
# Dry-run to preview settings without running ffmpeg
broadcastx watermark video.mp4 --dry-runThe watermark command uses ffmpeg's drawtext filter. Stock ffmpeg builds include libfreetype and support this filter out of the box.
Options:
| Flag | Short | Default | Description |
|---|---|---|---|
--text |
-t |
broadcastx |
Watermark text |
--font |
-f |
sans-serif |
Font family or path |
--font-size |
-s |
24 |
Font size in points |
--opacity |
-o |
0.7 |
Text opacity (0.0β1.0) |
--color |
-c |
white |
Font color name or hex |
--position |
-p |
bottom-right |
One of: bottom-right, bottom-left, top-right, top-left |
--output |
-O |
(in-place) | Output video path |
--dry-run |
(off) | Preview without executing |
output/
βββ alice/
β βββ broadcasts.json
β βββ monitor_events.json
β βββ videos/
β βββ 2026-08-28_22-00-00_[id].mp4
β βββ 2026-08-28_22-00-00_[id].rotation.jsonl
βββ bob/
βββ ...
# Scan + download all found broadcasts
broadcastx scan @username
broadcastx download --from output/username/broadcasts.json
# Monitor with auto-download
broadcastx monitor @username --output-dir ./archive
# Watermark a downloaded video
broadcastx watermark ./output/username/videos/*.mp4 --text "broadcastx" --output ./output/username/videos/watermarked/Uses Playwright to intercept timeline GraphQL responses generated by X itself. It tracks target-authored post IDs for progress and extracts broadcasts only from posts attributable to the requested user, without depending on a fixed GraphQL operation name.
Wraps yt-dlp (which has a built-in TwitterBroadcastIE extractor) and adds:
- Rotation sidecar extraction β Parses timed-ID3 metadata from HLS segments
- Auto-rotation β Re-encodes the video with correct orientation via ffmpeg
The JSONL sidecar shares its filename stem with the MP4 and contains one record per HLS segment:
raw_rotationβ Original sensor angle from Periscoperotationβ Quantized to 0Β°, 90Β°, 180Β°, or 270Β° with hysteresisntpβ NTP timestamp for timeline reconstruction
MIT