package config
|
|
import (
|
"encoding/json"
|
"os"
|
"voicesnap/internal/logger"
|
"voicesnap/internal/model"
|
"voicesnap/internal/paths"
|
)
|
|
const (
|
HotkeyModeTap = "tap"
|
HotkeyModeHold = "hold"
|
LanguageModeAuto = "auto"
|
LanguageModeManual = "manual"
|
)
|
|
// Config holds the application configuration, compatible with the WPF version's config.json.
|
type Config struct {
|
HotkeyVK int `json:"HotkeyVK"`
|
HotkeyMode string `json:"HotkeyMode"`
|
AutoHide bool `json:"AutoHide"`
|
SoundFeedback bool `json:"SoundFeedback"`
|
HideDockIcon bool `json:"HideDockIcon"`
|
CopyToClipboard bool `json:"CopyToClipboard"`
|
DeviceName string `json:"DeviceName,omitempty"`
|
IndicatorX int `json:"IndicatorX,omitempty"`
|
IndicatorY int `json:"IndicatorY,omitempty"`
|
SelectedModelID string `json:"SelectedModelID,omitempty"`
|
LanguageMode string `json:"LanguageMode,omitempty"`
|
LanguageID string `json:"LanguageID,omitempty"`
|
ModelDownloadUrl string `json:"ModelDownloadUrl"`
|
FallbackModelDownloadUrl string `json:"FallbackModelDownloadUrl"`
|
}
|
|
// Default returns a default configuration.
|
func Default() *Config {
|
return &Config{
|
HotkeyVK: 0xA5, // Right Alt
|
HotkeyMode: HotkeyModeHold,
|
AutoHide: true,
|
SoundFeedback: true,
|
HideDockIcon: true,
|
CopyToClipboard: true,
|
SelectedModelID: model.DefaultModelID,
|
LanguageMode: LanguageModeAuto,
|
LanguageID: model.DefaultLanguageID,
|
ModelDownloadUrl: "http://www.maikami.com/voicesnap/sensevoice.zip",
|
FallbackModelDownloadUrl: "https://modelscope.cn/models/sherpa-onnx/sherpa-onnx-sense-voice-zh-en-ja-ko-yue/resolve/master/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2",
|
}
|
}
|
|
func configPath() string {
|
return paths.File("config.json")
|
}
|
|
// Load reads the config from disk. Returns default config on error.
|
func Load() (*Config, error) {
|
path := configPath()
|
data, err := os.ReadFile(path)
|
if err != nil {
|
if os.IsNotExist(err) {
|
cfg := Default()
|
Save(cfg)
|
return cfg, nil
|
}
|
return nil, err
|
}
|
|
cfg := Default()
|
if err := json.Unmarshal(data, cfg); err != nil {
|
return nil, err
|
}
|
cfg.HotkeyMode = NormalizeHotkeyMode(cfg.HotkeyMode)
|
cfg.LanguageMode = NormalizeLanguageMode(cfg.LanguageMode)
|
languageID := cfg.LanguageID
|
cfg.LanguageID = model.NormalizeLanguageID(cfg.LanguageID)
|
if languageID != "" && languageID != cfg.LanguageID {
|
logger.Info("Unknown LanguageID %q, falling back to %q", languageID, cfg.LanguageID)
|
}
|
selectedModelID := cfg.SelectedModelID
|
cfg.SelectedModelID = model.NormalizeModelID(cfg.SelectedModelID)
|
if selectedModelID != "" && selectedModelID != cfg.SelectedModelID {
|
logger.Info("Unknown SelectedModelID %q, falling back to %q", selectedModelID, cfg.SelectedModelID)
|
}
|
return cfg, nil
|
}
|
|
func NormalizeLanguageMode(mode string) string {
|
switch mode {
|
case LanguageModeAuto, LanguageModeManual:
|
return mode
|
default:
|
return LanguageModeAuto
|
}
|
}
|
|
func NormalizeHotkeyMode(mode string) string {
|
switch mode {
|
case HotkeyModeTap, HotkeyModeHold:
|
return mode
|
default:
|
return HotkeyModeHold
|
}
|
}
|
|
// Save writes the config to disk.
|
func Save(cfg *Config) {
|
if err := paths.Ensure(); err != nil {
|
logger.Error("Failed to create app data dir: %v", err)
|
return
|
}
|
|
data, err := json.MarshalIndent(cfg, "", " ")
|
if err != nil {
|
logger.Error("Failed to marshal config: %v", err)
|
return
|
}
|
if err := os.WriteFile(configPath(), data, 0644); err != nil {
|
logger.Error("Failed to save config: %v", err)
|
}
|
}
|