//go:build wasip1 package main import ( "context" "encoding/json" "fmt" "log" "github.com/navidrome/navidrome/plugins/api" "github.com/navidrome/navidrome/plugins/host/http" ) type CoverArtArchiveAgent struct{} var ErrNotFound = api.ErrNotFound type caaImage struct { Image string `json:"image"` Front bool `json:"front"` Types []string `json:"types"` Thumbnails map[string]string `json:"thumbnails"` } var client = http.NewHttpService() func (CoverArtArchiveAgent) GetAlbumImages(ctx context.Context, req *api.AlbumImagesRequest) (*api.AlbumImagesResponse, error) { if req.Mbid == "" { return nil, ErrNotFound } url := "https://coverartarchive.org/release/" + req.Mbid resp, err := client.Get(ctx, &http.HttpRequest{Url: url, TimeoutMs: 5000}) if err != nil || resp.Status != 200 { log.Printf("[CAA] Error getting album images from CoverArtArchive (status: %d): %v", resp.Status, err) return nil, ErrNotFound } images, err := extractFrontImages(resp.Body) if err != nil || len(images) == 0 { return nil, ErrNotFound } return &api.AlbumImagesResponse{Images: images}, nil } func extractFrontImages(body []byte) ([]*api.ExternalImage, error) { var data struct { Images []caaImage `json:"images"` } if err := json.Unmarshal(body, &data); err != nil { return nil, err } img := findFrontImage(data.Images) if img == nil { return nil, ErrNotFound } return buildImageList(img), nil } func findFrontImage(images []caaImage) *caaImage { for i, img := range images { if img.Front { return &images[i] } } for i, img := range images { for _, t := range img.Types { if t == "Front" { return &images[i] } } } if len(images) > 0 { return &images[0] } return nil } func buildImageList(img *caaImage) []*api.ExternalImage { var images []*api.ExternalImage // First, try numeric sizes only for sizeStr, url := range img.Thumbnails { if url == "" { continue } size := 0 if _, err := fmt.Sscanf(sizeStr, "%d", &size); err == nil { images = append(images, &api.ExternalImage{Url: url, Size: int32(size)}) } } // If no numeric sizes, fallback to large/small if len(images) == 0 { for sizeStr, url := range img.Thumbnails { if url == "" { continue } var size int switch sizeStr { case "large": size = 500 case "small": size = 250 default: continue } images = append(images, &api.ExternalImage{Url: url, Size: int32(size)}) } } if len(images) == 0 && img.Image != "" { images = append(images, &api.ExternalImage{Url: img.Image, Size: 0}) } return images } func (CoverArtArchiveAgent) GetAlbumInfo(ctx context.Context, req *api.AlbumInfoRequest) (*api.AlbumInfoResponse, error) { return nil, api.ErrNotImplemented } func (CoverArtArchiveAgent) GetArtistMBID(ctx context.Context, req *api.ArtistMBIDRequest) (*api.ArtistMBIDResponse, error) { return nil, api.ErrNotImplemented } func (CoverArtArchiveAgent) GetArtistURL(ctx context.Context, req *api.ArtistURLRequest) (*api.ArtistURLResponse, error) { return nil, api.ErrNotImplemented } func (CoverArtArchiveAgent) GetArtistBiography(ctx context.Context, req *api.ArtistBiographyRequest) (*api.ArtistBiographyResponse, error) { return nil, api.ErrNotImplemented } func (CoverArtArchiveAgent) GetSimilarArtists(ctx context.Context, req *api.ArtistSimilarRequest) (*api.ArtistSimilarResponse, error) { return nil, api.ErrNotImplemented } func (CoverArtArchiveAgent) GetArtistImages(ctx context.Context, req *api.ArtistImageRequest) (*api.ArtistImageResponse, error) { return nil, api.ErrNotImplemented } func (CoverArtArchiveAgent) GetArtistTopSongs(ctx context.Context, req *api.ArtistTopSongsRequest) (*api.ArtistTopSongsResponse, error) { return nil, api.ErrNotImplemented } func main() {} func init() { api.RegisterMetadataAgent(CoverArtArchiveAgent{}) }