Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Configuration is saved automatically to `~/.config/somafm/config.yml`.
volume: 70 # Volume level (0-100)
buffer_seconds: 5 # Audio buffer size (0-60 seconds)
last_station: groovesalad # Last played station ID
autostart: false # Auto-play last station on launch (true/false)
favorites: # List of favorite station IDs
- groovesalad
- dronezone
Expand Down
15 changes: 5 additions & 10 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Config struct {
Volume int `yaml:"volume"`
BufferSeconds int `yaml:"buffer_seconds"`
LastStation string `yaml:"last_station"`
Autostart bool `yaml:"autostart"`
Favorites []string `yaml:"favorites"`
Theme Theme `yaml:"theme"`
}
Expand Down Expand Up @@ -95,8 +96,8 @@ func Load() (*Config, error) {
return DefaultConfig(), fmt.Errorf("failed to read config file: %w", err)
}

var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
cfg := DefaultConfig()
if err := yaml.Unmarshal(data, cfg); err != nil {
return DefaultConfig(), fmt.Errorf("failed to parse config file: %w", err)
}

Expand All @@ -108,15 +109,8 @@ func Load() (*Config, error) {
if cfg.BufferSeconds > MaxBufferSecs {
cfg.BufferSeconds = MaxBufferSecs
}
if cfg.BufferSeconds == 0 && data != nil {
cfg.BufferSeconds = DefaultBufferSecs
}

if cfg.Theme.Background == "" {
cfg.Theme = DefaultConfig().Theme
}

return &cfg, nil
return cfg, nil
}

// Save writes the configuration to disk atomically using temp file + rename.
Expand Down Expand Up @@ -170,6 +164,7 @@ func DefaultConfig() *Config {
Volume: DefaultVolume,
BufferSeconds: DefaultBufferSecs,
LastStation: "",
Autostart: false,
Favorites: []string{},
Theme: Theme{
Background: "#1a1b25",
Expand Down
36 changes: 34 additions & 2 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ func TestDefaultConfig(t *testing.T) {
if cfg.LastStation != "" {
t.Errorf("DefaultConfig().LastStation = %q, want empty string", cfg.LastStation)
}

if cfg.Autostart != false {
t.Errorf("DefaultConfig().Autostart = %v, want false", cfg.Autostart)
}
}

func TestConfigSaveAndLoad(t *testing.T) {
Expand Down Expand Up @@ -415,9 +419,9 @@ func TestBufferSecondsValidation(t *testing.T) {
expectedBuffer int
}{
{"valid buffer 5", 5, 5},
{"valid buffer 0", 0, DefaultBufferSecs},
{"valid buffer 0", 0, 0},
{"valid buffer 60", 60, 60},
{"negative buffer", -10, DefaultBufferSecs},
{"negative buffer", -10, MinBufferSecs},
{"buffer over 60", 100, MaxBufferSecs},
{"buffer way over max", 1000, MaxBufferSecs},
}
Expand Down Expand Up @@ -486,6 +490,34 @@ func TestFavoritesPersistence(t *testing.T) {
}
}

func TestAutostartPersistence(t *testing.T) {
tmpDir := t.TempDir()
originalHome := os.Getenv("HOME")
os.Setenv("HOME", tmpDir)
defer os.Setenv("HOME", originalHome)

testCfg := &Config{
Volume: 70,
LastStation: "groovesalad",
Autostart: true,
Theme: DefaultConfig().Theme,
}

err := testCfg.Save()
if err != nil {
t.Fatalf("Save() error = %v", err)
}

loadedCfg, err := Load()
if err != nil {
t.Fatalf("Load() error = %v", err)
}

if loadedCfg.Autostart != true {
t.Errorf("Load().Autostart = %v, want true", loadedCfg.Autostart)
}
}

func TestLoadInvalidYAML(t *testing.T) {
tmpDir := t.TempDir()
originalHome := os.Getenv("HOME")
Expand Down
14 changes: 0 additions & 14 deletions internal/ui/stations.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,20 +177,6 @@ func (ui *UI) selectAndShowStation(index int) {
log.Debug().Msgf("Showing station info (without playing): %s", ui.currentStation.Title)
}

func (ui *UI) selectAndShowStationByID(stationID string) bool {
index := ui.stationService.FindIndexByID(stationID)
if index < 0 {
log.Debug().Msgf("Last played station '%s' not found in station list", stationID)
return false
}

ui.selectAndShowStation(index)
if s := ui.stationService.GetStation(index); s != nil {
log.Debug().Msgf("Auto-selected last played station: %s", s.Title)
}
return true
}

func (ui *UI) toggleFavorite() {
row, _ := ui.stationList.GetSelection()
stationCount := ui.stationService.StationCount()
Expand Down
25 changes: 20 additions & 5 deletions internal/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,12 +313,27 @@ func (ui *UI) fetchStationsAndInitUI() error {

if ui.startRandom {
ui.randomStation()
} else if ui.config.LastStation != "" {
if !ui.selectAndShowStationByID(ui.config.LastStation) {
ui.selectAndShowStation(0)
}
} else {
return
}

if ui.config.LastStation == "" {
ui.selectAndShowStation(0)
return
}

index := ui.stationService.FindIndexByID(ui.config.LastStation)
if index < 0 {
log.Debug().Msgf("Last station '%s' not found, showing first station", ui.config.LastStation)
ui.selectAndShowStation(0)
return
}

if ui.config.Autostart {
log.Debug().Msgf("Autostart enabled, playing last station: %s", ui.config.LastStation)
ui.stationList.Select(index+1, 0)
ui.onStationSelected(index)
} else {
ui.selectAndShowStation(index)
}
})

Expand Down