1package spotify 2 3import ( 4 "errors" 5 "fmt" 6 "net/url" 7 "strconv" 8 "strings" 9 "time" 10) 11 12// SimpleAlbum contains basic data about an album. 13type SimpleAlbum struct { 14 // The name of the album. 15 Name string `json:"name"` 16 // A slice of SimpleArtists 17 Artists []SimpleArtist `json:"artists"` 18 // The field is present when getting an artist’s 19 // albums. Possible values are “album”, “single”, 20 // “compilation”, “appears_on”. Compare to album_type 21 // this field represents relationship between the artist 22 // and the album. 23 AlbumGroup string `json:"album_group"` 24 // The type of the album: one of "album", 25 // "single", or "compilation". 26 AlbumType string `json:"album_type"` 27 // The SpotifyID for the album. 28 ID ID `json:"id"` 29 // The SpotifyURI for the album. 30 URI URI `json:"uri"` 31 // The markets in which the album is available, 32 // identified using ISO 3166-1 alpha-2 country 33 // codes. Note that al album is considered 34 // available in a market when at least 1 of its 35 // tracks is available in that market. 36 AvailableMarkets []string `json:"available_markets"` 37 // A link to the Web API enpoint providing full 38 // details of the album. 39 Endpoint string `json:"href"` 40 // The cover art for the album in various sizes, 41 // widest first. 42 Images []Image `json:"images"` 43 // Known external URLs for this album. 44 ExternalURLs map[string]string `json:"external_urls"` 45 // The date the album was first released. For example, "1981-12-15". 46 // Depending on the ReleaseDatePrecision, it might be shown as 47 // "1981" or "1981-12". You can use ReleaseDateTime to convert this 48 // to a time.Time value. 49 ReleaseDate string `json:"release_date"` 50 // The precision with which ReleaseDate value is known: "year", "month", or "day" 51 ReleaseDatePrecision string `json:"release_date_precision"` 52} 53 54// Copyright contains the copyright statement associated with an album. 55type Copyright struct { 56 // The copyright text for the album. 57 Text string `json:"text"` 58 // The type of copyright. 59 Type string `json:"type"` 60} 61 62// FullAlbum provides extra album data in addition to the data provided by SimpleAlbum. 63type FullAlbum struct { 64 SimpleAlbum 65 Artists []SimpleArtist `json:"artists"` 66 Copyrights []Copyright `json:"copyrights"` 67 Genres []string `json:"genres"` 68 // The popularity of the album, represented as an integer between 0 and 100, 69 // with 100 being the most popular. Popularity of an album is calculated 70 // from the popularify of the album's individual tracks. 71 Popularity int `json:"popularity"` 72 // The date the album was first released. For example, "1981-12-15". 73 // Depending on the ReleaseDatePrecision, it might be shown as 74 // "1981" or "1981-12". You can use ReleaseDateTime to convert this 75 // to a time.Time value. 76 ReleaseDate string `json:"release_date"` 77 // The precision with which ReleaseDate value is known: "year", "month", or "day" 78 ReleaseDatePrecision string `json:"release_date_precision"` 79 Tracks SimpleTrackPage `json:"tracks"` 80 ExternalIDs map[string]string `json:"external_ids"` 81} 82 83// SavedAlbum provides info about an album saved to an user's account. 84type SavedAlbum struct { 85 // The date and time the track was saved, represented as an ISO 86 // 8601 UTC timestamp with a zero offset (YYYY-MM-DDTHH:MM:SSZ). 87 // You can use the TimestampLayout constant to convert this to 88 // a time.Time value. 89 AddedAt string `json:"added_at"` 90 FullAlbum `json:"album"` 91} 92 93// ReleaseDateTime converts the album's ReleaseDate to a time.TimeValue. 94// All of the fields in the result may not be valid. For example, if 95// f.ReleaseDatePrecision is "month", then only the month and year 96// (but not the day) of the result are valid. 97func (f *FullAlbum) ReleaseDateTime() time.Time { 98 if f.ReleaseDatePrecision == "day" { 99 result, _ := time.Parse(DateLayout, f.ReleaseDate) 100 return result 101 } 102 if f.ReleaseDatePrecision == "month" { 103 ym := strings.Split(f.ReleaseDate, "-") 104 year, _ := strconv.Atoi(ym[0]) 105 month, _ := strconv.Atoi(ym[1]) 106 return time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) 107 } 108 year, _ := strconv.Atoi(f.ReleaseDate) 109 return time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC) 110} 111 112// GetAlbum gets Spotify catalog information for a single album, given its Spotify ID. 113func (c *Client) GetAlbum(id ID) (*FullAlbum, error) { 114 spotifyURL := fmt.Sprintf("%salbums/%s", c.baseURL, id) 115 116 var a FullAlbum 117 118 err := c.get(spotifyURL, &a) 119 if err != nil { 120 return nil, err 121 } 122 123 return &a, nil 124} 125 126func toStringSlice(ids []ID) []string { 127 result := make([]string, len(ids)) 128 for i, str := range ids { 129 result[i] = str.String() 130 } 131 return result 132} 133 134// GetAlbums gets Spotify Catalog information for multiple albums, given their 135// Spotify IDs. It supports up to 20 IDs in a single call. Albums are returned 136// in the order requested. If an album is not found, that position in the 137// result slice will be nil. 138func (c *Client) GetAlbums(ids ...ID) ([]*FullAlbum, error) { 139 if len(ids) > 20 { 140 return nil, errors.New("spotify: exceeded maximum number of albums") 141 } 142 spotifyURL := fmt.Sprintf("%salbums?ids=%s", c.baseURL, strings.Join(toStringSlice(ids), ",")) 143 144 var a struct { 145 Albums []*FullAlbum `json:"albums"` 146 } 147 148 err := c.get(spotifyURL, &a) 149 if err != nil { 150 return nil, err 151 } 152 153 return a.Albums, nil 154} 155 156// AlbumType represents the type of an album. It can be used to filter 157// results when searching for albums. 158type AlbumType int 159 160// AlbumType values that can be used to filter which types of albums are 161// searched for. These are flags that can be bitwise OR'd together 162// to search for multiple types of albums simultaneously. 163const ( 164 AlbumTypeAlbum AlbumType = 1 << iota 165 AlbumTypeSingle = 1 << iota 166 AlbummTypeAppearsOn = 1 << iota 167 AlbumTypeCompilation = 1 << iota 168) 169 170func (at AlbumType) encode() string { 171 types := []string{} 172 if at&AlbumTypeAlbum != 0 { 173 types = append(types, "album") 174 } 175 if at&AlbumTypeSingle != 0 { 176 types = append(types, "single") 177 } 178 if at&AlbummTypeAppearsOn != 0 { 179 types = append(types, "appears_on") 180 } 181 if at&AlbumTypeCompilation != 0 { 182 types = append(types, "compilation") 183 } 184 return strings.Join(types, ",") 185} 186 187// GetAlbumTracks gets the tracks for a particular album. 188// If you only care about the tracks, this call is more efficient 189// than GetAlbum. 190func (c *Client) GetAlbumTracks(id ID) (*SimpleTrackPage, error) { 191 return c.GetAlbumTracksOpt(id, -1, -1) 192} 193 194// GetAlbumTracksOpt behaves like GetAlbumTracks, with the exception that it 195// allows you to specify extra parameters that limit the number of results returned. 196// The maximum number of results to return is specified by limit. 197// The offset argument can be used to specify the index of the first track to return. 198// It can be used along with limit to reqeust the next set of results. 199func (c *Client) GetAlbumTracksOpt(id ID, limit, offset int) (*SimpleTrackPage, error) { 200 spotifyURL := fmt.Sprintf("%salbums/%s/tracks", c.baseURL, id) 201 v := url.Values{} 202 if limit != -1 { 203 v.Set("limit", strconv.Itoa(limit)) 204 } 205 if offset != -1 { 206 v.Set("offset", strconv.Itoa(offset)) 207 } 208 optional := v.Encode() 209 if optional != "" { 210 spotifyURL = spotifyURL + "?" + optional 211 } 212 213 var result SimpleTrackPage 214 err := c.get(spotifyURL, &result) 215 if err != nil { 216 return nil, err 217 } 218 219 return &result, nil 220} 221