feat(config): set default values for WebRTC and transcoding in minimal TOML config

This commit is contained in:
Deivid Soto 2026-05-08 17:21:53 +02:00
parent 209ea38ecf
commit 26814ff6f7
3 changed files with 176 additions and 50 deletions

View file

@ -382,6 +382,62 @@ enabled = true
country = "US"
```
### Streaming reference
The in-browser player on torrentclaw.com streams from the daemon over WebRTC
(low-latency P2P) or HLS (HTTP fragments + ffmpeg transcode for codecs the
browser can't decode natively). Both are enabled by default — a fresh install
"just works" without editing the TOML. Disable surgically only if you have a
reason.
```toml
[downloads.webrtc]
enabled = true # master switch
trackers = ["wss://tracker.torrentclaw.com"] # signaling trackers
stun_servers = [ # NAT traversal
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
]
turn_servers = [] # optional TURN relays
turn_user = ""
turn_pass = ""
[downloads.transcode]
enabled = true # master switch
hw_accel = "auto" # auto | none | nvenc | qsv | vaapi | videotoolbox
preset = "veryfast" # libx264 preset
video_bitrate = "" # e.g. "5M" caps -b:v; empty = engine fallback (5M)
audio_bitrate = "192k" # e.g. "128k", "192k", "256k"
max_height = 0 # 0 = no cap; e.g. 720 forces 720p max
max_concurrent = 2 # max simultaneous ffmpeg processes
```
#### `[downloads.webrtc]`
| Key | Type | Default | Notes |
|-----|------|---------|-------|
| `enabled` | bool | `true` | Browser↔daemon WebRTC peer for the in-browser P2P player. Disable to skip WebRTC tracker signalling (saves ~5MB RAM, blocks WebRTC streaming — HLS still works). |
| `trackers` | `[]string` | `["wss://tracker.torrentclaw.com"]` | Signaling trackers for peer discovery. |
| `stun_servers` | `[]string` | Google public STUN ×2 | ICE candidate gathering. |
| `turn_servers` | `[]string` | `[]` | Optional TURN relays for symmetric-NAT users. |
| `turn_user` / `turn_pass` | string | `""` | Credentials for authed TURN servers. Applied to all `turn_servers`. |
#### `[downloads.transcode]`
| Key | Type | Default | Notes |
|-----|------|---------|-------|
| `enabled` | bool | `true` | Real-time HLS transcoding when source codec is browser-incompatible (HEVC, AV1, AC3, DTS). Requires `ffmpeg` + `ffprobe` on PATH. |
| `hw_accel` | string | `"auto"` | Hardware accel: `"auto"`, `"none"`, `"nvenc"` (NVIDIA), `"qsv"` (Intel), `"vaapi"` (Linux), `"videotoolbox"` (macOS). |
| `preset` | string | `"veryfast"` | libx264 preset. Slower preset = smaller files but higher CPU. Options: `ultrafast`, `superfast`, `veryfast`, `faster`, `fast`, `medium`, `slow`, `slower`, `veryslow`. |
| `video_bitrate` | string | `""` | E.g. `"5M"` caps `-b:v`. Empty falls back to the engine default (`5M`). |
| `audio_bitrate` | string | `"192k"` | E.g. `"128k"`, `"256k"`. |
| `max_height` | int | `0` | `0` = no cap. E.g. `720` forces 720p max — useful on weak GPUs. |
| `max_concurrent` | int | `2` | Max simultaneous ffmpeg processes. Increase if hosting multiple users on a beefy box. |
If `transcode.enabled = true` but `ffmpeg` / `ffprobe` aren't on PATH, the
daemon logs a warning at startup and HLS sessions are rejected at runtime
with a clear error — install ffmpeg or set `enabled = false`.
### Environment variables
Environment variables override config file values:

View file

@ -106,7 +106,9 @@ type LibraryConfig struct {
AllowDelete bool `toml:"allow_delete"` // allow web UI to request file deletion from disk
}
// Default returns a Config with sensible defaults.
// Default returns a Config with sensible defaults. Used both for fresh
// installs (no config file yet) and as the baseline for Load — fields not
// present in the user's TOML keep their Default() value.
func Default() Config {
return Config{
Auth: AuthConfig{
@ -117,7 +119,7 @@ func Default() Config {
MaxConcurrent: 3,
StreamPort: 11818,
WebRTC: WebRTCConfig{
Enabled: false,
Enabled: true,
Trackers: []string{"wss://tracker.torrentclaw.com"},
STUNServers: []string{"stun:stun.l.google.com:19302", "stun:stun1.l.google.com:19302"},
},
@ -125,7 +127,6 @@ func Default() Config {
Enabled: true,
HWAccel: "auto",
Preset: "veryfast",
VideoBitrate: "5M",
AudioBitrate: "192k",
MaxConcurrent: 2,
},
@ -167,67 +168,66 @@ func Load(path string) (Config, error) {
return cfg, fmt.Errorf("read config: %w", err)
}
if err := toml.Unmarshal(data, &cfg); err != nil {
meta, err := toml.Decode(string(data), &cfg)
if err != nil {
return cfg, fmt.Errorf("parse config: %w", err)
}
// Re-apply defaults for zero values that should have defaults
if cfg.Auth.APIURL == "" {
applyDefaults(&cfg, meta)
return cfg, nil
}
// applyDefaults fills in sensible defaults for keys that the user did not
// define in the TOML file. We use MetaData (rather than zero-value checks) so
// that explicitly setting a field to its zero value (e.g. `enabled = false`)
// is respected — only truly missing keys get defaulted. This lets a fresh
// install work out of the box for streaming without forcing every user to
// edit the TOML, while still letting power users disable features.
func applyDefaults(cfg *Config, meta toml.MetaData) {
if !meta.IsDefined("auth", "api_url") {
cfg.Auth.APIURL = "https://torrentclaw.com"
}
if cfg.Download.PreferredMethod == "" {
if !meta.IsDefined("downloads", "preferred_method") {
cfg.Download.PreferredMethod = "auto"
}
if cfg.Download.MaxConcurrent == 0 {
if !meta.IsDefined("downloads", "max_concurrent") {
cfg.Download.MaxConcurrent = 3
}
if cfg.General.Country == "" {
cfg.General.Country = "US"
}
if cfg.Download.StreamPort == 0 {
if !meta.IsDefined("downloads", "stream_port") {
cfg.Download.StreamPort = 11818
}
// Re-apply WebRTC defaults only when the user enabled WebRTC but didn't
// supply trackers/STUN — leave both empty if disabled to keep config diffs clean.
if cfg.Download.WebRTC.Enabled {
if len(cfg.Download.WebRTC.Trackers) == 0 {
cfg.Download.WebRTC.Trackers = []string{"wss://tracker.torrentclaw.com"}
}
if len(cfg.Download.WebRTC.STUNServers) == 0 {
cfg.Download.WebRTC.STUNServers = []string{
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
}
}
// Auto-enable transcode for the in-browser player when WebRTC is on
// AND the user hasn't explicitly opted out. The struct's Enabled
// field is `false` for legacy configs because the field didn't
// exist when they were written; we treat "no transcode section at
// all" as "use defaults" rather than "off".
tc := &cfg.Download.Transcode
if !tc.Enabled && tc.HWAccel == "" && tc.Preset == "" && tc.VideoBitrate == "" {
tc.Enabled = true
}
if tc.Enabled {
if tc.HWAccel == "" {
tc.HWAccel = "auto"
}
if tc.Preset == "" {
tc.Preset = "veryfast"
}
if tc.VideoBitrate == "" {
tc.VideoBitrate = "5M"
}
if tc.AudioBitrate == "" {
tc.AudioBitrate = "192k"
}
if tc.MaxConcurrent == 0 {
tc.MaxConcurrent = 2
}
if !meta.IsDefined("general", "country") {
cfg.General.Country = "US"
}
if !meta.IsDefined("downloads", "webrtc", "enabled") {
cfg.Download.WebRTC.Enabled = true
}
if !meta.IsDefined("downloads", "webrtc", "trackers") {
cfg.Download.WebRTC.Trackers = []string{"wss://tracker.torrentclaw.com"}
}
if !meta.IsDefined("downloads", "webrtc", "stun_servers") {
cfg.Download.WebRTC.STUNServers = []string{
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
}
}
return cfg, nil
if !meta.IsDefined("downloads", "transcode", "enabled") {
cfg.Download.Transcode.Enabled = true
}
if !meta.IsDefined("downloads", "transcode", "hw_accel") {
cfg.Download.Transcode.HWAccel = "auto"
}
if !meta.IsDefined("downloads", "transcode", "preset") {
cfg.Download.Transcode.Preset = "veryfast"
}
if !meta.IsDefined("downloads", "transcode", "audio_bitrate") {
cfg.Download.Transcode.AudioBitrate = "192k"
}
if !meta.IsDefined("downloads", "transcode", "max_concurrent") {
cfg.Download.Transcode.MaxConcurrent = 2
}
}
// Save writes config to the default or specified path using atomic write.

View file

@ -190,6 +190,76 @@ func TestParseSpeed(t *testing.T) {
}
}
func TestLoadMinimalTOMLAppliesStreamingDefaults(t *testing.T) {
tmp := t.TempDir()
path := filepath.Join(tmp, "config.toml")
// Minimal config — only auth + agent. Nothing about webrtc / transcode.
os.WriteFile(path, []byte(`[auth]
api_key = "tc_minimal"
[agent]
id = "agent-uuid"
name = "Test"
`), 0o644)
cfg, err := Load(path)
if err != nil {
t.Fatalf("Load failed: %v", err)
}
// WebRTC should be on by default for fresh installs.
if !cfg.Download.WebRTC.Enabled {
t.Error("WebRTC.Enabled should default to true when [downloads.webrtc] is absent")
}
if len(cfg.Download.WebRTC.Trackers) == 0 {
t.Error("WebRTC.Trackers should default to torrentclaw tracker when absent")
}
if len(cfg.Download.WebRTC.STUNServers) == 0 {
t.Error("WebRTC.STUNServers should default to public STUN list when absent")
}
// Transcode should be on by default.
if !cfg.Download.Transcode.Enabled {
t.Error("Transcode.Enabled should default to true when [downloads.transcode] is absent")
}
if cfg.Download.Transcode.HWAccel != "auto" {
t.Errorf("Transcode.HWAccel = %q, want auto", cfg.Download.Transcode.HWAccel)
}
if cfg.Download.Transcode.Preset != "veryfast" {
t.Errorf("Transcode.Preset = %q, want veryfast", cfg.Download.Transcode.Preset)
}
if cfg.Download.Transcode.MaxConcurrent != 2 {
t.Errorf("Transcode.MaxConcurrent = %d, want 2", cfg.Download.Transcode.MaxConcurrent)
}
}
func TestLoadRespectsExplicitlyDisabledStreaming(t *testing.T) {
tmp := t.TempDir()
path := filepath.Join(tmp, "config.toml")
// User explicitly opted out of webrtc + transcode. Defaults must NOT
// override them — that would silently re-enable features the user disabled.
os.WriteFile(path, []byte(`[downloads.webrtc]
enabled = false
[downloads.transcode]
enabled = false
`), 0o644)
cfg, err := Load(path)
if err != nil {
t.Fatalf("Load failed: %v", err)
}
if cfg.Download.WebRTC.Enabled {
t.Error("WebRTC.Enabled = true, want false (user explicitly disabled)")
}
if cfg.Download.Transcode.Enabled {
t.Error("Transcode.Enabled = true, want false (user explicitly disabled)")
}
}
func TestLoadInvalidTOML(t *testing.T) {
tmp := t.TempDir()
path := filepath.Join(tmp, "config.toml")