lyrics

lyrics
git clone git://brookjeynes.dev/bjeynes/lyrics.git
Log | Files | Refs

musicbrainz.go (2817B)


      1 package musicbrainz
      2 
      3 import (
      4 	"context"
      5 	"encoding/json"
      6 	"fmt"
      7 	"net/http"
      8 	"time"
      9 )
     10 
     11 type CoverArtQuality string
     12 
     13 const (
     14 	CoverArtQuality250      CoverArtQuality = "250"
     15 	CoverArtQuality500      CoverArtQuality = "500"
     16 	CoverArtQuality1200     CoverArtQuality = "1200"
     17 	CoverArtQualitySmall    CoverArtQuality = "small"
     18 	CoverArtQualityLarge    CoverArtQuality = "large"
     19 	CoverArtQualityOriginal CoverArtQuality = "original"
     20 )
     21 
     22 type CoverArtResponse struct {
     23 	Images []CoverArtImage `json:"images"`
     24 }
     25 
     26 type CoverArtImage struct {
     27 	Front      bool              `json:"front"`
     28 	Approved   bool              `json:"approved"`
     29 	Image      string            `json:"image"`
     30 	Thumbnails map[string]string `json:"thumbnails"`
     31 }
     32 
     33 type CoverArt struct {
     34 	URL     string
     35 	Quality CoverArtQuality
     36 }
     37 
     38 var client = &http.Client{
     39 	Timeout: 5 * time.Second,
     40 }
     41 
     42 func FetchCoverArt(ctx context.Context, mbid string, quality CoverArtQuality) (*CoverArt, error) {
     43 	req, err := http.NewRequestWithContext(
     44 		ctx,
     45 		http.MethodGet,
     46 		"https://coverartarchive.org/release/"+mbid,
     47 		nil,
     48 	)
     49 	if err != nil {
     50 		return nil, err
     51 	}
     52 
     53 	req.Header.Set("Accept", "application/json")
     54 	req.Header.Set("User-Agent", "lyrics/0.1")
     55 
     56 	res, err := client.Do(req)
     57 	if err != nil {
     58 		return nil, err
     59 	}
     60 	defer res.Body.Close()
     61 
     62 	if res.StatusCode == http.StatusNotFound {
     63 		return nil, nil
     64 	}
     65 	if res.StatusCode < 200 || res.StatusCode >= 300 {
     66 		return nil, fmt.Errorf("cover art request failed: %s", res.Status)
     67 	}
     68 
     69 	var data CoverArtResponse
     70 	if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
     71 		return nil, err
     72 	}
     73 
     74 	for _, image := range data.Images {
     75 		if !image.Front {
     76 			continue
     77 		}
     78 
     79 		url, actualQuality := coverArtURL(image, quality)
     80 		if url == "" {
     81 			return nil, nil
     82 		}
     83 
     84 		return &CoverArt{
     85 			URL:     url,
     86 			Quality: actualQuality,
     87 		}, nil
     88 	}
     89 
     90 	return nil, nil
     91 }
     92 
     93 func coverArtURL(image CoverArtImage, quality CoverArtQuality) (string, CoverArtQuality) {
     94 	switch quality {
     95 	case CoverArtQualityOriginal:
     96 		if image.Image != "" {
     97 			return image.Image, CoverArtQualityOriginal
     98 		}
     99 		fallthrough
    100 	case CoverArtQuality1200:
    101 		if url := image.Thumbnails["1200"]; url != "" {
    102 			return url, CoverArtQuality1200
    103 		}
    104 		fallthrough
    105 	case CoverArtQualityLarge:
    106 		if url := image.Thumbnails["large"]; url != "" {
    107 			return url, CoverArtQuality500
    108 		}
    109 		fallthrough
    110 	case CoverArtQuality500:
    111 		if url := image.Thumbnails["500"]; url != "" {
    112 			return url, CoverArtQuality500
    113 		}
    114 		fallthrough
    115 	case CoverArtQualitySmall:
    116 		if url := image.Thumbnails["small"]; url != "" {
    117 			return url, CoverArtQuality250
    118 		}
    119 		fallthrough
    120 	case CoverArtQuality250:
    121 		if url := image.Thumbnails["250"]; url != "" {
    122 			return url, CoverArtQuality250
    123 		}
    124 	}
    125 
    126 	if image.Image != "" {
    127 		return image.Image, CoverArtQualityOriginal
    128 	}
    129 
    130 	return "", ""
    131 }