package config import ( "encoding/json" "os" "path/filepath" "time" ) // Config holds application configuration type Config struct { SeekStep time.Duration `json:"seek_step"` BigSeekStep time.Duration `json:"big_seek_step"` Volume float64 `json:"volume"` Theme string `json:"theme"` Editor string `json:"editor"` } // configFile is the JSON serialization format type configFile struct { SeekStepMs int64 `json:"seek_step_ms"` BigSeekStepMs int64 `json:"big_seek_step_ms"` Volume float64 `json:"volume"` Theme string `json:"theme"` Editor string `json:"editor"` } // configPath returns the path to the config file func configPath() string { configDir, err := os.UserConfigDir() if err != nil { configDir = os.Getenv("HOME") } return filepath.Join(configDir, "playback", "config.json") } // Load loads configuration from disk, returning defaults if not found func Load() Config { cfg := DefaultConfig() data, err := os.ReadFile(configPath()) if err != nil { return cfg } var cf configFile if err := json.Unmarshal(data, &cf); err != nil { return cfg } if cf.SeekStepMs > 0 { cfg.SeekStep = time.Duration(cf.SeekStepMs) * time.Millisecond } if cf.BigSeekStepMs > 0 { cfg.BigSeekStep = time.Duration(cf.BigSeekStepMs) * time.Millisecond } if cf.Volume > 0 { cfg.Volume = cf.Volume } if cf.Theme != "" { cfg.Theme = cf.Theme } if cf.Editor != "" { cfg.Editor = cf.Editor } return cfg } // Save writes configuration to disk func (c Config) Save() error { cf := configFile{ SeekStepMs: c.SeekStep.Milliseconds(), BigSeekStepMs: c.BigSeekStep.Milliseconds(), Volume: c.Volume, Theme: c.Theme, Editor: c.Editor, } data, err := json.MarshalIndent(cf, "", " ") if err != nil { return err } // Ensure directory exists dir := filepath.Dir(configPath()) if err := os.MkdirAll(dir, 0755); err != nil { return err } return os.WriteFile(configPath(), data, 0644) }