Skip to content

numan7272/CaptureStream

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CaptureStream

CaptureStream is a lightweight, self-hosted live-stream recorder written in Python. It continuously polls a configured list of creators on Twitch, YouTube Live and TikTok Live and, as soon as one of them goes live, records the stream as a local VOD.

  • Web UI — add a streamer by pasting a URL or typing a username, just like streamrecorder.io, and watch live status in real time.
  • One thread per streamer — detections and recordings run in parallel.
  • Polite polling with jitter so you stay well below platform rate limits.
  • Automatic reconnect if a stream briefly drops mid-broadcast.
  • Clean, structured output: Recordings/<platform>/<streamer>/<date>_<time>_*.mp4.
  • Built-in recordings browser with direct downloads.

Project layout

CaptureStream/
├── main.py                 # Entry point + loop manager
├── config.json             # Example config (streamers + general settings)
├── requirements.txt        # Python dependencies
├── recorder/               # Platform logic (checkers + downloaders)
│   ├── __init__.py
│   ├── base.py             # BaseRecorder + StreamInfo + state
│   ├── manager.py          # Runtime add/remove + config persistence
│   ├── twitch.py           # Twitch checker (GQL) + streamlink recorder
│   ├── youtube.py          # YouTube Live probe + streamlink recorder
│   ├── tiktok.py           # TikTok Live probe + yt-dlp recorder
│   └── utils.py            # Logging, filename helpers, URL parser
├── web/                    # Web UI
│   ├── server.py           # Flask REST API + werkzeug server
│   └── static/             # index.html, app.js, style.css
└── README.md

Requirements

  • Python 3.10+
  • streamlink (used for Twitch and YouTube capture)
  • yt-dlp (used for TikTok capture — handles both HLS and FLV)
  • ffmpeg on your PATH (both tools use it for muxing)

The Python dependencies are listed in requirements.txt.

Installation

Clone the repository and create an isolated virtual environment:

git clone https://github.com/<your-user>/CaptureStream.git
cd CaptureStream

python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

pip install -r requirements.txt

Make sure ffmpeg is installed and available on your PATH:

streamlink and yt-dlp are installed from requirements.txt and expose CLI binaries inside the active virtual environment. If you install them system-wide instead, make sure both are reachable from your shell (streamlink --version, yt-dlp --version).

Configuration

Edit config.json (or pass your own file via --config). Example:

{
    "general": {
        "output_dir": "Recordings",
        "check_interval_seconds": 240,
        "jitter_seconds": 30,
        "stream_quality": "best",
        "log_level": "INFO",
        "filename_template": "{date}_{time}_{streamer}_{title}.mp4",
        "max_filename_length": 180
    },
    "streamers": [
        { "platform": "twitch",  "username": "shroud",    "enabled": true  },
        { "platform": "youtube", "username": "@LofiGirl", "enabled": true  },
        { "platform": "tiktok",  "username": "tiktok",    "enabled": false }
    ]
}

Notes:

  • check_interval_seconds is the base poll cadence per streamer (default 4 min). A random jitter_seconds offset is added on each cycle to avoid synchronised bursts. The minimum effective interval is 60 s.
  • stream_quality is passed to streamlink (e.g. best, 720p, audio_only).
  • filename_template supports the placeholders {date}, {time}, {streamer}, {title}, {platform}. Invalid filesystem characters are replaced automatically.
  • YouTube username accepts handles (@name), channel slugs, or channel IDs (UC…, 24 characters).
  • TikTok username is the @ handle (with or without the @ prefix).

YAML configs are also supported if you install PyYAML (already in the requirements) — just pass --config config.yaml.

Running

By default, CaptureStream starts both the polling manager and the web UI (on http://127.0.0.1:7878):

python main.py
# or with a custom config / host / port:
python main.py --config my-config.json --host 0.0.0.0 --port 8080
# headless (no web UI):
python main.py --no-web

You should see log output like:

2026-04-23 12:34:56 [INFO] manager :: Watching 2 streamer(s); poll interval 240s (±30s).
2026-04-23 12:34:56 [INFO] web     :: Web UI available at http://127.0.0.1:7878/
2026-04-23 12:34:57 [INFO] twitch:shroud :: Watching — waiting for streams…
2026-04-23 12:39:01 [INFO] twitch:shroud :: Live detected (VALORANT ranked grind) — starting recording.
2026-04-23 12:39:01 [INFO] twitch:shroud :: Recording to Recordings/twitch/shroud/2026-04-23_12-39-01_shroud_VALORANT ranked grind.mp4

Press Ctrl+C (or send SIGTERM) to stop gracefully — active recordings are terminated cleanly and any in-flight file is flushed.

Using the web UI

Open http://127.0.0.1:7878 in your browser. You can:

  • Add a streamer by pasting a channel URL — Twitch, YouTube, or TikTok:
    • https://twitch.tv/<channel>
    • https://youtube.com/@<handle> or https://youtube.com/channel/UC…
    • https://tiktok.com/@<username>
  • Add by username: type just the name (e.g. shroud) and pick the platform from the dropdown. YouTube handles without @ are auto-prefixed.
  • See live status update every few seconds: waiting, live, recording, or error, with each streamer's last-check timestamp.
  • Stop watching a streamer with the Remove button — the change is persisted back to config.json immediately.
  • Download recordings from the Recordings table; files stream directly from the configured output_dir.

Changes made through the UI are written to config.json atomically (temp file

  • rename) so you always have a consistent snapshot on disk.

REST API

The UI is a thin client over this JSON API, which you can use from your own scripts too:

Method Path Description
GET /api/streamers List watched streamers with status.
POST /api/streamers Body: `{ "input": "", "platform": "twitch
DELETE /api/streamers/<id> Stop watching. <id> is platform:username.
GET /api/recordings List recorded files.
GET /recordings/<path> Download a recorded file (sandboxed to output_dir).
GET /api/health Liveness probe.

Security notes

  • The server binds to 127.0.0.1 by default. Only bind to 0.0.0.0 on a trusted network — there is no authentication.
  • The download endpoint resolves and re-verifies every path against output_dir, and only serves files with known media suffixes (.mp4, .mkv, .ts, .flv, .webm).

How reconnection works

Inside BaseRecorder.record each platform's downloader (streamlink/yt-dlp) is already configured with its own per-segment retry flags. If the downloader itself exits with a non-zero status while the stream is still live, CaptureStream re-checks the live state and relaunches it — up to MAX_RECONNECT_ATTEMPTS (5) times per session, with a short backoff between attempts. Once the recheck reports "not live", the recorder returns to the polling state.

Disclaimer

This project is intended for personal, archival use of content you are allowed to record (for example, your own streams or content under an appropriate license). Always respect each platform's Terms of Service and the creators' rights when using this tool.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors