mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
Selecting a Radio Browser station from the player UI returned HTTP 500 and the speaker dropped to INVALID_SOURCE. The play path sent the speaker a ContentItem with source="URL" and an absolute location (https://all.api.radio-browser.info/soundtouch/stations/byuuid/<uuid>). source="URL" makes the speaker fetch that location as a raw audio stream, but the URL returns station JSON, not audio, so the speaker rejects it. "URL" was never a real source: it is not in the speaker's sourceprovider registry and never persisted in any datastore. The rest of the stack is already built for the native RADIO_BROWSER source (BMX registry provider 39 with base URL .../soundtouch, a seeded RADIO_BROWSER source, marge classification, and the documented relative location form). Working RADIO_BROWSER items use source="RADIO_BROWSER" with a relative location="/stations/byuuid/<uuid>", which the speaker resolves against the registry base URL and plays directly. - stations.ResolveContentItem: emit source=RADIO_BROWSER for the provider - RadioBrowserSearch: emit the relative /stations/byuuid/<uuid> playback href so the speaker prepends the registry base URL - marge classifier: match the relative /stations/byuuid/ segment (covers both the relative and legacy absolute forms) - tests updated to assert the native source + relative location Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88 lines
2.9 KiB
Go
88 lines
2.9 KiB
Go
package marge
|
|
|
|
import (
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/models"
|
|
"github.com/gesellix/bose-soundtouch/pkg/service/constants"
|
|
)
|
|
|
|
// TestClassifyAsRadioBrowser verifies that classifyAsRadioBrowser sets
|
|
// the expected fields on a ConfiguredSource.
|
|
func TestClassifyAsRadioBrowser(t *testing.T) {
|
|
src := &models.ConfiguredSource{}
|
|
|
|
classifyAsRadioBrowser(src)
|
|
|
|
if src.SourceKey.Type != constants.ProviderRadioBrowser {
|
|
t.Errorf("SourceKey.Type = %q, want %q", src.SourceKey.Type, constants.ProviderRadioBrowser)
|
|
}
|
|
|
|
if src.SourceKeyType != constants.ProviderRadioBrowser {
|
|
t.Errorf("SourceKeyType = %q, want %q", src.SourceKeyType, constants.ProviderRadioBrowser)
|
|
}
|
|
|
|
if src.Type != "Audio" {
|
|
t.Errorf("Type = %q, want Audio", src.Type)
|
|
}
|
|
|
|
if src.SecretType != constants.CredentialTypeToken {
|
|
t.Errorf("SecretType = %q, want %q", src.SecretType, constants.CredentialTypeToken)
|
|
}
|
|
|
|
if src.Secret == "" {
|
|
t.Error("expected Secret to be generated, got empty string")
|
|
}
|
|
|
|
if src.DisplayName != constants.ProviderRadioBrowser {
|
|
t.Errorf("DisplayName = %q, want %q", src.DisplayName, constants.ProviderRadioBrowser)
|
|
}
|
|
}
|
|
|
|
// TestClassifyAsRadioBrowser_PreservesExistingSecret verifies that a
|
|
// pre-existing secret is NOT overwritten.
|
|
func TestClassifyAsRadioBrowser_PreservesExistingSecret(t *testing.T) {
|
|
src := &models.ConfiguredSource{Secret: "existing-secret"}
|
|
|
|
classifyAsRadioBrowser(src)
|
|
|
|
if src.Secret != "existing-secret" {
|
|
t.Errorf("expected existing secret to be preserved, got %q", src.Secret)
|
|
}
|
|
}
|
|
|
|
// TestClassifyLearnedSource_RadioBrowserByProviderID verifies that the
|
|
// classifyLearnedSource dispatcher routes to classifyAsRadioBrowser when
|
|
// sourceProviderID matches RadioBrowserProviderID (39).
|
|
func TestClassifyLearnedSource_RadioBrowserByProviderID(t *testing.T) {
|
|
src := &models.ConfiguredSource{}
|
|
|
|
classifyLearnedSource(src, "", "", strconv.Itoa(constants.RadioBrowserProviderID))
|
|
|
|
if src.SourceKey.Type != constants.ProviderRadioBrowser {
|
|
t.Errorf("expected RADIO_BROWSER from providerID 39, got %q", src.SourceKey.Type)
|
|
}
|
|
}
|
|
|
|
// TestClassifyLearnedSource_RadioBrowserByLocation verifies that the dispatcher
|
|
// routes to classifyAsRadioBrowser when the location contains the RadioBrowser
|
|
// byuuid path segment, for both the relative form the native RADIO_BROWSER play
|
|
// path now emits and the legacy absolute form (#479).
|
|
func TestClassifyLearnedSource_RadioBrowserByLocation(t *testing.T) {
|
|
locations := []string{
|
|
"/stations/byuuid/abc-123", // native relative location
|
|
"https://all.api.radio-browser.info/soundtouch/stations/byuuid/abc-123", // legacy absolute location
|
|
}
|
|
|
|
for _, location := range locations {
|
|
src := &models.ConfiguredSource{}
|
|
|
|
classifyLearnedSource(src, "", location, "")
|
|
|
|
if src.SourceKey.Type != constants.ProviderRadioBrowser {
|
|
t.Errorf("expected RADIO_BROWSER from byuuid location %q, got %q", location, src.SourceKey.Type)
|
|
}
|
|
}
|
|
}
|