package spotify import ( "context" "encoding/json" "errors" "fmt" "golang.org/x/oauth2/clientcredentials" spotifyAuth "golang.org/x/oauth2/spotify" "io" "net/http" "net/url" "strings" ) type SpotifyClient struct { clientID string clientSecret string bearerAuth string baseURL *url.URL httpClient *http.Client } type TrackInfo struct { Album AlbumInfo `json:"album"` Artists []ArtistInfo `json:"artists"` DiskNumber int `json:"disc_number"` DurationMS int `json:"duration_ms"` Explicit bool `json:"explicit"` ExternalIds ExternalIds `json:"external_ids"` ExternalUrls ExternalUrls `json:"external_urls"` Href string `json:"href"` ID string `json:"id"` IsPlayable bool `json:"is_playable"` Name string `json:"name"` Popularity int `json:"popularity"` PreviewUrl string `json:"preview_url"` TrackNumber int `json:"track_number"` Type string `json:"type"` Uri string `json:"uri"` } type AlbumInfo struct { AlbumType string `json:"album_type"` Artists []ArtistInfo `json:"artists"` ExternalUrls ExternalUrls `json:"external_urls"` Href string `json:"href"` ID string `json:"id"` Images []ImageInfo `json:"images"` Name string `json:"name"` Type string `json:"type"` Uri string `json:"uri"` } type ArtistInfo struct { ExternalUrls ExternalUrls `json:"external_urls"` Href string `json:"href"` ID string `json:"id"` Name string `json:"name"` Type string `json:"type"` Uri string `json:"uri"` } type ImageInfo struct { Height int `json:"height"` URL string `json:url` Width int `json:"width"` } type ExternalIds struct { ISRC string `json:"isrc"` } type ExternalUrls struct { Spotify string `json:"spotify"` } type TokenInfo struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` ExpiresIn int `json:"expires_in"` } type AuthErrorResponse struct { Error string `json:"error"` ErrorDescription string `json:"error_description"` } type ErrorResponse struct { Error ErrorInfo `json:"error"` } type ErrorInfo struct { Status int `json:"status"` Message string `json:"message"` } func NewClient(username, secret string) *SpotifyClient { baseURL, _ := url.Parse("https://api.spotify.com") httpClient := &http.Client{} return &SpotifyClient{ clientID: username, clientSecret: secret, baseURL: baseURL, httpClient: httpClient, } } func (s *SpotifyClient) Authenticate() error { config := &clientcredentials.Config{ ClientID: s.clientID, ClientSecret: s.clientSecret, TokenURL: spotifyAuth.Endpoint.TokenURL, } token, err := config.Token(context.Background()) if err != nil { fmt.Println(err) return err } s.bearerAuth = token.AccessToken return nil } func (s *SpotifyClient) GetTrack(trackId string) (*TrackInfo, error) { var trackInfo TrackInfo url := s.urlFromBase(fmt.Sprintf("/v1/tracks/%s", trackId)) err := s.doRequest("GET", url, &trackInfo, nil) if err != nil { return nil, err } else { return &trackInfo, nil } } func (s *SpotifyClient) urlFromBase(path string) *url.URL { endpoint := &url.URL{Path: path} return s.baseURL.ResolveReference(endpoint) } func (s *SpotifyClient) doRequest(method string, url *url.URL, object interface{}, values *url.Values) error { var bodyReader io.Reader if values != nil { s := values.Encode() bodyReader = strings.NewReader(s) } req, err := http.NewRequest(method, url.String(), bodyReader) if err != nil { return err } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.bearerAuth)) if values != nil { req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", "github.com/lamperi/e4bot/spotify") resp, err := s.httpClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { var errorResp ErrorResponse err = json.NewDecoder(resp.Body).Decode(&errorResp) return errors.New(fmt.Sprintf("Spotify API returned: %d: %s", resp.StatusCode, errorResp.Error.Message)) } err = json.NewDecoder(resp.Body).Decode(object) if err != nil { return err } return nil }