mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
refactor(speaker): introduce pkg/speaker leaf for shared protocol constants
The HTTP port and on-device paths for the SoundTouch speaker were duplicated across pkg/client (unexported) and pkg/service/constants (under a service-layer prefix). Both spots needed the same values, and the next round of work (group/persistence handling in the CLI) would have created a third — or worse, dragged pkg/service into the CLI's dependency graph just for a port number. pkg/speaker is a no-deps leaf that holds the speaker-protocol constants: HTTPPort, the request paths, and the on-device persistence file locations (now including GroupServiceFileLocation, for the upcoming stereo-pair sync work). The client library, the service, the CLI, and tests can all import it without introducing a layering edge. This commit moves nothing into pkg/speaker that doesn't belong there — the service-specific constants (provider IDs, file names, date stub, etc.) stay in pkg/service/constants. Only the genuinely protocol-level values move. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
bb71253690
commit
c8c38b78e6
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gesellix/bose-soundtouch/pkg/models"
|
||||
"github.com/gesellix/bose-soundtouch/pkg/speaker"
|
||||
)
|
||||
|
||||
// Integration tests for bass control functionality
|
||||
@@ -426,7 +427,7 @@ func BenchmarkClient_Bass_Integration(b *testing.B) {
|
||||
// This is a simple version for test use
|
||||
func parseBassHostPort(hostPort string) (string, int) {
|
||||
if !containsSubstring(hostPort, ":") {
|
||||
return hostPort, defaultSoundTouchPort
|
||||
return hostPort, speaker.HTTPPort
|
||||
}
|
||||
|
||||
// Simple parsing - in real use, we'd use net.SplitHostPort
|
||||
@@ -448,7 +449,7 @@ func parseBassHostPort(hostPort string) (string, int) {
|
||||
|
||||
if len(parts) == 2 {
|
||||
// Try to parse port
|
||||
port := defaultSoundTouchPort
|
||||
port := speaker.HTTPPort
|
||||
portStr := parts[1]
|
||||
portInt := 0
|
||||
|
||||
@@ -468,5 +469,5 @@ func parseBassHostPort(hostPort string) (string, int) {
|
||||
return parts[0], port
|
||||
}
|
||||
|
||||
return hostPort, defaultSoundTouchPort
|
||||
return hostPort, speaker.HTTPPort
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gesellix/bose-soundtouch/pkg/speaker"
|
||||
)
|
||||
|
||||
// Integration tests for source selection functionality
|
||||
@@ -386,7 +388,7 @@ func BenchmarkClient_SelectSource_Integration(b *testing.B) {
|
||||
// This is a simple version for test use
|
||||
func parseHostPort(hostPort string) (string, int) {
|
||||
if !containsSubstring(hostPort, ":") {
|
||||
return hostPort, defaultSoundTouchPort
|
||||
return hostPort, speaker.HTTPPort
|
||||
}
|
||||
|
||||
// Simple parsing - in real use, we'd use net.SplitHostPort
|
||||
@@ -408,7 +410,7 @@ func parseHostPort(hostPort string) (string, int) {
|
||||
|
||||
if len(parts) == 2 {
|
||||
// Try to parse port
|
||||
port := defaultSoundTouchPort
|
||||
port := speaker.HTTPPort
|
||||
portStr := parts[1]
|
||||
portInt := 0
|
||||
|
||||
@@ -428,5 +430,5 @@ func parseHostPort(hostPort string) (string, int) {
|
||||
return parts[0], port
|
||||
}
|
||||
|
||||
return hostPort, defaultSoundTouchPort
|
||||
return hostPort, speaker.HTTPPort
|
||||
}
|
||||
|
||||
@@ -299,11 +299,10 @@ const (
|
||||
RecentsFile = "Recents.xml"
|
||||
SourcesFile = "Sources.xml"
|
||||
|
||||
SpeakerHTTPPort = 8090
|
||||
SpeakerDeviceInfoPath = "/info"
|
||||
SpeakerRecentsPath = "/recents"
|
||||
SpeakerPresetsPath = "/presets"
|
||||
SpeakerSourcesFileLocation = "/mnt/nv/BoseApp-Persistence/1/Sources.xml"
|
||||
// Speaker-protocol constants (HTTP port, paths, on-device file
|
||||
// locations) moved to github.com/gesellix/bose-soundtouch/pkg/speaker
|
||||
// so the client library and CLI can share them without depending on
|
||||
// the service package.
|
||||
|
||||
// DateStr is the hardcoded date used in many Bose XML responses
|
||||
DateStr = "2012-09-19T12:43:00.000+00:00"
|
||||
|
||||
@@ -9,10 +9,6 @@ func TestConstants(t *testing.T) {
|
||||
t.Error("DateStr should not be empty")
|
||||
}
|
||||
|
||||
if SpeakerHTTPPort != 8090 {
|
||||
t.Errorf("Expected SpeakerHTTPPort 8090, got %d", SpeakerHTTPPort)
|
||||
}
|
||||
|
||||
if len(GetProviders()) == 0 {
|
||||
t.Error("Providers should not be empty")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Package speaker holds protocol-level constants for the Bose SoundTouch
|
||||
// speaker's local API surface: the well-known HTTP port, the request paths
|
||||
// exposed by every device, and the on-device file locations the migration
|
||||
// flow needs to know about.
|
||||
//
|
||||
// This package is intentionally a leaf with no internal dependencies, so
|
||||
// any layer can import it (client library, service, CLI, tests) without
|
||||
// introducing a cycle or a cross-topic edge. Anything speaker-shaped that
|
||||
// would otherwise be duplicated between packages belongs here.
|
||||
package speaker
|
||||
|
||||
// HTTPPort is the well-known port the SoundTouch device exposes its local
|
||||
// API on (e.g. /info, /presets, /group).
|
||||
const HTTPPort = 8090
|
||||
|
||||
// Well-known HTTP paths the SoundTouch device serves on HTTPPort.
|
||||
const (
|
||||
DeviceInfoPath = "/info"
|
||||
PresetsPath = "/presets"
|
||||
RecentsPath = "/recents"
|
||||
)
|
||||
|
||||
// On-device filesystem paths that the migration/sync flow needs to read or
|
||||
// write over SSH. These live in the device's persistence area and are not
|
||||
// part of the HTTP surface.
|
||||
const (
|
||||
SourcesFileLocation = "/mnt/nv/BoseApp-Persistence/1/Sources.xml"
|
||||
GroupServiceFileLocation = "/mnt/nv/BoseApp-Persistence/1/GroupService.xml"
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
package speaker
|
||||
|
||||
import "testing"
|
||||
|
||||
// Sanity-check the well-known values — a wrong number here would silently
|
||||
// break every transport and is cheap to guard against.
|
||||
func TestSpeakerConstants(t *testing.T) {
|
||||
if HTTPPort != 8090 {
|
||||
t.Errorf("HTTPPort = %d, want 8090", HTTPPort)
|
||||
}
|
||||
|
||||
cases := map[string]string{
|
||||
"DeviceInfoPath": DeviceInfoPath,
|
||||
"PresetsPath": PresetsPath,
|
||||
"RecentsPath": RecentsPath,
|
||||
"SourcesFileLocation": SourcesFileLocation,
|
||||
"GroupServiceFileLocation": GroupServiceFileLocation,
|
||||
}
|
||||
|
||||
for name, val := range cases {
|
||||
if val == "" {
|
||||
t.Errorf("%s is empty", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user