Skip to content

Commit a80ad10

Browse files
committed
feat: configure album art size
1 parent 35437f8 commit a80ad10

7 files changed

Lines changed: 55 additions & 21 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ server:
129129
allow_origin: '' # Value for the Access-Control-Allow-Origin header
130130
cert_file: '' # Path to certificate file for TLS
131131
key_file: '' # Path to key file for TLS
132+
image_size: 'default' # Album art image size (default, small, large, xlarge)
132133
```
133134

134135
For detailed API documentation see [here](/API.md).

cmd/daemon/api_server.go

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"context"
5-
"encoding/hex"
65
"encoding/json"
76
"errors"
87
"fmt"
@@ -14,9 +13,9 @@ import (
1413
"sync"
1514
"time"
1615

17-
"github.com/rs/cors"
18-
1916
librespot "github.com/devgianlu/go-librespot"
17+
metadatapb "github.com/devgianlu/go-librespot/proto/spotify/metadata"
18+
"github.com/rs/cors"
2019
"nhooyr.io/websocket"
2120
"nhooyr.io/websocket/wsjson"
2221
)
@@ -139,15 +138,31 @@ type ApiResponseStatusTrack struct {
139138
Name string `json:"name"`
140139
ArtistNames []string `json:"artist_names"`
141140
AlbumName string `json:"album_name"`
142-
AlbumCoverUrl string `json:"album_cover_url"`
141+
AlbumCoverUrl *string `json:"album_cover_url"`
143142
Position int64 `json:"position"`
144143
Duration int `json:"duration"`
145144
ReleaseDate string `json:"release_date"`
146145
TrackNumber int `json:"track_number"`
147146
DiscNumber int `json:"disc_number"`
148147
}
149148

150-
func NewApiResponseStatusTrack(media *librespot.Media, prodInfo *ProductInfo, position int64) *ApiResponseStatusTrack {
149+
func getBestImageIdForSize(images []*metadatapb.Image, size string) []byte {
150+
if len(images) == 0 {
151+
return nil
152+
}
153+
154+
imageSize := metadatapb.Image_Size(metadatapb.Image_Size_value[strings.ToUpper(size)])
155+
156+
for _, img := range images {
157+
if img.Size != nil && *img.Size == imageSize {
158+
return img.FileId
159+
}
160+
}
161+
162+
return images[0].FileId
163+
}
164+
165+
func (p *AppPlayer) newApiResponseStatusTrack(media *librespot.Media, position int64) *ApiResponseStatusTrack {
151166
if media.IsTrack() {
152167
track := media.Track()
153168

@@ -156,19 +171,17 @@ func NewApiResponseStatusTrack(media *librespot.Media, prodInfo *ProductInfo, po
156171
artists = append(artists, *a.Name)
157172
}
158173

159-
var albumCoverId string
160-
if len(track.Album.Cover) > 0 {
161-
albumCoverId = hex.EncodeToString(track.Album.Cover[0].FileId)
162-
} else if track.Album.CoverGroup != nil && len(track.Album.CoverGroup.Image) > 0 {
163-
albumCoverId = hex.EncodeToString(track.Album.CoverGroup.Image[0].FileId)
174+
albumCoverId := getBestImageIdForSize(track.Album.Cover, p.app.cfg.Server.ImageSize)
175+
if albumCoverId == nil && track.Album.CoverGroup != nil {
176+
albumCoverId = getBestImageIdForSize(track.Album.CoverGroup.Image, p.app.cfg.Server.ImageSize)
164177
}
165178

166179
return &ApiResponseStatusTrack{
167180
Uri: librespot.SpotifyIdFromGid(librespot.SpotifyIdTypeTrack, track.Gid).Uri(),
168181
Name: *track.Name,
169182
ArtistNames: artists,
170183
AlbumName: *track.Album.Name,
171-
AlbumCoverUrl: prodInfo.ImageUrl(albumCoverId),
184+
AlbumCoverUrl: p.prodInfo.ImageUrl(albumCoverId),
172185
Position: position,
173186
Duration: int(*track.Duration),
174187
ReleaseDate: track.Album.Date.String(),
@@ -178,17 +191,14 @@ func NewApiResponseStatusTrack(media *librespot.Media, prodInfo *ProductInfo, po
178191
} else {
179192
episode := media.Episode()
180193

181-
var albumCoverId string
182-
if len(episode.CoverImage.Image) > 0 {
183-
albumCoverId = hex.EncodeToString(episode.CoverImage.Image[0].FileId)
184-
}
194+
albumCoverId := getBestImageIdForSize(episode.CoverImage.Image, p.app.cfg.Server.ImageSize)
185195

186196
return &ApiResponseStatusTrack{
187197
Uri: librespot.SpotifyIdFromGid(librespot.SpotifyIdTypeEpisode, episode.Gid).Uri(),
188198
Name: *episode.Name,
189199
ArtistNames: []string{*episode.Show.Name},
190200
AlbumName: *episode.Show.Name,
191-
AlbumCoverUrl: prodInfo.ImageUrl(albumCoverId),
201+
AlbumCoverUrl: p.prodInfo.ImageUrl(albumCoverId),
192202
Position: position,
193203
Duration: int(*episode.Duration),
194204
ReleaseDate: "",

cmd/daemon/controls.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ func (p *AppPlayer) loadCurrentTrack(ctx context.Context, paused, drop bool) err
358358

359359
p.app.server.Emit(&ApiEvent{
360360
Type: ApiEventTypeMetadata,
361-
Data: ApiEventDataMetadata(*NewApiResponseStatusTrack(p.primaryStream.Media, p.prodInfo, trackPosition)),
361+
Data: ApiEventDataMetadata(*p.newApiResponseStatusTrack(p.primaryStream.Media, trackPosition)),
362362
})
363363
return nil
364364
}

cmd/daemon/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,8 @@ type Config struct {
421421
AllowOrigin string `koanf:"allow_origin"`
422422
CertFile string `koanf:"cert_file"`
423423
KeyFile string `koanf:"key_file"`
424+
425+
ImageSize string `koanf:"image_size"`
424426
} `koanf:"server"`
425427
Credentials struct {
426428
Type string `koanf:"type"`
@@ -489,7 +491,9 @@ func loadConfig(cfg *Config) error {
489491
"initial_volume": 100,
490492

491493
"credentials.type": "zeroconf",
492-
"server.address": "localhost",
494+
495+
"server.address": "localhost",
496+
"server.image_size": "default",
493497
}, "."), nil)
494498

495499
// load file configuration (if available)

cmd/daemon/player.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ func (p *AppPlayer) handleApiRequest(ctx context.Context, req ApiRequest) (any,
435435
}
436436

437437
if p.primaryStream != nil && p.prodInfo != nil {
438-
resp.Track = NewApiResponseStatusTrack(p.primaryStream.Media, p.prodInfo, p.state.trackPosition())
438+
resp.Track = p.newApiResponseStatusTrack(p.primaryStream.Media, p.state.trackPosition())
439439
}
440440

441441
return resp, nil

cmd/daemon/product_info.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"encoding/hex"
45
"encoding/xml"
56
"strings"
67
)
@@ -15,6 +16,14 @@ type ProductInfo struct {
1516
} `xml:"product"`
1617
}
1718

18-
func (pi ProductInfo) ImageUrl(fileId string) string {
19-
return strings.Replace(pi.Products[0].ImageUrl, "{file_id}", strings.ToLower(fileId), 1)
19+
func (pi ProductInfo) ImageUrl(fileId []byte) *string {
20+
if len(pi.Products) == 0 || pi.Products[0].ImageUrl == "" {
21+
return nil
22+
} else if len(fileId) == 0 {
23+
return nil
24+
}
25+
26+
fileIdHex := strings.ToLower(hex.EncodeToString(fileId))
27+
val := strings.Replace(pi.Products[0].ImageUrl, "{file_id}", fileIdHex, 1)
28+
return &val
2029
}

config_schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,16 @@
147147
"type": "string",
148148
"description": "File path of the private key file to use for TLS",
149149
"default": ""
150+
},
151+
"image_size": {
152+
"type": "string",
153+
"description": "The preferred size of album cover images served by the API server",
154+
"enum": [
155+
"default",
156+
"small",
157+
"large",
158+
"xlarge"
159+
]
150160
}
151161
}
152162
},

0 commit comments

Comments
 (0)