piper-tts 1.6.0 moved synthesis from POST / to POST /synthesize, with an identical request body; the VPS sidecars run 1.6.0 and returned 405 to every read-aloud request, while millenia's older server still expects /. Rather than pinning both deployments to one Piper release, the path is configuration -- default "/" keeps millenia working untouched, and the compose stack sets /synthesize. The container healthcheck moves with it, since it was probing the old route too.
91 lines
3.2 KiB
Go
91 lines
3.2 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Config holds all runtime configuration, loaded from environment variables.
|
|
// See .env.example for the full list and defaults.
|
|
type Config struct {
|
|
Port string
|
|
BaseURL string
|
|
DatabasePath string
|
|
ImageDir string // on-disk store for editor image uploads
|
|
|
|
// LLM
|
|
LLMBackend string // "vllm" | "ollama"
|
|
LLMEndpoint string
|
|
LLMModel string // checkpoint model (small, fast)
|
|
LLMChatModel string // Ask Petal model; falls back to LLMModel if empty
|
|
LLMTimeout time.Duration
|
|
|
|
// TTS (read-aloud). Off unless TTSEndpoint is set — when empty, the /api/tts
|
|
// route isn't mounted and the frontend falls back to the browser's Web Speech
|
|
// API. Endpoint points at a local Piper HTTP server.
|
|
TTSEndpoint string // Piper instance serving the English voice
|
|
TTSEndpointZH string // Piper instance serving the Chinese voice; empty = zh falls back to Web Speech
|
|
TTSVoiceEN string // Piper voice id for English (e.g. en_US-amy-medium)
|
|
TTSVoiceZH string // Piper voice id for Chinese (e.g. zh_CN-huayan-medium)
|
|
// TTSPath is the path Piper serves synthesis on. Piper moved it from "/" to
|
|
// "/synthesize" in 1.6.0 with an unchanged request body, so this is a
|
|
// version knob, not a feature: millenia's older server keeps the default,
|
|
// the containerised sidecars set "/synthesize".
|
|
TTSPath string
|
|
TTSCacheDir string // on-disk store for synthesized clips (content-addressed)
|
|
TTSTimeout time.Duration
|
|
TTSFormat string // mp3 | opus | wav — mp3/opus transcode Piper's WAV via ffmpeg
|
|
|
|
// Auth (deferred — not wired in the local-dev build, kept for later)
|
|
AuthentikURL string
|
|
AuthentikClientID string
|
|
AuthentikClientSecret string
|
|
SessionSecret string
|
|
}
|
|
|
|
// Load reads configuration from the environment, applying sane local-dev defaults.
|
|
func Load() *Config {
|
|
return &Config{
|
|
Port: env("PORT", "8080"),
|
|
BaseURL: env("BASE_URL", "http://localhost:8080"),
|
|
DatabasePath: env("DATABASE_PATH", "./data/petal.db"),
|
|
ImageDir: env("IMAGE_DIR", "./data/images"),
|
|
|
|
LLMBackend: env("LLM_BACKEND", "vllm"),
|
|
LLMEndpoint: env("LLM_ENDPOINT", "http://localhost:8000"),
|
|
LLMModel: env("LLM_MODEL", ""),
|
|
LLMChatModel: env("LLM_CHAT_MODEL", ""),
|
|
LLMTimeout: envDuration("LLM_TIMEOUT", 30*time.Second),
|
|
|
|
TTSEndpoint: env("TTS_ENDPOINT", ""),
|
|
TTSEndpointZH: env("TTS_ENDPOINT_ZH", ""),
|
|
TTSVoiceEN: env("TTS_VOICE_EN", "en_US-amy-medium"),
|
|
TTSVoiceZH: env("TTS_VOICE_ZH", "zh_CN-huayan-medium"),
|
|
TTSPath: env("TTS_PATH", "/"),
|
|
TTSCacheDir: env("TTS_CACHE_DIR", "./data/tts"),
|
|
TTSTimeout: envDuration("TTS_TIMEOUT", 15*time.Second),
|
|
TTSFormat: env("TTS_AUDIO_FORMAT", "mp3"),
|
|
|
|
AuthentikURL: env("AUTHENTIK_URL", ""),
|
|
AuthentikClientID: env("AUTHENTIK_CLIENT_ID", ""),
|
|
AuthentikClientSecret: env("AUTHENTIK_CLIENT_SECRET", ""),
|
|
SessionSecret: env("SESSION_SECRET", "dev-insecure-secret-change-me"),
|
|
}
|
|
}
|
|
|
|
func env(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func envDuration(key string, fallback time.Duration) time.Duration {
|
|
if v := os.Getenv(key); v != "" {
|
|
if d, err := time.ParseDuration(v); err == nil {
|
|
return d
|
|
}
|
|
}
|
|
return fallback
|
|
}
|