spotify.go 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package spotify
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "golang.org/x/oauth2/clientcredentials"
  8. spotifyAuth "golang.org/x/oauth2/spotify"
  9. "io"
  10. "net/http"
  11. "net/url"
  12. "strings"
  13. )
  14. type SpotifyClient struct {
  15. clientID string
  16. clientSecret string
  17. bearerAuth string
  18. baseURL *url.URL
  19. httpClient *http.Client
  20. }
  21. type TrackInfo struct {
  22. Album AlbumInfo `json:"album"`
  23. Artists []ArtistInfo `json:"artists"`
  24. DiskNumber int `json:"disc_number"`
  25. DurationMS int `json:"duration_ms"`
  26. Explicit bool `json:"explicit"`
  27. ExternalIds ExternalIds `json:"external_ids"`
  28. ExternalUrls ExternalUrls `json:"external_urls"`
  29. Href string `json:"href"`
  30. ID string `json:"id"`
  31. IsPlayable bool `json:"is_playable"`
  32. Name string `json:"name"`
  33. Popularity int `json:"popularity"`
  34. PreviewUrl string `json:"preview_url"`
  35. TrackNumber int `json:"track_number"`
  36. Type string `json:"type"`
  37. Uri string `json:"uri"`
  38. }
  39. type AlbumInfo struct {
  40. AlbumType string `json:"album_type"`
  41. Artists []ArtistInfo `json:"artists"`
  42. ExternalUrls ExternalUrls `json:"external_urls"`
  43. Href string `json:"href"`
  44. ID string `json:"id"`
  45. Images []ImageInfo `json:"images"`
  46. Name string `json:"name"`
  47. Type string `json:"type"`
  48. Uri string `json:"uri"`
  49. }
  50. type ArtistInfo struct {
  51. ExternalUrls ExternalUrls `json:"external_urls"`
  52. Href string `json:"href"`
  53. ID string `json:"id"`
  54. Name string `json:"name"`
  55. Type string `json:"type"`
  56. Uri string `json:"uri"`
  57. }
  58. type ImageInfo struct {
  59. Height int `json:"height"`
  60. URL string `json:url`
  61. Width int `json:"width"`
  62. }
  63. type ExternalIds struct {
  64. ISRC string `json:"isrc"`
  65. }
  66. type ExternalUrls struct {
  67. Spotify string `json:"spotify"`
  68. }
  69. type TokenInfo struct {
  70. AccessToken string `json:"access_token"`
  71. TokenType string `json:"token_type"`
  72. ExpiresIn int `json:"expires_in"`
  73. }
  74. type AuthErrorResponse struct {
  75. Error string `json:"error"`
  76. ErrorDescription string `json:"error_description"`
  77. }
  78. type ErrorResponse struct {
  79. Error ErrorInfo `json:"error"`
  80. }
  81. type ErrorInfo struct {
  82. Status int `json:"status"`
  83. Message string `json:"message"`
  84. }
  85. func NewClient(username, secret string) *SpotifyClient {
  86. baseURL, _ := url.Parse("https://api.spotify.com")
  87. httpClient := &http.Client{}
  88. return &SpotifyClient{
  89. clientID: username,
  90. clientSecret: secret,
  91. baseURL: baseURL,
  92. httpClient: httpClient,
  93. }
  94. }
  95. func (s *SpotifyClient) Authenticate() error {
  96. config := &clientcredentials.Config{
  97. ClientID: s.clientID,
  98. ClientSecret: s.clientSecret,
  99. TokenURL: spotifyAuth.Endpoint.TokenURL,
  100. }
  101. token, err := config.Token(context.Background())
  102. if err != nil {
  103. fmt.Println(err)
  104. return err
  105. }
  106. s.bearerAuth = token.AccessToken
  107. return nil
  108. }
  109. func (s *SpotifyClient) GetTrack(trackId string) (*TrackInfo, error) {
  110. var trackInfo TrackInfo
  111. url := s.urlFromBase(fmt.Sprintf("/v1/tracks/%s", trackId))
  112. err := s.doRequest("GET", url, &trackInfo, nil)
  113. if err != nil {
  114. return nil, err
  115. } else {
  116. return &trackInfo, nil
  117. }
  118. }
  119. func (s *SpotifyClient) urlFromBase(path string) *url.URL {
  120. endpoint := &url.URL{Path: path}
  121. return s.baseURL.ResolveReference(endpoint)
  122. }
  123. func (s *SpotifyClient) doRequest(method string, url *url.URL, object interface{}, values *url.Values) error {
  124. var bodyReader io.Reader
  125. if values != nil {
  126. s := values.Encode()
  127. bodyReader = strings.NewReader(s)
  128. }
  129. req, err := http.NewRequest(method, url.String(), bodyReader)
  130. if err != nil {
  131. return err
  132. }
  133. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.bearerAuth))
  134. if values != nil {
  135. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  136. }
  137. req.Header.Set("Accept", "application/json")
  138. req.Header.Set("User-Agent", "github.com/lamperi/e4bot/spotify")
  139. resp, err := s.httpClient.Do(req)
  140. if err != nil {
  141. return err
  142. }
  143. defer resp.Body.Close()
  144. if resp.StatusCode != 200 {
  145. var errorResp ErrorResponse
  146. err = json.NewDecoder(resp.Body).Decode(&errorResp)
  147. return errors.New(fmt.Sprintf("Spotify API returned: %d: %s", resp.StatusCode, errorResp.Error.Message))
  148. }
  149. err = json.NewDecoder(resp.Body).Decode(object)
  150. if err != nil {
  151. return err
  152. }
  153. return nil
  154. }