mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
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.
345 lines
11 KiB
Go
345 lines
11 KiB
Go
package models
|
|
|
|
import "encoding/xml"
|
|
|
|
// NavigateRequest represents a request to navigate content sources
|
|
type NavigateRequest struct {
|
|
XMLName xml.Name `xml:"navigate"`
|
|
Source string `xml:"source,attr"`
|
|
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
|
|
Menu string `xml:"menu,attr,omitempty"`
|
|
Sort string `xml:"sort,attr,omitempty"`
|
|
StartItem int `xml:"startItem"`
|
|
NumItems int `xml:"numItems"`
|
|
Item *NavigateItem `xml:"item,omitempty"`
|
|
}
|
|
|
|
// NavigateResponse represents the response from a navigate request
|
|
type NavigateResponse struct {
|
|
XMLName xml.Name `xml:"navigateResponse"`
|
|
Source string `xml:"source,attr"`
|
|
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
|
|
TotalItems int `xml:"totalItems"`
|
|
Items []NavigateItem `xml:"items>item"`
|
|
}
|
|
|
|
// NavigateItem represents a single item in a navigate response
|
|
type NavigateItem struct {
|
|
XMLName xml.Name `xml:"item"`
|
|
Playable int `xml:"Playable,attr,omitempty"`
|
|
Name string `xml:"name"`
|
|
Type string `xml:"type"`
|
|
ContentItem *ContentItem `xml:"ContentItem,omitempty"`
|
|
MediaItemContainer *MediaItemContainer `xml:"mediaItemContainer,omitempty"`
|
|
ArtistName string `xml:"artistName,omitempty"`
|
|
AlbumName string `xml:"albumName,omitempty"`
|
|
}
|
|
|
|
// MediaItemContainer represents a media container within a navigate item
|
|
type MediaItemContainer struct {
|
|
XMLName xml.Name `xml:"mediaItemContainer"`
|
|
Offset int `xml:"offset,attr"`
|
|
ContentItem *ContentItem `xml:"ContentItem,omitempty"`
|
|
}
|
|
|
|
// AddStationRequest represents a request to add a station
|
|
type AddStationRequest struct {
|
|
XMLName xml.Name `xml:"addStation"`
|
|
Source string `xml:"source,attr"`
|
|
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
|
|
Token string `xml:"token,attr,omitempty"`
|
|
Name string `xml:"name"`
|
|
}
|
|
|
|
// RemoveStationRequest represents a request to remove a station
|
|
// Note: For removeStation, we send the ContentItem directly, not wrapped
|
|
type RemoveStationRequest = ContentItem
|
|
|
|
// StationResponse represents the response from add/remove station operations
|
|
type StationResponse struct {
|
|
XMLName xml.Name `xml:"status"`
|
|
Status string `xml:",chardata"`
|
|
}
|
|
|
|
// SearchStationRequest represents a request to search for stations
|
|
type SearchStationRequest struct {
|
|
XMLName xml.Name `xml:"search"`
|
|
Source string `xml:"source,attr"`
|
|
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
|
|
SearchTerm string `xml:",chardata"`
|
|
}
|
|
|
|
// SearchStationResponse represents the response from a station search
|
|
type SearchStationResponse struct {
|
|
XMLName xml.Name `xml:"results"`
|
|
DeviceID string `xml:"deviceID,attr"`
|
|
Source string `xml:"source,attr"`
|
|
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
|
|
Songs []SearchResult `xml:"songs>searchResult"`
|
|
Artists []SearchResult `xml:"artists>searchResult"`
|
|
Stations []SearchResult `xml:"stations>searchResult"`
|
|
}
|
|
|
|
// SearchResult represents a single search result (song, artist, or station)
|
|
type SearchResult struct {
|
|
XMLName xml.Name `xml:"searchResult"`
|
|
Source string `xml:"source,attr"`
|
|
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
|
|
Token string `xml:"token,attr"`
|
|
Name string `xml:"name"`
|
|
Artist string `xml:"artist,omitempty"`
|
|
Album string `xml:"album,omitempty"`
|
|
Logo string `xml:"logo,omitempty"`
|
|
Description string `xml:"description,omitempty"`
|
|
}
|
|
|
|
// NewNavigateRequest creates a new navigate request for browsing content
|
|
func NewNavigateRequest(source, sourceAccount string, startItem, numItems int) *NavigateRequest {
|
|
return &NavigateRequest{
|
|
Source: source,
|
|
SourceAccount: sourceAccount,
|
|
StartItem: startItem,
|
|
NumItems: numItems,
|
|
}
|
|
}
|
|
|
|
// NewNavigateRequestWithMenu creates a navigate request with menu and sort parameters
|
|
func NewNavigateRequestWithMenu(source, sourceAccount, menu, sort string, startItem, numItems int) *NavigateRequest {
|
|
return &NavigateRequest{
|
|
Source: source,
|
|
SourceAccount: sourceAccount,
|
|
Menu: menu,
|
|
Sort: sort,
|
|
StartItem: startItem,
|
|
NumItems: numItems,
|
|
}
|
|
}
|
|
|
|
// NewNavigateRequestWithItem creates a navigate request to browse a specific container item
|
|
func NewNavigateRequestWithItem(source, sourceAccount string, startItem, numItems int, item *ContentItem) *NavigateRequest {
|
|
navigateItem := &NavigateItem{
|
|
Playable: 1,
|
|
Name: item.ItemName,
|
|
Type: "dir",
|
|
ContentItem: item,
|
|
}
|
|
|
|
return &NavigateRequest{
|
|
Source: source,
|
|
SourceAccount: sourceAccount,
|
|
StartItem: startItem,
|
|
NumItems: numItems,
|
|
Item: navigateItem,
|
|
}
|
|
}
|
|
|
|
// NewAddStationRequest creates a new add station request
|
|
func NewAddStationRequest(source, sourceAccount, token, name string) *AddStationRequest {
|
|
return &AddStationRequest{
|
|
Source: source,
|
|
SourceAccount: sourceAccount,
|
|
Token: token,
|
|
Name: name,
|
|
}
|
|
}
|
|
|
|
// NewRemoveStationRequest creates a new remove station request
|
|
func NewRemoveStationRequest(contentItem *ContentItem) *RemoveStationRequest {
|
|
return contentItem
|
|
}
|
|
|
|
// NewSearchStationRequest creates a new search station request
|
|
func NewSearchStationRequest(source, sourceAccount, searchTerm string) *SearchStationRequest {
|
|
return &SearchStationRequest{
|
|
Source: source,
|
|
SourceAccount: sourceAccount,
|
|
SearchTerm: searchTerm,
|
|
}
|
|
}
|
|
|
|
// GetPlayableItems returns only the playable items from the navigate response
|
|
func (nr *NavigateResponse) GetPlayableItems() []NavigateItem {
|
|
var playable []NavigateItem
|
|
|
|
for _, item := range nr.Items {
|
|
if item.Playable == 1 {
|
|
playable = append(playable, item)
|
|
}
|
|
}
|
|
|
|
return playable
|
|
}
|
|
|
|
// GetDirectories returns only the directory items from the navigate response
|
|
func (nr *NavigateResponse) GetDirectories() []NavigateItem {
|
|
var directories []NavigateItem
|
|
|
|
for _, item := range nr.Items {
|
|
if item.Type == "dir" {
|
|
directories = append(directories, item)
|
|
}
|
|
}
|
|
|
|
return directories
|
|
}
|
|
|
|
// GetTracks returns only the track items from the navigate response
|
|
func (nr *NavigateResponse) GetTracks() []NavigateItem {
|
|
var tracks []NavigateItem
|
|
|
|
for _, item := range nr.Items {
|
|
if item.Type == "track" {
|
|
tracks = append(tracks, item)
|
|
}
|
|
}
|
|
|
|
return tracks
|
|
}
|
|
|
|
// GetStations returns only the station items from the navigate response
|
|
func (nr *NavigateResponse) GetStations() []NavigateItem {
|
|
var stations []NavigateItem
|
|
|
|
for _, item := range nr.Items {
|
|
if item.Type == "stationurl" || (item.ContentItem != nil && item.ContentItem.Type == "stationurl") {
|
|
stations = append(stations, item)
|
|
}
|
|
}
|
|
|
|
return stations
|
|
}
|
|
|
|
// IsEmpty returns true if the navigate response contains no items
|
|
func (nr *NavigateResponse) IsEmpty() bool {
|
|
return nr.TotalItems == 0 || len(nr.Items) == 0
|
|
}
|
|
|
|
// GetDisplayName returns the display name for a navigate item
|
|
func (ni *NavigateItem) GetDisplayName() string {
|
|
if ni.Name != "" {
|
|
return ni.Name
|
|
}
|
|
|
|
if ni.ContentItem != nil && ni.ContentItem.ItemName != "" {
|
|
return ni.ContentItem.ItemName
|
|
}
|
|
|
|
return "Unknown Item"
|
|
}
|
|
|
|
// IsPlayable returns true if the navigate item can be played directly
|
|
func (ni *NavigateItem) IsPlayable() bool {
|
|
return ni.Playable == 1
|
|
}
|
|
|
|
// IsDirectory returns true if the navigate item is a directory/container
|
|
func (ni *NavigateItem) IsDirectory() bool {
|
|
return ni.Type == "dir"
|
|
}
|
|
|
|
// IsTrack returns true if the navigate item is a track
|
|
func (ni *NavigateItem) IsTrack() bool {
|
|
return ni.Type == "track"
|
|
}
|
|
|
|
// IsStation returns true if the navigate item is a radio station
|
|
func (ni *NavigateItem) IsStation() bool {
|
|
return ni.Type == "stationurl" || (ni.ContentItem != nil && ni.ContentItem.Type == "stationurl")
|
|
}
|
|
|
|
// GetContentItem returns the ContentItem for this navigate item
|
|
func (ni *NavigateItem) GetContentItem() *ContentItem {
|
|
return ni.ContentItem
|
|
}
|
|
|
|
// GetArtwork returns the artwork URL if available
|
|
func (ni *NavigateItem) GetArtwork() string {
|
|
if ni.ContentItem != nil && ni.ContentItem.ContainerArt != "" {
|
|
return ni.ContentItem.ContainerArt
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// GetAllResults returns all search results regardless of type
|
|
func (sr *SearchStationResponse) GetAllResults() []SearchResult {
|
|
var allResults []SearchResult
|
|
|
|
allResults = append(allResults, sr.Songs...)
|
|
allResults = append(allResults, sr.Artists...)
|
|
allResults = append(allResults, sr.Stations...)
|
|
|
|
return allResults
|
|
}
|
|
|
|
// GetSongs returns only song results
|
|
func (sr *SearchStationResponse) GetSongs() []SearchResult {
|
|
return sr.Songs
|
|
}
|
|
|
|
// GetArtists returns only artist results
|
|
func (sr *SearchStationResponse) GetArtists() []SearchResult {
|
|
return sr.Artists
|
|
}
|
|
|
|
// GetStations returns only station results
|
|
func (sr *SearchStationResponse) GetStations() []SearchResult {
|
|
return sr.Stations
|
|
}
|
|
|
|
// HasResults returns true if the search response contains any results
|
|
func (sr *SearchStationResponse) HasResults() bool {
|
|
return len(sr.Songs) > 0 || len(sr.Artists) > 0 || len(sr.Stations) > 0
|
|
}
|
|
|
|
// GetResultCount returns the total number of results
|
|
func (sr *SearchStationResponse) GetResultCount() int {
|
|
return len(sr.Songs) + len(sr.Artists) + len(sr.Stations)
|
|
}
|
|
|
|
// IsEmpty returns true if the search response contains no results
|
|
func (sr *SearchStationResponse) IsEmpty() bool {
|
|
return !sr.HasResults()
|
|
}
|
|
|
|
// GetDisplayName returns the display name for a search result
|
|
func (sr *SearchResult) GetDisplayName() string {
|
|
if sr.Name != "" {
|
|
return sr.Name
|
|
}
|
|
|
|
return "Unknown"
|
|
}
|
|
|
|
// GetArtworkURL returns the logo/artwork URL if available
|
|
func (sr *SearchResult) GetArtworkURL() string {
|
|
return sr.Logo
|
|
}
|
|
|
|
// IsArtist returns true if this is an artist result
|
|
func (sr *SearchResult) IsArtist() bool {
|
|
// Artist result: no artist field (name is the artist) and no description
|
|
return sr.Artist == "" && sr.Album == "" && sr.Description == ""
|
|
}
|
|
|
|
// IsSong returns true if this is a song result
|
|
func (sr *SearchResult) IsSong() bool {
|
|
// Song result: has artist field populated
|
|
return sr.Artist != ""
|
|
}
|
|
|
|
// IsStation returns true if this is a station result
|
|
func (sr *SearchResult) IsStation() bool {
|
|
// Station result: has description or is neither artist nor song
|
|
return sr.Description != "" || (!sr.IsSong() && !sr.IsArtist())
|
|
}
|
|
|
|
// GetFullTitle returns a full title with artist if available
|
|
func (sr *SearchResult) GetFullTitle() string {
|
|
if sr.Artist != "" {
|
|
return sr.Name + " - " + sr.Artist
|
|
}
|
|
|
|
return sr.Name
|
|
}
|