-
Notifications
You must be signed in to change notification settings - Fork 295
/
recommendation.go
120 lines (102 loc) · 3.75 KB
/
recommendation.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package spotify
import (
"context"
"fmt"
"net/url"
"strconv"
"strings"
)
// Seeds contains IDs of artists, genres and/or tracks
// to be used as seeds for recommendations
type Seeds struct {
Artists []ID
Tracks []ID
Genres []string
}
// count returns the total number of seeds contained in s.
func (s Seeds) count() int {
return len(s.Artists) + len(s.Tracks) + len(s.Genres)
}
// Recommendations contains a list of recommended tracks based on seeds.
type Recommendations struct {
Seeds []RecommendationSeed `json:"seeds"`
Tracks []SimpleTrack `json:"tracks"`
}
// RecommendationSeed represents a recommendation seed after
// being processed by the Spotify API.
type RecommendationSeed struct {
AfterFilteringSize Numeric `json:"afterFilteringSize"`
AfterRelinkingSize Numeric `json:"afterRelinkingSize"`
Endpoint string `json:"href"`
ID ID `json:"id"`
InitialPoolSize Numeric `json:"initialPoolSize"`
Type string `json:"type"`
}
// MaxNumberOfSeeds allowed by Spotify for a recommendation request.
const MaxNumberOfSeeds = 5
// setSeedValues sets url values into v for each seed in seeds
func setSeedValues(seeds Seeds, v url.Values) {
if len(seeds.Artists) != 0 {
v.Set("seed_artists", strings.Join(toStringSlice(seeds.Artists), ","))
}
if len(seeds.Tracks) != 0 {
v.Set("seed_tracks", strings.Join(toStringSlice(seeds.Tracks), ","))
}
if len(seeds.Genres) != 0 {
v.Set("seed_genres", strings.Join(seeds.Genres, ","))
}
}
// setTrackAttributesValues sets track attributes values to the given url values
func setTrackAttributesValues(trackAttributes *TrackAttributes, values url.Values) {
if trackAttributes == nil {
return
}
for attr, val := range trackAttributes.intAttributes {
values.Set(attr, strconv.Itoa(val))
}
for attr, val := range trackAttributes.floatAttributes {
values.Set(attr, strconv.FormatFloat(val, 'f', -1, 64))
}
}
// GetRecommendations returns a [list of recommended tracks] based on the given
// seeds. Recommendations are generated based on the available information for a
// given seed entity and matched against similar artists and tracks. If there is
// sufficient information about the provided seeds, a list of tracks will be
// returned together with pool size details. For artists and tracks that are
// very new or obscure there might not be enough data to generate a list of
// tracks.
//
// Supported options: [Limit], [Country].
//
// [list of recommended tracks]: https://developer.spotify.com/documentation/web-api/reference/get-recommendations
func (c *Client) GetRecommendations(ctx context.Context, seeds Seeds, trackAttributes *TrackAttributes, opts ...RequestOption) (*Recommendations, error) {
v := processOptions(opts...).urlParams
if seeds.count() == 0 {
return nil, fmt.Errorf("spotify: at least one seed is required")
}
if seeds.count() > MaxNumberOfSeeds {
return nil, fmt.Errorf("spotify: exceeded maximum of %d seeds", MaxNumberOfSeeds)
}
setSeedValues(seeds, v)
setTrackAttributesValues(trackAttributes, v)
spotifyURL := c.baseURL + "recommendations?" + v.Encode()
var recommendations Recommendations
err := c.get(ctx, spotifyURL, &recommendations)
if err != nil {
return nil, err
}
return &recommendations, err
}
// GetAvailableGenreSeeds retrieves a [list of available genres] seed parameter
// values for recommendations.
//
// [list of available genres]: https://developer.spotify.com/documentation/web-api/reference/get-recommendation-genres
func (c *Client) GetAvailableGenreSeeds(ctx context.Context) ([]string, error) {
spotifyURL := c.baseURL + "recommendations/available-genre-seeds"
genreSeeds := make(map[string][]string)
err := c.get(ctx, spotifyURL, &genreSeeds)
if err != nil {
return nil, err
}
return genreSeeds["genres"], nil
}