Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 96 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ Then open `https://localhost:8090` for the dashboard.

## CLI Options

| Option | Description | Default |
|---|---|---|
| `-f`, `--file` | Room list config | `list.conf` |
| `-o`, `--output` | Output directory | `out` |
| `-t`, `--tmp` | Temp directory | `tmp` |
| `-p`, `--port` | HTTP server port | `8090` |
| `-u`, `--users` | Users file | `users.txt` |
| `-post` | Post processor config | `postprocessor.json` |
| Option | Description | Default |
|------------------|-----------------------|----------------------|
| `-f`, `--file` | Room list config | `list.conf` |
| `-o`, `--output` | Output directory | `out` |
| `-t`, `--tmp` | Temp directory | `tmp` |
| `-p`, `--port` | HTTP server port | `8090` |
| `-u`, `--users` | Users file | `users.txt` |
| `-post` | Post processor config | `postprocessor.json` |

```shell
java -jar build/libs/XhRec-all.jar -p 12340 -f list.conf -post postprocessor.json -t /tmp/xhrec -o /out
Expand All @@ -40,13 +40,13 @@ One room per line. Lines starting with `#` or `;` are inactive (not automaticall
https://stripchat.com/modelC q:highest
```

| Field | Description |
|---|---|
| `q:<quality>` | Preferred quality: `240p`, `480p`, `720p`, `720p60`, `1080p`, `1080p60`, or `highest` (default). `raw` is deprecated, use `highest` instead |
| `limit:<sec>` | Recording time limit in seconds |
| `size:<bytes>` | Recording size limit (supports suffixes: `K`, `M`, `G`, e.g. `500M`) |
| `autopay` | Enable auto-payment for private shows |
| `pkey:<key>` | Custom psch key |
| Field | Description |
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| `q:<quality>` | Preferred quality: `240p`, `480p`, `720p`, `720p60`, `1080p`, `1080p60`, or `highest` (default). `raw` is deprecated, use `highest` instead |
| `limit:<sec>` | Recording time limit in seconds |
| `size:<bytes>` | Recording size limit (supports suffixes: `K`, `M`, `G`, e.g. `500M`) |
| `autopay` | Enable auto-payment for private shows |
| `pkey:<key>` | Custom psch key |

If the requested quality is unavailable, the closest match is selected automatically.

Expand All @@ -65,17 +65,18 @@ Stream decryption key store. Created with defaults on first run if absent.
}
```

| Field | Description |
|---|---|
| `streamAuthKey` | Default psch key for stream auth |
| Field | Description |
|---------------------|----------------------------------------------------------------------------------------------------------------------|
| `streamAuthKey` | Default psch key for stream auth |
| `maskSensitiveLogs` | Enable log masking (model names, cookies, tokens, proxy URLs). Toggle in WebUI or via `/mask/toggle`. Default `true` |
| `decryptKeys` | Key-value map of decryption keys (psch key → key) |
| `decryptKeys` | Key-value map of decryption keys (psch key → key) |

### users.txt

User cookies for auto-payment. One cookie per line. Lines starting with `#` or `;` are ignored.

Each cookie is validated against the platform API on startup to resolve the user's ID, name, and coin balance. When a private show requires payment, the system selects a user with sufficient coins.
Each cookie is validated against the platform API on startup to resolve the user's ID, name, and coin balance. When a
private show requires payment, the system selects a user with sufficient coins.

```
# optional comments
Expand All @@ -101,50 +102,50 @@ All endpoints return JSON unless noted. Parameters are passed as query strings.

### Room Management

| Endpoint | Params | Description |
|---|---|---|
| `/add` | `name`, `quality`, `active`, `limit`, `autopay`, `pkey`, `size` | Add a room |
| `/remove` | `id` | Remove a room |
| `/start` | `id` | Start recording |
| `/stop` | `id` | Stop recording |
| `/restart` | `id` | Stop then restart recording |
| `/break` | `id` | Temporary stop (resumes on next poll) |
| Endpoint | Params | Description |
|------------|-----------------------------------------------------------------|---------------------------------------|
| `/add` | `name`, `quality`, `active`, `limit`, `autopay`, `pkey`, `size` | Add a room |
| `/remove` | `id` | Remove a room |
| `/start` | `id` | Start recording |
| `/stop` | `id` | Stop recording |
| `/restart` | `id` | Stop then restart recording |
| `/break` | `id` | Temporary stop (resumes on next poll) |

### Room Settings

| Endpoint | Params | Description |
|---|---|---|
| `/activate` | `id` | Enable auto-recording |
| `/deactivate` | `id` | Disable auto-recording |
| `/quality` | `id`, `q` | Set quality |
| `/autopay` | `id`, `v` (true/false) | Toggle auto-payment |
| `/limit` | `id`, `v` (seconds) | Set time limit (0 = unlimited) |
| `/sizelimit` | `id`, `v` | Set size limit (0 = unlimited) |
| Endpoint | Params | Description |
|---------------|------------------------|--------------------------------|
| `/activate` | `id` | Enable auto-recording |
| `/deactivate` | `id` | Disable auto-recording |
| `/quality` | `id`, `q` | Set quality |
| `/autopay` | `id`, `v` (true/false) | Toggle auto-payment |
| `/limit` | `id`, `v` (seconds) | Set time limit (0 = unlimited) |
| `/sizelimit` | `id`, `v` | Set size limit (0 = unlimited) |

### Status & Monitoring

| Endpoint | Description |
|---|---|
| `/status` | Active room status (segments, bytes, running downloads) |
| `/list` | All rooms with status, session state, quality |
| `/dashboard` | Consolidated payload: rooms, statuses, listv2, metrics |
| `/metrics` | Prometheus metrics endpoint |
| Endpoint | Description |
|--------------|---------------------------------------------------------|
| `/status` | Active room status (segments, bytes, running downloads) |
| `/list` | All rooms with status, session state, quality |
| `/dashboard` | Consolidated payload: rooms, statuses, listv2, metrics |
| `/metrics` | Prometheus metrics endpoint |

| `/mask/toggle` | Toggle log masking on/off |
| `/mask/status` | Get current mask status (true/false) |

### Live Preview

| Endpoint | Params | Description |
|---|---|---|
| `/mse/live` | `id` | MP4 stream of in-progress recording |
| Endpoint | Params | Description |
|-------------|--------|-------------------------------------|
| `/mse/live` | `id` | MP4 stream of in-progress recording |

### Server Control

| Endpoint | Description |
|---|---|
| `/graceful-stop` | Finish recordings and shut down |
| `/stop-server` | Finish recordings and post-processing, then exit |
| Endpoint | Description |
|------------------|--------------------------------------------------|
| `/graceful-stop` | Finish recordings and shut down |
| `/stop-server` | Finish recordings and post-processing, then exit |

### Status Response Format

Expand All @@ -171,55 +172,69 @@ Defined in `postprocessor.json`. Processors run in sequence after a recording fi

### Built-in Processors

| Type | Description |
|---|---|
| `fix_stamp` | Fix MP4 timestamps |
| `move` | Move/rename output files |
| `slice` | Split video into segments |
| `shell` | Run arbitrary shell commands |
| Type | Description |
|-------------|------------------------------|
| `fix_stamp` | Fix MP4 timestamps |
| `move` | Move/rename output files |
| `slice` | Split video into segments |
| `shell` | Run arbitrary shell commands |

### Template Variables

Available in `move` destinations and `shell` arguments:

| Variable | Description |
|---|---|
| `{{ROOM_NAME}}` | Model/room name |
| `{{ROOM_ID}}` | Room ID |
| `{{RECORD_START}}` | Formatted start time |
| `{{RECORD_END}}` | Formatted end time |
| `{{RECORD_DURATION}}` | Duration in seconds |
| `{{RECORD_DURATION_STR}}` | Duration as `00h01m30s` |
| `{{RECORD_QUALITY}}` | Quality string |
| `{{INPUT_ABS}}` | Input file path |
| `{{INPUT_DIR}}` | Input directory |
| `{{INPUT_NAME}}` | Input filename |
| `{{INPUT_NAME_NOEXT}}` | Filename without extension |
| `{{TOTAL_FRAMES}}` | Accurate frame count |
| `{{TOTAL_FRAMES_GUESS}}` | Estimated frame count (FPS × duration) |
| Variable | Description |
|---------------------------|----------------------------------------|
| `{{ROOM_NAME}}` | Model/room name |
| `{{ROOM_ID}}` | Room ID |
| `{{RECORD_START}}` | Formatted start time |
| `{{RECORD_END}}` | Formatted end time |
| `{{RECORD_DURATION}}` | Duration in seconds |
| `{{RECORD_DURATION_STR}}` | Duration as `00h01m30s` |
| `{{RECORD_QUALITY}}` | Quality string |
| `{{INPUT_ABS}}` | Input file path |
| `{{INPUT_DIR}}` | Input directory |
| `{{INPUT_NAME}}` | Input filename |
| `{{INPUT_NAME_NOEXT}}` | Filename without extension |
| `{{TOTAL_FRAMES}}` | Accurate frame count |
| `{{TOTAL_FRAMES_GUESS}}` | Estimated frame count (FPS × duration) |

### Example Configuration

```json
{
"default": [
{ "type": "fix_stamp", "output": "out" },
{
"type": "fix_stamp",
"output": "out"
},
{
"type": "move",
"output": "out/[{{ROOM_ID}}]{{ROOM_NAME}}@{{RECORD_START}}-{{RECORD_END}} {{RECORD_DURATION_STR}}",
"date_pattern": "yyyy-MM-dd HH:mm:ss"
},
{ "type": "slice", "output": "out", "duration": "1m10s" },
{
"type": "slice",
"output": "out",
"duration": "1m10s"
},
{
"type": "shell",
"noreturn": true,
"remove_input": false,
"date_pattern": "yyyy-MM-dd_HH-mm-ss",
"cmd": [
"ffmpeg", "-hide_banner", "-loglevel", "error", "-stats",
"-i", "{{INPUT_ABS}}",
"-vf", "thumbnail={{TOTAL_FRAMES_GUESS}}/400,scale=200:-1,tile=20x20",
"-vframes", "1",
"ffmpeg",
"-hide_banner",
"-loglevel",
"error",
"-stats",
"-i",
"{{INPUT_ABS}}",
"-vf",
"thumbnail={{TOTAL_FRAMES_GUESS}}/400,scale=200:-1,tile=20x20",
"-vframes",
"1",
"{{INPUT_DIR}}/{{INPUT_NAME_NOEXT}}.thumb.png",
"-y"
]
Expand All @@ -234,9 +249,13 @@ Logs are written to `./logs` with daily rotation (`xhrec.yyyy-MM-dd.log`).

### Log Masking

Sensitive information is replaced in log output by default. Static patterns (JWT tokens, cookies, auth URL parameters, proxy addresses) are masked with `***`. Dynamic strings (model names, usernames) are registered at startup and replaced with a stable CRC32-based hash that persists within a session but changes on restart, allowing log correlation without revealing identities.
Sensitive information is replaced in log output by default. Static patterns (JWT tokens, cookies, auth URL parameters,
proxy addresses) are masked with `***`. Dynamic strings (model names, usernames) are registered at startup and replaced
with a stable CRC32-based hash that persists within a session but changes on restart, allowing log correlation without
revealing identities.

Masking can be toggled at runtime via the eye icon in the WebUI toolbar, or through the API:

```shell
curl -k https://localhost:8090/mask/toggle # on/off
curl -k https://localhost:8090/mask/status # current state
Expand Down
Loading
Loading