mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 00:26:29 +00:00
- Used 'golangci-lint run --fix' to automatically resolve formatting issues - Fixed all 52 remaining wsl_v5 (whitespace) issues automatically - Applied go fmt to ensure consistent formatting across codebase - Touched 49 files with automatic formatting improvements MAJOR PROGRESS: Reduced total issues from 79 to 27 (66% reduction!) Remaining issues: - gocyclo: 14 (complexity - requires manual refactoring) - revive: 5 (style/naming) - staticcheck: 5 (static analysis) - unparam: 3 (unused parameters)
252 lines
6.3 KiB
Go
252 lines
6.3 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"strings"
|
|
)
|
|
|
|
// Sources represents the response from /sources endpoint
|
|
type Sources struct {
|
|
XMLName xml.Name `xml:"sources"`
|
|
DeviceID string `xml:"deviceID,attr"`
|
|
SourceItem []SourceItem `xml:"sourceItem"`
|
|
}
|
|
|
|
// SourceItem represents an individual audio source
|
|
type SourceItem struct {
|
|
Source string `xml:"source,attr"`
|
|
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
|
|
Status SourceStatus `xml:"status,attr"`
|
|
IsLocal bool `xml:"isLocal,attr"`
|
|
MultiroomAllowed bool `xml:"multiroomallowed,attr"`
|
|
DisplayName string `xml:",chardata"`
|
|
}
|
|
|
|
// SourceStatus represents the availability status of a source
|
|
type SourceStatus string
|
|
|
|
const (
|
|
// SourceStatusReady indicates the source is ready for use
|
|
SourceStatusReady SourceStatus = "READY"
|
|
// SourceStatusUnavailable indicates the source is not available
|
|
SourceStatusUnavailable SourceStatus = "UNAVAILABLE"
|
|
// SourceStatusError indicates an error with the source
|
|
SourceStatusError SourceStatus = "ERROR"
|
|
)
|
|
|
|
// IsReady returns true if the source is ready for use
|
|
func (ss SourceStatus) IsReady() bool {
|
|
return ss == SourceStatusReady
|
|
}
|
|
|
|
// IsUnavailable returns true if the source is unavailable
|
|
func (ss SourceStatus) IsUnavailable() bool {
|
|
return ss == SourceStatusUnavailable
|
|
}
|
|
|
|
// String returns a human-readable string representation
|
|
func (ss SourceStatus) String() string {
|
|
switch ss {
|
|
case SourceStatusReady:
|
|
return "Ready"
|
|
case SourceStatusUnavailable:
|
|
return "Unavailable"
|
|
case SourceStatusError:
|
|
return "Error"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// UnmarshalXML implements custom XML unmarshaling with validation
|
|
func (ss *SourceStatus) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
var s string
|
|
if err := d.DecodeElement(&s, &start); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch s {
|
|
case string(SourceStatusReady), string(SourceStatusUnavailable), string(SourceStatusError):
|
|
*ss = SourceStatus(s)
|
|
default:
|
|
*ss = SourceStatusUnavailable // Default fallback for unknown states
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetDisplayName returns the best available display name for the source
|
|
func (si *SourceItem) GetDisplayName() string {
|
|
if si.DisplayName != "" {
|
|
return si.DisplayName
|
|
}
|
|
|
|
if si.SourceAccount != "" && si.SourceAccount != si.Source {
|
|
return si.SourceAccount
|
|
}
|
|
// Manually implement title case to replace deprecated strings.Title
|
|
source := strings.ToLower(si.Source)
|
|
if len(source) == 0 {
|
|
return source
|
|
}
|
|
|
|
return strings.ToUpper(source[:1]) + source[1:]
|
|
}
|
|
|
|
// IsSpotify returns true if this is a Spotify source
|
|
func (si *SourceItem) IsSpotify() bool {
|
|
return si.Source == "SPOTIFY"
|
|
}
|
|
|
|
// IsBluetoothSource returns true if this is a Bluetooth source
|
|
func (si *SourceItem) IsBluetoothSource() bool {
|
|
return si.Source == "BLUETOOTH"
|
|
}
|
|
|
|
// IsAuxSource returns true if this is an AUX input source
|
|
func (si *SourceItem) IsAuxSource() bool {
|
|
return si.Source == "AUX"
|
|
}
|
|
|
|
// IsStreamingService returns true if this is an online streaming service
|
|
func (si *SourceItem) IsStreamingService() bool {
|
|
streamingSources := []string{"SPOTIFY", "PANDORA", "TUNEIN", "IHEARTRADIO", "AMAZON", "LOCAL_INTERNET_RADIO"}
|
|
for _, source := range streamingSources {
|
|
if si.Source == source {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsLocalSource returns true if this is a local input source
|
|
func (si *SourceItem) IsLocalSource() bool {
|
|
return si.IsLocal
|
|
}
|
|
|
|
// SupportsMultiroom returns true if this source supports multiroom playback
|
|
func (si *SourceItem) SupportsMultiroom() bool {
|
|
return si.MultiroomAllowed
|
|
}
|
|
|
|
// GetAvailableSources returns only sources that are ready for use
|
|
func (s *Sources) GetAvailableSources() []SourceItem {
|
|
var available []SourceItem
|
|
|
|
for _, source := range s.SourceItem {
|
|
if source.Status.IsReady() {
|
|
available = append(available, source)
|
|
}
|
|
}
|
|
|
|
return available
|
|
}
|
|
|
|
// GetSourcesByType returns sources filtered by source type
|
|
func (s *Sources) GetSourcesByType(sourceType string) []SourceItem {
|
|
var filtered []SourceItem
|
|
|
|
for _, source := range s.SourceItem {
|
|
if source.Source == sourceType {
|
|
filtered = append(filtered, source)
|
|
}
|
|
}
|
|
|
|
return filtered
|
|
}
|
|
|
|
// GetSpotifySources returns all Spotify sources (there can be multiple accounts)
|
|
func (s *Sources) GetSpotifySources() []SourceItem {
|
|
return s.GetSourcesByType("SPOTIFY")
|
|
}
|
|
|
|
// GetReadySpotifySources returns only ready Spotify sources
|
|
func (s *Sources) GetReadySpotifySources() []SourceItem {
|
|
var ready []SourceItem
|
|
|
|
for _, source := range s.GetSpotifySources() {
|
|
if source.Status.IsReady() {
|
|
ready = append(ready, source)
|
|
}
|
|
}
|
|
|
|
return ready
|
|
}
|
|
|
|
// GetStreamingSources returns all streaming service sources
|
|
func (s *Sources) GetStreamingSources() []SourceItem {
|
|
var streaming []SourceItem
|
|
|
|
for _, source := range s.SourceItem {
|
|
if source.IsStreamingService() {
|
|
streaming = append(streaming, source)
|
|
}
|
|
}
|
|
|
|
return streaming
|
|
}
|
|
|
|
// GetLocalSources returns all local input sources
|
|
func (s *Sources) GetLocalSources() []SourceItem {
|
|
var local []SourceItem
|
|
|
|
for _, source := range s.SourceItem {
|
|
if source.IsLocalSource() {
|
|
local = append(local, source)
|
|
}
|
|
}
|
|
|
|
return local
|
|
}
|
|
|
|
// GetMultiroomSources returns sources that support multiroom playback
|
|
func (s *Sources) GetMultiroomSources() []SourceItem {
|
|
var multiroom []SourceItem
|
|
|
|
for _, source := range s.SourceItem {
|
|
if source.SupportsMultiroom() {
|
|
multiroom = append(multiroom, source)
|
|
}
|
|
}
|
|
|
|
return multiroom
|
|
}
|
|
|
|
// HasSource returns true if the specified source type is available
|
|
func (s *Sources) HasSource(sourceType string) bool {
|
|
sources := s.GetSourcesByType(sourceType)
|
|
for _, source := range sources {
|
|
if source.Status.IsReady() {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// HasSpotify returns true if any Spotify source is ready
|
|
func (s *Sources) HasSpotify() bool {
|
|
return s.HasSource("SPOTIFY")
|
|
}
|
|
|
|
// HasBluetooth returns true if Bluetooth source is ready
|
|
func (s *Sources) HasBluetooth() bool {
|
|
return s.HasSource("BLUETOOTH")
|
|
}
|
|
|
|
// HasAux returns true if AUX input is ready
|
|
func (s *Sources) HasAux() bool {
|
|
return s.HasSource("AUX")
|
|
}
|
|
|
|
// GetSourceCount returns the total number of sources
|
|
func (s *Sources) GetSourceCount() int {
|
|
return len(s.SourceItem)
|
|
}
|
|
|
|
// GetReadySourceCount returns the number of ready sources
|
|
func (s *Sources) GetReadySourceCount() int {
|
|
return len(s.GetAvailableSources())
|
|
}
|