mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
feat: add LiveKit infrastructure (Phase 0)
- Replace VoiceConfig STUN/TURN/MediaPort fields with LiveKit API key, secret, URL, and binary path - Add livekit_process.go: companion process manager with auto-restart and config generation - Add livekit.go: token generation, room service client, participant management, video track counting - Add LiveKit server SDK dependency (server-sdk-go/v2) - Update config tests for new VoiceConfig fields
This commit is contained in:
+22
-42
@@ -29,18 +29,13 @@ type GitHubConfig struct {
|
||||
Token string `koanf:"token"`
|
||||
}
|
||||
|
||||
// VoiceConfig holds STUN/TURN server settings and SFU configuration.
|
||||
// VoiceConfig holds LiveKit server connection and voice quality settings.
|
||||
type VoiceConfig struct {
|
||||
TURNSecret string `koanf:"turn_secret"` // HMAC-SHA1 secret; auto-generated if empty
|
||||
STUNPort int `koanf:"stun_port"` // default 3478
|
||||
TURNPort int `koanf:"turn_port"` // default 3478
|
||||
TURNEnabled bool `koanf:"turn_enabled"` // default true
|
||||
Quality string `koanf:"quality"` // low | medium | high
|
||||
MixingThreshold int `koanf:"mixing_threshold"` // selective forwarding threshold
|
||||
TopSpeakers int `koanf:"top_speakers"` // top-N speakers in selective mode
|
||||
ExternalIP string `koanf:"external_ip"` // set if behind NAT
|
||||
MediaPortMin int `koanf:"media_port_min"` // UDP port range start for WebRTC media
|
||||
MediaPortMax int `koanf:"media_port_max"` // UDP port range end for WebRTC media
|
||||
LiveKitAPIKey string `koanf:"livekit_api_key"` // LiveKit API key
|
||||
LiveKitAPISecret string `koanf:"livekit_api_secret"` // LiveKit API secret
|
||||
LiveKitURL string `koanf:"livekit_url"` // LiveKit server WebSocket URL (e.g. ws://localhost:7880)
|
||||
LiveKitBinaryPath string `koanf:"livekit_binary"` // path to livekit-server binary; empty = don't auto-start
|
||||
Quality string `koanf:"quality"` // low | medium | high
|
||||
}
|
||||
|
||||
// ServerConfig holds HTTP server settings.
|
||||
@@ -105,14 +100,10 @@ func defaults() Config {
|
||||
StorageDir: "data/uploads",
|
||||
},
|
||||
Voice: VoiceConfig{
|
||||
STUNPort: 3478,
|
||||
TURNPort: 3478,
|
||||
TURNEnabled: true,
|
||||
Quality: "medium",
|
||||
MixingThreshold: 10,
|
||||
TopSpeakers: 3,
|
||||
MediaPortMin: 10000,
|
||||
MediaPortMax: 10100,
|
||||
LiveKitAPIKey: "devkey",
|
||||
LiveKitAPISecret: "secret",
|
||||
LiveKitURL: "ws://localhost:7880",
|
||||
Quality: "medium",
|
||||
},
|
||||
GitHub: GitHubConfig{},
|
||||
}
|
||||
@@ -148,13 +139,11 @@ upload:
|
||||
storage_dir: "data/uploads"
|
||||
|
||||
voice:
|
||||
# external_ip: "" # set to your public IP if behind NAT (required for voice over internet)
|
||||
# stun_port: 3478 # UDP port for STUN
|
||||
# turn_port: 3478 # UDP port for TURN relay
|
||||
# turn_enabled: true
|
||||
# quality: "medium" # low | medium | high
|
||||
# media_port_min: 10000 # UDP port range for WebRTC media
|
||||
# media_port_max: 10100
|
||||
livekit_api_key: "devkey" # LiveKit API key
|
||||
livekit_api_secret: "secret" # LiveKit API secret
|
||||
livekit_url: "ws://localhost:7880" # LiveKit server WebSocket URL
|
||||
# livekit_binary: "" # path to livekit-server binary; empty = don't auto-start
|
||||
# quality: "medium" # low | medium | high
|
||||
|
||||
# github:
|
||||
# token: "" # optional: GitHub API token for higher rate limits (5000 req/hr vs 60)
|
||||
@@ -226,27 +215,18 @@ func Load(cfgPath string) (*Config, error) {
|
||||
// overwrites struct defaults with Go zero values.
|
||||
func applyVoiceDefaults(v *VoiceConfig) {
|
||||
def := defaults().Voice
|
||||
if v.STUNPort == 0 {
|
||||
v.STUNPort = def.STUNPort
|
||||
if v.LiveKitAPIKey == "" {
|
||||
v.LiveKitAPIKey = def.LiveKitAPIKey
|
||||
}
|
||||
if v.TURNPort == 0 {
|
||||
v.TURNPort = def.TURNPort
|
||||
if v.LiveKitAPISecret == "" {
|
||||
v.LiveKitAPISecret = def.LiveKitAPISecret
|
||||
}
|
||||
if v.LiveKitURL == "" {
|
||||
v.LiveKitURL = def.LiveKitURL
|
||||
}
|
||||
if v.Quality == "" {
|
||||
v.Quality = def.Quality
|
||||
}
|
||||
if v.MediaPortMin == 0 {
|
||||
v.MediaPortMin = def.MediaPortMin
|
||||
}
|
||||
if v.MediaPortMax == 0 {
|
||||
v.MediaPortMax = def.MediaPortMax
|
||||
}
|
||||
if v.MixingThreshold == 0 {
|
||||
v.MixingThreshold = def.MixingThreshold
|
||||
}
|
||||
if v.TopSpeakers == 0 {
|
||||
v.TopSpeakers = def.TopSpeakers
|
||||
}
|
||||
}
|
||||
|
||||
// validateYAML checks that raw bytes are valid YAML.
|
||||
|
||||
@@ -241,14 +241,9 @@ func TestLoadVoiceConfigDefaults(t *testing.T) {
|
||||
want any
|
||||
}{
|
||||
{"Voice.Quality", cfg.Voice.Quality, "medium"},
|
||||
{"Voice.MixingThreshold", cfg.Voice.MixingThreshold, 10},
|
||||
{"Voice.TopSpeakers", cfg.Voice.TopSpeakers, 3},
|
||||
{"Voice.ExternalIP", cfg.Voice.ExternalIP, ""},
|
||||
{"Voice.MediaPortMin", cfg.Voice.MediaPortMin, 10000},
|
||||
{"Voice.MediaPortMax", cfg.Voice.MediaPortMax, 10100},
|
||||
{"Voice.STUNPort", cfg.Voice.STUNPort, 3478},
|
||||
{"Voice.TURNPort", cfg.Voice.TURNPort, 3478},
|
||||
{"Voice.TURNEnabled", cfg.Voice.TURNEnabled, true},
|
||||
{"Voice.LiveKitAPIKey", cfg.Voice.LiveKitAPIKey, "devkey"},
|
||||
{"Voice.LiveKitAPISecret", cfg.Voice.LiveKitAPISecret, "secret"},
|
||||
{"Voice.LiveKitURL", cfg.Voice.LiveKitURL, "ws://localhost:7880"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -267,11 +262,9 @@ func TestLoadVoiceConfigFromYAML(t *testing.T) {
|
||||
yaml := `
|
||||
voice:
|
||||
quality: high
|
||||
mixing_threshold: 5
|
||||
top_speakers: 4
|
||||
external_ip: "1.2.3.4"
|
||||
media_port_min: 20000
|
||||
media_port_max: 20500
|
||||
livekit_api_key: "mykey"
|
||||
livekit_api_secret: "mysecret"
|
||||
livekit_url: "ws://lk.example.com:7880"
|
||||
`
|
||||
if err := os.WriteFile(cfgPath, []byte(yaml), 0o644); err != nil {
|
||||
t.Fatalf("failed to write yaml: %v", err)
|
||||
@@ -285,20 +278,14 @@ voice:
|
||||
if cfg.Voice.Quality != "high" {
|
||||
t.Errorf("Voice.Quality = %q, want 'high'", cfg.Voice.Quality)
|
||||
}
|
||||
if cfg.Voice.MixingThreshold != 5 {
|
||||
t.Errorf("Voice.MixingThreshold = %d, want 5", cfg.Voice.MixingThreshold)
|
||||
if cfg.Voice.LiveKitAPIKey != "mykey" {
|
||||
t.Errorf("Voice.LiveKitAPIKey = %q, want 'mykey'", cfg.Voice.LiveKitAPIKey)
|
||||
}
|
||||
if cfg.Voice.TopSpeakers != 4 {
|
||||
t.Errorf("Voice.TopSpeakers = %d, want 4", cfg.Voice.TopSpeakers)
|
||||
if cfg.Voice.LiveKitAPISecret != "mysecret" {
|
||||
t.Errorf("Voice.LiveKitAPISecret = %q, want 'mysecret'", cfg.Voice.LiveKitAPISecret)
|
||||
}
|
||||
if cfg.Voice.ExternalIP != "1.2.3.4" {
|
||||
t.Errorf("Voice.ExternalIP = %q, want '1.2.3.4'", cfg.Voice.ExternalIP)
|
||||
}
|
||||
if cfg.Voice.MediaPortMin != 20000 {
|
||||
t.Errorf("Voice.MediaPortMin = %d, want 20000", cfg.Voice.MediaPortMin)
|
||||
}
|
||||
if cfg.Voice.MediaPortMax != 20500 {
|
||||
t.Errorf("Voice.MediaPortMax = %d, want 20500", cfg.Voice.MediaPortMax)
|
||||
if cfg.Voice.LiveKitURL != "ws://lk.example.com:7880" {
|
||||
t.Errorf("Voice.LiveKitURL = %q, want 'ws://lk.example.com:7880'", cfg.Voice.LiveKitURL)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user