feat: add browse api and subsonic read endpoints
Expose internal browse endpoints for artists, albums, tracks, and search using the SQLite-backed library service. Add Subsonic-compatible getArtist, getAlbum, getSong, and stream.view handlers by mapping the same persistence layer into lightweight response envelopes.
This commit is contained in:
@@ -18,6 +18,9 @@ type Response struct {
|
||||
Server string `json:"serverVersion"`
|
||||
OpenAPI bool `json:"openSubsonic"`
|
||||
Artists []ArtistRef `json:"artists,omitempty"`
|
||||
Artist *ArtistFull `json:"artist,omitempty"`
|
||||
Album *AlbumFull `json:"album,omitempty"`
|
||||
Song *SongFull `json:"song,omitempty"`
|
||||
RandomSong []SongRef `json:"randomSongs,omitempty"`
|
||||
ScanStatus *ScanStatus `json:"scanStatus,omitempty"`
|
||||
Error *ErrorRef `json:"error,omitempty"`
|
||||
@@ -35,6 +38,45 @@ type SongRef struct {
|
||||
Artist string `json:"artist"`
|
||||
}
|
||||
|
||||
type ArtistFull struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CoverArt string `json:"coverArt,omitempty"`
|
||||
AlbumCount int `json:"albumCount,omitempty"`
|
||||
Albums []AlbumRef `json:"album,omitempty"`
|
||||
}
|
||||
|
||||
type AlbumRef struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Artist string `json:"artist"`
|
||||
ArtistID string `json:"artistId"`
|
||||
Year int `json:"year,omitempty"`
|
||||
CoverArt string `json:"coverArt,omitempty"`
|
||||
}
|
||||
|
||||
type AlbumFull struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Artist string `json:"artist"`
|
||||
ArtistID string `json:"artistId"`
|
||||
Year int `json:"year,omitempty"`
|
||||
CoverArt string `json:"coverArt,omitempty"`
|
||||
Song []SongRef `json:"song,omitempty"`
|
||||
}
|
||||
|
||||
type SongFull struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Album string `json:"album"`
|
||||
Artist string `json:"artist"`
|
||||
AlbumID string `json:"albumId"`
|
||||
ArtistID string `json:"artistId"`
|
||||
Track int `json:"track,omitempty"`
|
||||
Duration int `json:"duration,omitempty"`
|
||||
CoverArt string `json:"coverArt,omitempty"`
|
||||
}
|
||||
|
||||
type ScanStatus struct {
|
||||
Scanning bool `json:"scanning"`
|
||||
Count int `json:"count"`
|
||||
@@ -85,6 +127,66 @@ func RandomSongsResponse(tracks []library.Track) Envelope {
|
||||
return response
|
||||
}
|
||||
|
||||
func ArtistResponse(artist library.ArtistDetail) Envelope {
|
||||
response := PingResponse()
|
||||
item := &ArtistFull{
|
||||
ID: artist.ID,
|
||||
Name: artist.Name,
|
||||
CoverArt: artist.CoverArtID,
|
||||
AlbumCount: artist.AlbumCount,
|
||||
}
|
||||
for _, album := range artist.Albums {
|
||||
item.Albums = append(item.Albums, AlbumRef{
|
||||
ID: album.ID,
|
||||
Name: album.Title,
|
||||
Artist: album.ArtistName,
|
||||
ArtistID: album.ArtistID,
|
||||
Year: album.Year,
|
||||
CoverArt: album.CoverArtID,
|
||||
})
|
||||
}
|
||||
response.SubsonicResponse.Artist = item
|
||||
return response
|
||||
}
|
||||
|
||||
func AlbumResponse(album library.AlbumDetail) Envelope {
|
||||
response := PingResponse()
|
||||
item := &AlbumFull{
|
||||
ID: album.ID,
|
||||
Name: album.Title,
|
||||
Artist: album.ArtistName,
|
||||
ArtistID: album.ArtistID,
|
||||
Year: album.Year,
|
||||
CoverArt: album.CoverArtID,
|
||||
}
|
||||
for _, track := range album.Tracks {
|
||||
item.Song = append(item.Song, SongRef{
|
||||
ID: track.ID,
|
||||
Title: track.Title,
|
||||
Album: track.AlbumTitle,
|
||||
Artist: track.ArtistName,
|
||||
})
|
||||
}
|
||||
response.SubsonicResponse.Album = item
|
||||
return response
|
||||
}
|
||||
|
||||
func SongResponse(track library.Track) Envelope {
|
||||
response := PingResponse()
|
||||
response.SubsonicResponse.Song = &SongFull{
|
||||
ID: track.ID,
|
||||
Title: track.Title,
|
||||
Album: track.AlbumTitle,
|
||||
Artist: track.ArtistName,
|
||||
AlbumID: track.AlbumID,
|
||||
ArtistID: track.ArtistID,
|
||||
Track: track.TrackNumber,
|
||||
Duration: track.DurationSecs,
|
||||
CoverArt: track.CoverArtID,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func ScanStatusResponse(status scanner.Status) Envelope {
|
||||
response := PingResponse()
|
||||
response.SubsonicResponse.ScanStatus = &ScanStatus{
|
||||
|
||||
Reference in New Issue
Block a user