mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 16:46:17 +00:00
The ST10's /presets response after a factory reset emits self-closing <preset/> entries with no ContentItem child. cmd/soundtouch-cli's getPresets() handled the missing ContentItem in GetDisplayName() but then dereferenced preset.ContentItem.Source on the next line, panicking with "invalid memory address or nil pointer dereference" the moment the loop reached the first empty entry. A second placeholder shape was observed on healthy devices that were never reset: <preset id="0"><ContentItem source="INVALID_SOURCE" isPresetable="true"/></preset>. ContentItem is non-nil here, so the previous "ContentItem != nil" guard at other call sites still let these placeholders through into listings and into the AfterTouch datastore. Fix shape: pkg/models/presets.go - extend Preset.IsEmpty() to recognise both shapes (ContentItem == nil, OR Source == "" / "INVALID_SOURCE"). HasPresets, GetEmptyPresetSlots and GetUsedPresetSlots become honest about which slots actually carry playable content. cmd/soundtouch-cli/cmd_info.go (the crash site) - filter the slice via IsEmpty before the print loop, and switch the still-printed fields to the existing nil-safe Get* helpers. pkg/service/setup/setup.go - upgrade syncPresets's "ContentItem == nil" continue-guard to IsEmpty so Shape B placeholders don't get persisted in the AfterTouch datastore and then surface as junk rows in the admin web UI. cmd/soundtouch-cli/cmd_events.go, cmd/websocket-demo/main.go - same nil-guard upgrade. These already nil-checked so were crash-safe; the change is for consistency and to stop printing "Preset 0: (INVALID_SOURCE)" demo lines. examples/preset-management/main.go - had the same latent crash as cmd_info.go; same fix shape. Regression tests in pkg/models/presets_test.go cover both shapes using the exact XML observed in the wild: the reporter's three <preset/> placeholders plus the three INVALID_SOURCE entries from a live device. The reporter XML test walks every preset through the same accessor path the CLI used and asserts no panic. The soundtouch-web Go code does not deref preset.ContentItem.X anywhere - presets flow through as JSON - so no separate crash trap exists there. The web frontend will pick up the cleaner data once syncPresets stops persisting placeholders. Closes #308 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>