gostations/todo/queued/per-station-volume.md
Greg Gauthier 6ed2225a4f
All checks were successful
CI / Test (push) Successful in 55s
CI / Build (push) Successful in 40s
chore(todo): add todo/ directory modeled on grokkit; seed with per-station-volume as first queued item
2026-06-06 11:46:24 +01:00

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

  1. New data store (internal/data/volumes.go):

    • type Volumes struct { ... } (map[url]volume, mutex, dirty flag, path to volumes.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).
  2. 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 desired as --volume=... extra arg (or call a new player.SetVolume(desired) once IPC is ready).
    • On every volume change (in volumeMsg handler or on keypress):
      • volumes.Set(currentStation.Url, newVol)
      • volumes.Save() (or mark dirty and save on stop/quit for batching).
    • On s/x stop or app quit: ensure pending volume for the current station is saved.
  3. Player interface extension (optional but clean):

    • Add SetVolume(v int) error to Player interface.
    • mpvPlayer implements it with set_property volume X + local cache update.
    • legacyPlayer is a no-op (or could try command-line args on next Play).
  4. 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.
  5. 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=XX extra arg passed to Play() is the simplest reliable way (command-line wins). IPC set_property after 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.go or new volumes_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.