feat: add subsonic library discovery endpoints
This commit is contained in:
@@ -25,6 +25,7 @@ type Album struct {
|
||||
Title string `json:"title"`
|
||||
Year int `json:"year"`
|
||||
TrackCount int `json:"trackCount"`
|
||||
Genre string `json:"genre"`
|
||||
CoverArtID string `json:"coverArtId"`
|
||||
}
|
||||
|
||||
@@ -73,6 +74,12 @@ type StarredResults struct {
|
||||
Tracks []Track `json:"tracks"`
|
||||
}
|
||||
|
||||
type GenreSummary struct {
|
||||
Value string `json:"value"`
|
||||
AlbumCount int `json:"albumCount"`
|
||||
SongCount int `json:"songCount"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
db *sql.DB
|
||||
}
|
||||
@@ -165,6 +172,7 @@ func (s *Service) RecentAlbums(ctx context.Context, limit int) ([]Album, error)
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id) AS track_count
|
||||
, COALESCE(al.genre, '')
|
||||
, COALESCE(al.cover_art_id, '')
|
||||
FROM albums al
|
||||
JOIN artists a ON a.id = al.artist_id
|
||||
@@ -182,7 +190,7 @@ func (s *Service) RecentAlbums(ctx context.Context, limit int) ([]Album, error)
|
||||
var albums []Album
|
||||
for rows.Next() {
|
||||
var album Album
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.CoverArtID); err != nil {
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.Genre, &album.CoverArtID); err != nil {
|
||||
return nil, fmt.Errorf("scan album: %w", err)
|
||||
}
|
||||
albums = append(albums, album)
|
||||
@@ -194,7 +202,7 @@ func (s *Service) RecentAlbums(ctx context.Context, limit int) ([]Album, error)
|
||||
func (s *Service) Albums(ctx context.Context, limit int) ([]Album, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id) AS track_count, COALESCE(al.cover_art_id, '')
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id) AS track_count, COALESCE(al.genre, ''), COALESCE(al.cover_art_id, '')
|
||||
FROM albums al
|
||||
JOIN artists a ON a.id = al.artist_id
|
||||
LEFT JOIN tracks t ON t.album_id = al.id
|
||||
@@ -211,7 +219,7 @@ func (s *Service) Albums(ctx context.Context, limit int) ([]Album, error) {
|
||||
var albums []Album
|
||||
for rows.Next() {
|
||||
var album Album
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.CoverArtID); err != nil {
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.Genre, &album.CoverArtID); err != nil {
|
||||
return nil, fmt.Errorf("scan all albums: %w", err)
|
||||
}
|
||||
albums = append(albums, album)
|
||||
@@ -225,7 +233,7 @@ func (s *Service) AlbumByID(ctx context.Context, id string) (AlbumDetail, error)
|
||||
err := s.db.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0),
|
||||
COUNT(t.id) AS track_count, COALESCE(al.cover_art_id, '')
|
||||
COUNT(t.id) AS track_count, COALESCE(al.genre, ''), COALESCE(al.cover_art_id, '')
|
||||
FROM albums al
|
||||
JOIN artists a ON a.id = al.artist_id
|
||||
LEFT JOIN tracks t ON t.album_id = al.id
|
||||
@@ -239,6 +247,7 @@ func (s *Service) AlbumByID(ctx context.Context, id string) (AlbumDetail, error)
|
||||
&album.Title,
|
||||
&album.Year,
|
||||
&album.TrackCount,
|
||||
&album.Genre,
|
||||
&album.CoverArtID,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -380,6 +389,32 @@ func (s *Service) Starred(ctx context.Context, userID string) (StarredResults, e
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Genres(ctx context.Context) ([]GenreSummary, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT al.genre, COUNT(DISTINCT al.id) AS album_count, COUNT(t.id) AS song_count
|
||||
FROM albums al
|
||||
LEFT JOIN tracks t ON t.album_id = al.id
|
||||
WHERE TRIM(COALESCE(al.genre, '')) <> ''
|
||||
GROUP BY al.genre
|
||||
ORDER BY song_count DESC, album_count DESC, al.genre ASC`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query genres: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var genres []GenreSummary
|
||||
for rows.Next() {
|
||||
var genre GenreSummary
|
||||
if err := rows.Scan(&genre.Value, &genre.AlbumCount, &genre.SongCount); err != nil {
|
||||
return nil, fmt.Errorf("scan genre: %w", err)
|
||||
}
|
||||
genres = append(genres, genre)
|
||||
}
|
||||
return genres, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Service) Star(ctx context.Context, userID string, trackIDs, albumIDs, artistIDs []string) error {
|
||||
return s.updateFavorites(ctx, userID, trackIDs, albumIDs, artistIDs, true)
|
||||
}
|
||||
@@ -553,7 +588,7 @@ func (s *Service) PopulateTrackStats(ctx context.Context, userID string, tracks
|
||||
func (s *Service) albumsByArtistID(ctx context.Context, artistID string) ([]Album, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id) AS track_count, COALESCE(al.cover_art_id, '')
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id) AS track_count, COALESCE(al.genre, ''), COALESCE(al.cover_art_id, '')
|
||||
FROM albums al
|
||||
JOIN artists a ON a.id = al.artist_id
|
||||
LEFT JOIN tracks t ON t.album_id = al.id
|
||||
@@ -570,7 +605,7 @@ func (s *Service) albumsByArtistID(ctx context.Context, artistID string) ([]Albu
|
||||
var albums []Album
|
||||
for rows.Next() {
|
||||
var album Album
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.CoverArtID); err != nil {
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.Genre, &album.CoverArtID); err != nil {
|
||||
return nil, fmt.Errorf("scan album by artist: %w", err)
|
||||
}
|
||||
albums = append(albums, album)
|
||||
@@ -631,7 +666,7 @@ func (s *Service) searchArtists(ctx context.Context, pattern string, limit int)
|
||||
func (s *Service) searchAlbums(ctx context.Context, pattern string, limit int) ([]Album, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id), COALESCE(al.cover_art_id, '')
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id), COALESCE(al.genre, ''), COALESCE(al.cover_art_id, '')
|
||||
FROM albums al
|
||||
JOIN artists a ON a.id = al.artist_id
|
||||
LEFT JOIN tracks t ON t.album_id = al.id
|
||||
@@ -651,7 +686,7 @@ func (s *Service) searchAlbums(ctx context.Context, pattern string, limit int) (
|
||||
var albums []Album
|
||||
for rows.Next() {
|
||||
var album Album
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.CoverArtID); err != nil {
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.Genre, &album.CoverArtID); err != nil {
|
||||
return nil, fmt.Errorf("scan searched album: %w", err)
|
||||
}
|
||||
albums = append(albums, album)
|
||||
@@ -753,7 +788,7 @@ func (s *Service) starredArtists(ctx context.Context, userID string) ([]Artist,
|
||||
func (s *Service) starredAlbums(ctx context.Context, userID string) ([]Album, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id), COALESCE(al.cover_art_id, '')
|
||||
`SELECT al.id, al.artist_id, a.name, al.title, COALESCE(al.year, 0), COUNT(t.id), COALESCE(al.genre, ''), COALESCE(al.cover_art_id, '')
|
||||
FROM favorites f
|
||||
JOIN albums al ON al.id = f.entity_id
|
||||
JOIN artists a ON a.id = al.artist_id
|
||||
@@ -771,7 +806,7 @@ func (s *Service) starredAlbums(ctx context.Context, userID string) ([]Album, er
|
||||
var albums []Album
|
||||
for rows.Next() {
|
||||
var album Album
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.CoverArtID); err != nil {
|
||||
if err := rows.Scan(&album.ID, &album.ArtistID, &album.ArtistName, &album.Title, &album.Year, &album.TrackCount, &album.Genre, &album.CoverArtID); err != nil {
|
||||
return nil, fmt.Errorf("scan starred album: %w", err)
|
||||
}
|
||||
albums = append(albums, album)
|
||||
|
||||
Reference in New Issue
Block a user