4.2 KiB
Per-station volume savings
Description: Persist the last-used volume level on a per-station basis (keyed by stream URL), so that returning to a favorite or previously-played station restores the volume the user last set for that specific station, rather than always falling back to the global last-volume.
Problem It Solves
Currently only a single global player.last_volume is saved in radiostations.ini. When a user fine-tunes the volume while listening to Station A (e.g. to 35), stops, then plays Station B, the volume is reset to whatever was last saved globally (or the default 70). Users have to re-adjust the volume every time they switch stations.
Benefits
- Natural UX: Volume preference is station-specific (a quiet classical station vs. a loud rock station).
- Seamless resumption: Pick a favorite → volume is already where you left it.
- Low friction: No extra UI; the existing volume controls + persistence just become smarter.
- Backward compatible: Falls back to global last-volume when no per-station entry exists.
- Leverages existing patterns: Same atomic JSON storage + XDG path as
favorites.json.
High-Level Implementation
-
New data store (
internal/data/volumes.go):type Volumes struct { ... }(map[url]volume, mutex, dirty flag, path tovolumes.json).NewVolumes(),Load(),Save()(exact same atomic tmp+rename pattern as Favorites).Get(url string) (int, bool),Set(url string, vol int),Remove(url string).
-
Wire into TUI / playback start (in
internal/ui/ui.go):- On entering playback for a station:
if vol, ok := volumes.Get(station.Url); ok { desired = vol } else { desired = config.LastVolume() }- Pass
desiredas--volume=...extra arg (or call a newplayer.SetVolume(desired)once IPC is ready).
- On every volume change (in
volumeMsghandler or on keypress):volumes.Set(currentStation.Url, newVol)volumes.Save()(or mark dirty and save on stop/quit for batching).
- On
s/xstop or app quit: ensure pending volume for the current station is saved.
- On entering playback for a station:
-
Player interface extension (optional but clean):
- Add
SetVolume(v int) errortoPlayerinterface. mpvPlayerimplements it withset_property volume X+ local cache update.legacyPlayeris a no-op (or could try command-line args on next Play).
- Add
-
Config / init:
- Load volumes in
NewApp()or lazily when first needed (same as favorites). - On station removal from favorites (if desired): optionally prune the volume entry.
- Load volumes in
-
Persistence file:
~/.config/gostations/volumes.json(array of{url, volume}or map for simplicity).
Flags / Config
| Key | Description |
|---|---|
| (none yet) | Could add player.per_station_volume=true (default on) in future |
Implementation Notes
- Key choice: Use the station's
Url(same as Favorites). Stable enough for the use-case. - When to persist: Save on every change (cheap) or only on stop/quit. Current global volume already saves on change; we can do the same for per-station or batch on exit.
- mpv timing: The
--volume=XXextra arg passed toPlay()is the simplest reliable way (command-line wins). IPCset_propertyafter connect is a good fallback/override. - Legacy player: Per-station volumes will be ignored for now (documented limitation).
- First-time migration: Existing global last-volume becomes the fallback; no automatic per-station entries are created until user actually changes volume on a station.
- Tests: Add to
stations_test.goor newvolumes_test.go; mock the volumes store. - Effort: Medium (~200-300 LOC new + wiring). Mostly data layer + 3-4 call sites in the TUI.
ROI
High. Volume is one of the most frequently adjusted controls in a radio player. Making it "stick" per station removes a constant micro-friction and makes the TUI feel more thoughtful and polished.
| Stage | Covered |
|---|---|
| Global last-volume | ✓ (already shipped in 2.1) |
| Per-station | new |
| Favorites | ✓ (existing) |
| TUI playback | ✓ |
Users will notice it immediately the second time they return to a station they previously tuned.