GoondVR
A self-hosted web UI and CLI for recording public livestreams from Chaturbate and Stripchat. Supports macOS, Windows, Linux, and Docker. Favicon from Twemoji.
[!IMPORTANT]
The original teacat/chaturbate-dvr repository is archived and no longer maintained. This fork remains active and includes ongoing fixes, new releases, and additional features like Stripchat support.
Features added in this version include:
- Updated web UI
- Live stream thumbnails for streaming channels and previews for offline channels
- Stripchat support
- Discord webhook and
ntfy notifications
Example dashboard and settings views from the current web UI.





Getting Started
Go to the π¦ Releases page and download the appropriate binary. (e.g., windows_amd64_goondvr.exe)
π Launching the Web UI
# Windows
$ windows_amd64_goondvr.exe
# macOS (Intel)
$ ./darwin_amd64_goondvr
# macOS (Apple Silicon)
$ ./darwin_arm64_goondvr
# Linux
$ ./linux_amd64_goondvr
Then visit http://localhost:8080 in your browser.
# Windows
$ windows_amd64_goondvr.exe -u CHANNEL_USERNAME --site chaturbate
# macOS (Intel)
$ ./darwin_amd64_goondvr -u CHANNEL_USERNAME --site chaturbate
# macOS (Apple Silicon)
$ ./darwin_arm64_goondvr -u CHANNEL_USERNAME --site chaturbate
# Linux
$ ./linux_amd64_goondvr -u CHANNEL_USERNAME --site chaturbate
This starts recording immediately. The Web UI will be disabled.
π³ Running with Docker
Pre-built image from GitHub Container Registry:
Persist ./videos for recordings and ./conf for saved channels and settings.
By default, closed recordings are moved into a completed subdirectory under the recording directory after they are finalized.
The Docker image built from this repository includes ffmpeg so remux/transcode mode works out of the box there.
# Run the container and save videos to ./videos
$ docker run -d \
--name my-dvr \
--restart unless-stopped \
-p 8080:8080 \
-v "./videos:/usr/src/app/videos" \
-v "./conf:/usr/src/app/conf" \
ghcr.io/heapofchaos/goondvr:latest
...Or build your own image using the Dockerfile in this repository.
# Build the image
$ docker build -t goondvr .
# Run the container and save videos to ./videos
$ docker run -d \
--name my-dvr \
--restart unless-stopped \
-p 8080:8080 \
-v "./videos:/usr/src/app/videos" \
-v "./conf:/usr/src/app/conf" \
goondvr
...Or use docker-compose.yml:
$ docker compose up -d
Then visit http://localhost:8080 in your browser.
π§Ύ Command-Line Options
Available options:
--username value, -u value The username of the channel to record
--site value Site to record from: chaturbate or stripchat (default: "chaturbate")
--admin-username value Username for web authentication (optional)
--admin-password value Password for web authentication (optional)
--framerate value Desired framerate (FPS) (default: 30)
--resolution value Desired resolution (e.g., 1080 for 1080p) (default: 1080)
--pattern value Template for naming recorded videos (default: "videos/{{if ne .Site \"chaturbate\"}}{{.Site}}/{{end}}{{.Username}}_{{.Year}}-{{.Month}}-{{.Day}}_{{.Hour}}-{{.Minute}}-{{.Second}}{{if .Sequence}}_{{.Sequence}}{{end}}")
--max-duration value Split video into segments every N minutes ('0' to disable) (default: 0)
--max-filesize value Split video into segments every N MB ('0' to disable) (default: 0)
--port value, -p value Port for the web interface and API (default: "8080")
--interval value Check if the channel is online every N minutes (default: 1)
--cookies value Cookies to use in the request (format: key=value; key2=value2)
--user-agent value Custom User-Agent for the request
--stripchat-pdkey value Manually specify Stripchat pdkey if keys have rotated
--domain value Chaturbate domain to use (default: "https://chaturbate.com/")
--completed-dir value Directory to move fully closed recordings into (default: <recording dir>/completed)
--finalize-mode value Post-process closed recordings: none, remux, or transcode (default: "none")
--ffmpeg-encoder value FFmpeg video encoder for transcode mode (default: "libx264")
--ffmpeg-container value FFmpeg output container for remux/transcode mode: mp4 or mkv (default: "mp4")
--ffmpeg-quality value FFmpeg quality value; CRF for software encoders, CQ/global quality for many hardware encoders (default: 23)
--ffmpeg-preset value FFmpeg preset for transcode mode (default: "medium")
--debug Dump full HTML to a temp file when stream detection fails, for diagnosing Cloudflare blocks
--help, -h show help
--version, -v print the version
Examples:
# Record at 720p / 60fps
$ ./goondvr -u yamiodymel -resolution 720 -framerate 60
# Record from Stripchat
$ ./goondvr -u some_model --site stripchat
# Split every 30 minutes
$ ./goondvr -u yamiodymel -max-duration 30
# Split at 1024 MB
$ ./goondvr -u yamiodymel -max-filesize 1024
# Custom filename format
$ ./goondvr -u yamiodymel \
-pattern "video/{{.Username}}/{{.Year}}-{{.Month}}-{{.Day}}_{{.Hour}}-{{.Minute}}-{{.Second}}_{{.Sequence}}"
# Move closed recordings to another disk
$ ./goondvr -u yamiodymel \
-completed-dir "/mnt/archive/goondvr-completed"
# Remux finalized recordings with ffmpeg for better seekability
$ ./goondvr -u yamiodymel \
-finalize-mode remux
# Transcode finalized recordings to save space
$ ./goondvr -u yamiodymel \
-finalize-mode transcode \
-ffmpeg-encoder libx264 \
-ffmpeg-quality 23 \
-ffmpeg-preset medium
Note: In Web UI mode, these flags serve as default values for new channels.
When the app is stopped with Ctrl+C or the container shuts down, it now waits for active recordings to close and for any finalization moves to finish before exiting.
For remux and transcode, ffmpeg must be installed and available on PATH. If you choose a hardware encoder such as h264_nvenc, hevc_nvenc, h264_qsv, or h264_vaapi, the host and container also need compatible GPU/device access. If FFmpeg or the selected encoder is unavailable, the app keeps the original recording instead of deleting it.
Common encoder examples: libx264, libx265, h264_nvenc, hevc_nvenc, h264_qsv, h264_vaapi.
To prevent file collisions, channels whose filename patterns would resolve to the same output path cannot be added or loaded at the same time. If you record the same username on multiple sites, make sure the pattern includes {{.Site}} or otherwise produces distinct paths.
On startup, the app will automatically migrate the old default filename pattern to the newer site-aware default when that is enough to resolve a cross-site filename collision. Custom conflicting patterns are not rewritten automatically and will still need manual adjustment.
πͺ Cookies & User-Agent
You can set Cookies and User-Agent via the Web UI or command-line arguments.

Note: Use semicolons to separate multiple cookies, e.g., key1=value1; key2=value2.
βοΈ Bypass Cloudflare
-
Open Chaturbate in your browser and complete the Cloudflare check.
(Keep refresh with F5 if the check doesn't appear)
-
DevTools (F12) β Application β Cookies β https://chaturbate.com β Copy the cf_clearance value

-
User-Agent can be found using WhatIsMyBrowser, now run with -cookies and -user-agent:
$ ./goondvr -u yamiodymel \
-cookies "cf_clearance=PASTE_YOUR_CF_CLEARANCE_HERE" \
-user-agent "PASTE_YOUR_USER_AGENT_HERE"
Example:
$ ./goondvr -u yamiodymel \
-cookies "cf_clearance=i975JyJSMZUuEj2kIqfaClPB2dLomx3.iYo6RO1IIRg-1746019135-1.2.1.1-2CX..." \
-user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64)..."
π΅οΈ Record Private Shows
-
Login Chaturbate in your browser.
-
DevTools (F12) β Application β Cookies β https://chaturbate.com β Copy the sessionid value
-
Run with -cookies:
$ ./goondvr -u yamiodymel -cookies "sessionid=PASTE_YOUR_SESSIONID_HERE"
π Filename Pattern
The format is based on Go Template Syntax, available variables are:
{{.Username}}, {{.Site}}, {{.Year}}, {{.Month}}, {{.Day}}, {{.Hour}}, {{.Minute}}, {{.Second}}, {{.Sequence}}
By default, it hides the sequence if it's zero.
Pattern: {{if ne .Site "chaturbate"}}{{.Site}}/{{end}}{{.Username}}_{{.Year}}-{{.Month}}-{{.Day}}_{{.Hour}}-{{.Minute}}-{{.Second}}{{if .Sequence}}_{{.Sequence}}{{end}}
Output: yamiodymel_2024-01-02_13-45-00.ts # Sequence won't be shown if it's zero.
Output: stripchat/yamiodymel_2024-01-02_13-45-00_1.ts
π or... The sequence can be shown even if it's zero.
Pattern: {{.Username}}_{{.Year}}-{{.Month}}-{{.Day}}_{{.Hour}}-{{.Minute}}-{{.Second}}_{{.Sequence}}
Output: yamiodymel_2024-01-02_13-45-00_0.ts
Output: yamiodymel_2024-01-02_13-45-00_1.ts
π or... Folder per each channel.
Pattern: video/{{.Username}}/{{.Year}}-{{.Month}}-{{.Day}}_{{.Hour}}-{{.Minute}}-{{.Second}}_{{.Sequence}}
Output: video/yamiodymel/2024-01-02_13-45-00_0.ts
Note: Legacy HLS streams are saved as .ts. LL-HLS streams are saved as .mp4 with muxed video and audio.
π€ Frequently Asked Questions
Q: The program closes immediately on Windows.
Open it via Command Prompt, the error message should appear. If needed, create an issue.
Β
Q: Error listen tcp :8080: bind: An attempt was... by its access permissions
The port 8080 is in use. Try another port with -p 8123, then visit http://localhost:8123.
If that fails, run Command Prompt as Administrator and execute:
$ net stop winnat
$ net start winnat
Q: Error A connection attempt failed... host has failed to respond
Likely a network issue (e.g., VPN, firewall, or blocked by Chaturbate). This cannot be fixed by the program.
Q: Error Channel was blocked by Cloudflare
You've been temporarily blocked. See the Cookies & User-Agent section to bypass.
Β
Q: Is Proxy or SOCKS5 supported?
Yes. You can launch the program using the HTTPS_PROXY environment variable:
$ HTTPS_PROXY="socks5://127.0.0.1:9050" ./goondvr -u CHANNEL_USERNAME