Running CaptureGem Recorderd Standalone on Linux
This guide explains how to run CaptureGem's recording daemon (recorderd) as a standalone process on Linux (e.g. Ubuntu), without the Electron desktop app. This is useful for headless servers or remote machines where you want to control recordings via the HTTP API.
Prerequisites
- A Linux x86_64 system (e.g. Ubuntu 20.04+)
- At least 1 GB of free disk space for binaries
- Sufficient storage for recorded videos
Step 1: Download the Required Binaries
You need the following files: recorderd, transcoder, ffmpeg, ffprobe, curl, and cacert.pem. If you have the CaptureGem Linux AppImage, you can extract them from it. Otherwise, download them individually.
Option A: Extract from AppImage
If you have the CaptureGem .AppImage file:
mkdir -p bin
chmod +x CaptureGem-*.AppImage
./CaptureGem-*.AppImage --appimage-extract
cp squashfs-root/resources/resources/recorderd ./bin/
cp squashfs-root/resources/resources/transcoder ./bin/
cp squashfs-root/resources/resources/ffmpeg ./bin/
cp squashfs-root/resources/resources/ffprobe ./bin/
cp squashfs-root/resources/resources/curl ./bin/
cp squashfs-root/resources/resources/cacert.pem ./bin/
rm -rf squashfs-root
Option B: Download Individually
FFmpeg (static build):
mkdir -p bin
wget https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-linux64-gpl-7.1.tar.xz
tar xf ffmpeg-n7.1-latest-linux64-gpl-7.1.tar.xz
cp ffmpeg-n7.1-latest-linux64-gpl-7.1/bin/ffmpeg ./bin/
cp ffmpeg-n7.1-latest-linux64-gpl-7.1/bin/ffprobe ./bin/
rm -rf ffmpeg-n7.1-latest-linux64-gpl-7.1*
Static curl (for HTTPS requests):
wget -O ./bin/curl https://github.com/moparisthebest/static-curl/releases/latest/download/curl-amd64
chmod +x ./bin/curl
CA Certificates:
wget -O ./bin/cacert.pem https://curl.se/ca/cacert.pem
The recorderd and transcoder binaries must come from a CaptureGem release (AppImage extraction) or be compiled from source.
Set Permissions
Make sure all binaries are executable:
chmod +x ./bin/recorderd ./bin/transcoder ./bin/ffmpeg ./bin/ffprobe ./bin/curl
Step 2: Directory Structure
Your bin directory setup should look like this:
capturegem/
└── bin/
├── recorderd
├── transcoder
├── ffmpeg
├── ffprobe
├── curl
└── cacert.pem
By default, recorderd stores configuration and recorded videos in ~/CaptureGem_Data/. You can change the video save directory by using the ChangeGlobalSettings API call after starting the daemon.
Step 3: Start recorderd
Run the daemon with the required flags:
./bin/recorderd -http 28471 -ws 28472 -binDir ./bin
Startup Flags
Flag Required Description -http Yes Port for the HTTP API server -ws Yes Port for the WebSocket server (used for real-time state updates) -proxy No Port for the proxy recording server -binDir No Path to the directory containing ffmpeg, transcoder, and curl -defaultPorts No Use default ports instead of specifying them manually -startupDiag No Enable/disable startup diagnostics (default: true)
Running in the Background
To keep recorderd running after you close your terminal:
nohup ./bin/recorderd -http 28471 -ws 28472 -binDir ./bin > recorderd.log 2>&1 &
Or create a systemd service (see "Running as a systemd Service" below).
Step 4: Verify It's Running
Once started, check that recorderd is responding:
curl http://localhost:28471/getVersion
You should see a JSON response with the version number.
Running as a systemd Service
Create a service file at /etc/systemd/system/recorderd.service:
[Unit]
Description=CaptureGem Recording Daemon
After=network.target
[Service]
Type=simple
User=your-username
WorkingDirectory=/home/your-username/capturegem
ExecStart=/home/your-username/capturegem/bin/recorderd -http 28471 -ws 28472 -binDir /home/your-username/capturegem/bin
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Then enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable recorderd
sudo systemctl start recorderd
sudo systemctl status recorderd
HTTP API Reference
Once recorderd is running, you can control it entirely via HTTP. All GET endpoints use query parameters. All POST endpoints (except /forceUnlockConfigFile) accept a JSON body with a single top-level field that determines the action.
GET Endpoints
State & Configuration
Endpoint Description /getVersion Returns the application version /getInitialState Returns full global state (config + all model states) /getDefaultConfig Returns the default configuration /getQueueState?filter=... Returns transcoding queue, ffmpeg queue, and API request queue state. Supports ETag caching /getSystemMonitorHistory Returns system monitoring event history /getRecordingLifecycleHistory Returns the recording lifecycle audit history
Video Metadata
Endpoint Description /getAllMetadata?page=1&entries=20 Returns paginated video metadata. Supports filters: tags, negativeTags, modelName, site, search, sort, startDate, endDate. Supports ETag caching /getAuditFiles?videoId=... Returns audit files (playlist history, recording stats, transcoder audit) for a video /fetchImageGrid?videoId=...&size=small Serves an image grid thumbnail for a video (small or large)
Preview & Monitoring
Endpoint Description /getPreviewFiles Returns all live preview image files. Supports If-None-Match / 304 caching /fetchPreviewImage?previewFile=...&isCacheFile=false Serves a preview image file /getFakeOfflineModels Returns sorted list of models configured to appear as fake-offline
Proxy
Endpoint Description /getProxyStreams Returns currently active proxy video streams /getProxyStreamFiles?streamId=... Returns file info for a proxy stream's saved directory /getProxyStats Returns proxy server statistics
Utilities
Endpoint Description /getRequestConfig?site=... Returns decoded cURL request config for a cam site /testUrlFetchChain Tests the URL fetch communication chain
POST Endpoints
All POST requests use a JSON body. The action is determined by which field is present in the JSON object.
Model Management
# Add a model
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"AddModel": {"username": "example_user", "site": "Chaturbate"}}'
# Remove a model
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"RemoveModel": {"username": "example_user", "site": "Chaturbate"}}'
# Change model settings (e.g. enable auto-record)
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ChangeModelSettings": {"username": "example_user", "site": "Chaturbate", "autoRecord": true}}'
# Add/remove fake offline
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"AddFakeOfflineModel": {"username": "example_user", "site": "Chaturbate"}}'
Recording Control
# Start recording
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"StartRecording": {"username": "example_user", "site": "Chaturbate"}}'
# Stop recording
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"StopRecording": {"username": "example_user", "site": "Chaturbate"}}'
# Pause recording
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"PauseRecording": {"username": "example_user", "site": "Chaturbate"}}'
# Start monitoring (check online status without recording)
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"StartMonitoring": {"username": "example_user", "site": "Chaturbate"}}'
Recording Groups
# Create a recording group
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"CreateRecordingGroup": {"models": [{"username": "user1", "site": "Chaturbate"}]}}'
# Update a recording group
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"UpdateRecordingGroup": {"groupId": "group-id-here"}}'
# Disassociate models from a group
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"DisassociateRecordingGroup": {"models": [{"username": "user1", "site": "Chaturbate"}]}}'
Video Management
# Delete videos
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"DeleteVideo": {"filenames": ["video1.mp4", "video2.mp4"]}}'
# Cut/trim a video
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"CutVideo": {"videoId": "my-video-id", "cutTimes": [10, 60]}}'
# Concatenate videos
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ConcatVideo": {"filenames": ["video1.mp4", "video2.mp4"]}}'
# Export/copy videos to a directory
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ExportVideos": {"filenames": ["video1.mp4"], "destination": "/path/to/backup"}}'
# Update video metadata
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"UpdateVideoMetadata": {"filename": "video.mp4", "modelName": "example_user", "siteName": "Chaturbate", "recordTime": "2026-03-09T12:00:00Z"}}'
# Add boolean metadata tags
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"AddVideoBoolMetadata": {"videoId": "my-video-id", "tags": ["favorite"]}}'
Import & Export Models
# Export models to CSV
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ExportModels": {}}'
# Import models from CSV
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ImportModels": {"filePath": "/path/to/models.csv"}}'
Proxy Recording
# Start proxy recording
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"StartProxyRecording": {"streamId": "stream-123"}}'
# Stop proxy recording (auto-imports)
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"StopProxyRecording": {"streamId": "stream-123"}}'
# Manually import proxy recording
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ImportProxyRecording": {"streamId": "stream-123", "site": "Chaturbate", "username": "example_user"}}'
Configuration
# Change global settings
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ChangeGlobalSettings": {"videoSaveDirectory": "/path/to/videos"}}'
# Reset to default configuration
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ResetToDefaultConfig": {}}'
# Set active tab
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"SetActiveTab": {"tab": "dashboard"}}'
Authentication & Licensing
# Activate via Patreon email
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"EnterPatreon": {"email": "[email protected]"}}'
# Request email verification code (v2 licensing)
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"RequestEmailCode": {"email": "[email protected]"}}'
# Submit email verification code
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"SubmitEmailCode": {"email": "[email protected]", "code": "123456"}}'
# Chaturbate login
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"ChaturbateLogin": {"username": "...", "password": "..."}}'
# Filebaron authentication
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"EnterFilebaronAuth": {"email": "...", "password": "..."}}'
Config Lock
# Force unlock config file (URL-path based, not JSON field)
curl -X POST http://localhost:28471/forceUnlockConfigFile
Utilities
# Test a cURL request for a site
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"TestCurlRequest": {"site": "Chaturbate", "request": "base64-encoded-curl-request", "testType": "example"}}'
# Forward a URL fetch request
curl -X POST http://localhost:28471/ \
-H "Content-Type: application/json" \
-d '{"UrlFetchRequest": {"method": "GET", "url": "https://example.com", "headers": {}, "body": ""}}'
WebSocket
Connect to the WebSocket server on the -ws port for real-time state updates:
# Using websocat (install with: cargo install websocat)
websocat ws://127.0.0.1:28472/ws
The WebSocket pushes state changes as they happen (model status changes, recording progress, etc.), which is the same mechanism the desktop UI uses to stay in sync.
Troubleshooting
- "bin directory not set": Make sure you're passing
-binDirpointing to the directory with ffmpeg/transcoder - Transcoder version check failed: Ensure the
transcoderbinary is in the bin directory and is executable - Permission denied: Run
chmod +xon all binaries in the bin directory - Port already in use: Choose different port numbers for
-httpand-ws - Config file locked: Use the
/forceUnlockConfigFilePOST endpoint to release the lock


