18 Commits
Author SHA1 Message Date
Tobias GesellchenandClaude Opus 4.8 c7eda7ed7b feat(cli)!: deprecate speaker-based station search in favour of find
The `find` family runs the search inside the CLI, querying the radio
provider's public API directly (no speaker cloud, no soundtouch-service).
Make it the canonical path and deprecate the speaker-based search family.

- Add `find-tunein` and `find-radiobrowser` siblings; refactor the find
  actions onto a shared `runFind` helper (all support `--more`).
- Rename the unreleased `search-radiobrowser` to `find-radiobrowser`.
- Deprecate `search`, `search-tunein`, `search-pandora`, `search-spotify`:
  they keep working but print a stderr deprecation notice (new
  `PrintDeprecation` helper) pointing at the `find*` replacement. Pandora
  and Spotify have no built-in equivalent yet (they need the speaker +
  account), so their notices say so.
- Docs: lead with the `find` family as recommended; mark the speaker-based
  search commands deprecated; drop the misleading "service-side" wording
  in favour of "built-in / queries the provider directly".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 20:53:40 +02:00
Tobias GesellchenandClaude Sonnet 4.6 0e9445af47 fix(security): sec7 — log-injection sweep, sanitizeErr helper
~30 remaining go/log-injection alerts share a common pattern: other
positional args in a log call are wrapped in sanitizeLog() but the
trailing 'err' value (via "%v") is not. CodeQL traces taint through
error chains back to the log.Printf call site itself.

Add sanitizeErr(err error) string to every affected package's
logutil.go (strips newlines from err.Error(), returns "<nil>" when
nil). Three packages had no logutil.go yet; new files added for
cmd/soundtouch-cli, cmd/websocket-demo, and examples.

Call-site changes (replace "%v, err" with "%s, sanitizeErr(err)" and
wrap any other unsanitised args in sanitizeLog):

pkg/client:
  - websocket.go:42   DefaultLogger.Printf now pre-formats and sanitises
                       the entire message (all variadic args sanitised)
  - websocket.go:445  err → sanitizeErr(err)

pkg/service/handlers:
  - handlers_account_mgmt.go:44   err
  - handlers_bmx_tunein.go:324,336 err (stationID already sanitised)
  - handlers_marge.go:288,510      err (deviceID/account already done)
  - handlers_mgmt.go:409,436,720  err
  - handlers_setup.go:1345        session + err
  - server.go:500                  bind
  - server.go:504,863,944,1029,   err (deviceIP/accountID already done)
    1164,1174

pkg/service/marge:
  - marge.go:1469,1923  saveErr / err

pkg/service/setup:
  - setup.go:1417,2316,2462  fmt.Printf — deviceIP / hostsContent / ip

pkg/service/stockholm:
  - proxy.go:117  effectiveTarget.String() + err

pkg/service/zeroconf:
  - zeroconf.go:312  err

pkg/service/proxy:
  - recorder.go:403  err (task.path already sanitised)

pkg/service/datastore:
  - datastore.go:940  werr (device already sanitised)

pkg/discovery:
  - dns.go:72   strings.Join(derived)
  - dns.go:503  d.upstreamDNS (fmt.Sprint of []string)

cmd/soundtouch-cli:
  - cmd_events.go:571  VerboseLogger.Printf — pre-format + sanitise
  - common.go:335      PrintError message

cmd/websocket-demo:
  - main.go:576   VerboseLogger.Printf — pre-format + sanitise

examples:
  - recording-filename-demo.go:79  err

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:28:26 +02:00
Tobias GesellchenandClaude Sonnet 4.6 fbc96c0c01 fix(cli): make --service-url required, remove default
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 22:45:15 +02:00
Tobias GesellchenandClaude Sonnet 4.6 9172072601 feat: add source removal — health check, API endpoint, and CLI commands
Health check (checks_stale_internet_radio.go): detects stub INTERNET_RADIO
sources (empty credentials) left on devices initialised before the stub was
removed from the default source list. Quick-fix removes by ID; skips any
INTERNET_RADIO source that has real credentials.

Datastore: DeleteSourceByID and DeleteSourceByType (uniqueness-guarded).

API: DELETE /setup/sources/{account}/{device}/{sourceID}

CLI — two new commands:
  soundtouch-cli cloud source remove --service-url ... --account ... --device ... [--id 10002 | --type INTERNET_RADIO]
    Talks to AfterTouch (service side). --type resolves to canonical ID
    locally; fails for unknown types.
  soundtouch-cli source notify-updated --host <speaker-ip>
    Talks to the speaker directly. Fetches device ID from /info, then
    POSTs sourcesUpdated to :8090/notification so the speaker re-fetches
    its source list immediately.

CloudCommonFlags (--service-url / AFTERTOUCH_URL) mirrors CommonFlags
(--host) for AfterTouch-facing command groups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 22:45:15 +02:00
Tobias GesellchenandGitHub b04b0bcc32 Add account registration/login (#129) 2026-03-27 08:37:50 +01:00
Tobias Gesellchen a19d34b55e Align soundtouch-service CLI with soundtouch-cli 2026-02-14 12:55:14 +01:00
Tobias Gesellchen a008093775 Implement Spotify URL metadata extraction and HTML entity unescaping 2026-02-01 22:58:24 +01:00
Tobias Gesellchen 2768838bad style: apply golangci-lint --fix for all remaining issues
Applied automatic fixes using golangci-lint --fix which resolved:
- All remaining wsl_v5 whitespace issues (28 issues)
- All whitespace formatting issues (1 issue)
- Improved code formatting consistency across the entire codebase

All tests passing and functionality preserved.
2026-02-01 22:11:12 +01:00
Tobias Gesellchen 7f6eccee39 style: fix golangci-lint issues for code quality improvement
- Fix error string capitalization to follow Go guidelines (ST1005)
- Fix unchecked error returns in test files (errcheck)
- Replace nil-nil return with proper error for non-TuneIn URLs (nilnil)
- Add missing comments for exported service type constants (revive)
- Rename unused parameters to underscore in test handlers (revive)
- Add t.Helper() calls to test helper functions (thelper)
- Update test expectations to match lowercase error messages

This addresses all critical linting issues while maintaining functionality.
2026-02-01 21:50:14 +01:00
Tobias Gesellchen de19cf1b0c Implement TuneIn URL conversion and metadata extraction for preset store 2026-02-01 21:26:59 +01:00
Tobias Gesellchen 01fbbcbcac refactor: Replace getBuildInfo() with updateBuildInfo() for consistency
- Use package-level variables instead of mixed return/ignore pattern
- Call updateBuildInfo() once at startup instead of multiple function calls
- Cleaner, more consistent design with single responsibility
- Eliminates confusing 'version, _, _' usage pattern

Thanks for the excellent code review feedback!
2026-01-11 17:33:01 +01:00
Tobias Gesellchen 1ed562f45e refactor: Replace ldflags version injection with debug.BuildInfo
- Use debug.ReadBuildInfo() for version information (Go 1.18+ best practice)
- Extract version from module info and VCS settings (vcs.revision, vcs.time)
- Remove complex ldflags setup from Makefile and GitHub workflows
- Simplify build process while maintaining all version information
- Cleaner approach recommended by Go community

Thanks to Gopher Slack feedback for this improvement!
2026-01-11 17:27:07 +01:00
Tobias Gesellchen 546634572a feat: implement build-time version injection
- Replace hardcoded version with build-time injected variables
- Add version, commit, and date variables to main.go with default values
- Update Makefile ldflags to use consistent variable names
- Add detailed 'version' subcommand showing build info, Go version, and platform
- Maintain compatibility with existing release workflow
- Support both --version flag (simple) and version subcommand (detailed)
2026-01-10 11:58:11 +01:00
Tobias Gesellchen ccd19d97a5 docs: add attribution to official Bose SoundTouch Web API documentation
- Reference original API documentation source from Bose Corporation
- Link to official Bose SoundTouch End-of-Life page
- Clarify this is an independent implementation
- Add disclaimer about non-affiliation with Bose Corporation
- Provide both online and local documentation references
2026-01-10 00:34:54 +01:00
Tobias Gesellchen a1917a1565 Fix parseHostPort function to properly handle IPv6 addresses and port validation
- Use net.SplitHostPort instead of simple string splitting to properly handle IPv6 addresses like [::1]:8090
- Add port range validation (1-65535)
- Return extracted host instead of full hostPort string on parse errors
- Add net import for SplitHostPort function
- Fixes all failing tests in cmd/soundtouch-cli package
2026-01-10 00:06:21 +01:00
Tobias Gesellchen 62a67818d2 Fix golangci-lint issues: resolve range copy, naming, and whitespace violations
- Fix range copy issues in cmd_network.go (use indexing instead of copying 168-byte structs)
- Rename DiscoveryService to Service to avoid package name stuttering
- Update all references to use new Service constructor names
- Apply automatic whitespace fixes using golangci-lint --fix
- Reduce linting issues from 32 to 7 (only cyclomatic complexity remains)

Remaining issues are architectural complexity violations that require manual refactoring.
2026-01-10 00:02:59 +01:00
Tobias Gesellchen e203909697 feat: complete CLI refactoring with urfave/cli
- Refactored soundtouch-cli from monolithic main.go to modular command structure
- Added urfave/cli/v2 dependency for better CLI organization
- Created separate command files for all SoundTouch features:
  * cmd_discover.go - Device discovery commands
  * cmd_info.go - Device information commands
  * cmd_volume.go - Volume control commands
  * cmd_playback.go - Playback control commands
  * cmd_source.go - Source selection commands
  * cmd_bass.go - Bass control commands (NEW)
  * cmd_balance.go - Balance control commands (NEW)
  * cmd_clock.go - Clock/time management commands (NEW)
  * cmd_network.go - Network information commands (NEW)
  * cmd_zone.go - Multi-room zone management commands (NEW)
  * common.go - Shared utilities and client setup

- Fixed flag conflicts by using --verbose instead of -v and removing -h alias from --host
- Implemented comprehensive CLI with organized subcommands and consistent UX
- Added proper help documentation and parameter validation
- Reduced golangci-lint issues by 70% (108+ → 32)
- Added package comments to new command files

Breaking changes:
- CLI now uses subcommands instead of flat flags
- Old: soundtouch-cli -host 192.168.1.100 -volume
- New: soundtouch-cli volume get --host 192.168.1.100

Examples:
- soundtouch-cli discover devices --all
- soundtouch-cli volume set --host 192.168.1.100 --level 50
- soundtouch-cli bass get --host 192.168.1.100
- soundtouch-cli clock set --host 192.168.1.100 --time '14:30'
- soundtouch-cli zone create --host 192.168.1.100 --members 192.168.1.101,192.168.1.102
2026-01-09 23:45:48 +01:00
Tobias Gesellchen 97677bc8d1 feat: refactor soundtouch-cli to use urfave/cli framework
- Added urfave/cli/v2 dependency for better CLI structure
- Created modular command structure with separate files:
  - common.go: Shared utilities and client setup
  - cmd_discover.go: Device discovery commands
  - cmd_info.go: Device information commands
  - cmd_volume.go: Volume control commands
  - cmd_playback.go: Playback control commands
  - cmd_source.go: Source selection commands
- Replaced giant main() function (complexity 149) with organized subcommands
- Added proper flag handling and validation
- Improved help text and user experience

WIP: Some issues remain (flag conflicts, missing commands)
Next: Complete remaining commands and fix conflicts
2026-01-09 23:35:23 +01:00