|
@@ -0,0 +1,171 @@
|
|
|
|
+package mb
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "encoding/json"
|
|
|
|
+ "fmt"
|
|
|
|
+ "net/http"
|
|
|
|
+ "sort"
|
|
|
|
+ "time"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+const (
|
|
|
|
+ apiURI = "https://musicbrainz.org/ws/2"
|
|
|
|
+ artistSearchEndpoint = "artist?query"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+//MB holds the http client
|
|
|
|
+type MB struct {
|
|
|
|
+ Client http.Client
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//Search holds the search returns
|
|
|
|
+type Search struct {
|
|
|
|
+ artist artistSearch
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//NewMusicBrainz returns a new Search object
|
|
|
|
+func NewMusicBrainz() MB {
|
|
|
|
+
|
|
|
|
+ var mb MB
|
|
|
|
+ timeout := time.Duration(10 * time.Second)
|
|
|
|
+
|
|
|
|
+ mb.Client = http.Client{Timeout: timeout}
|
|
|
|
+ return mb
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//SearchForArtist return a list of artists using `query` as parameter if none found returns error
|
|
|
|
+func (m MB) SearchForArtist(query string) (*Search, error) {
|
|
|
|
+ var ret Search
|
|
|
|
+ uri := fmt.Sprintf("%s/%s=%s", apiURI, artistSearchEndpoint, query)
|
|
|
|
+
|
|
|
|
+ req, err := http.NewRequest(
|
|
|
|
+ "GET",
|
|
|
|
+ uri,
|
|
|
|
+ nil)
|
|
|
|
+
|
|
|
|
+ req.Header.Set(
|
|
|
|
+ "User-Agent",
|
|
|
|
+ "Go Application Development: Create spotify playlist based on artist")
|
|
|
|
+ req.Header.Set("Content-Type", "application/json")
|
|
|
|
+ req.Header.Set("Accept", "application/json")
|
|
|
|
+
|
|
|
|
+ if err != nil {
|
|
|
|
+ fmt.Printf("error creating request\n%v", err)
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ resp, err := m.Client.Do(req)
|
|
|
|
+ if err != nil {
|
|
|
|
+ fmt.Printf("error opening request\n%v", err)
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ defer resp.Body.Close()
|
|
|
|
+ if err != nil {
|
|
|
|
+ fmt.Printf("could not read body\n%v", err)
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = json.NewDecoder(resp.Body).Decode(&ret.artist)
|
|
|
|
+ if err != nil {
|
|
|
|
+ fmt.Printf("could not decode body\n%v", err)
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ret.artist.Count == 0 {
|
|
|
|
+ return nil, fmt.Errorf("cound not found artist: %s", query)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return &ret, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//GetArtistName get the artist with the highest score on the search
|
|
|
|
+func (s Search) GetArtistName() (string, error) {
|
|
|
|
+
|
|
|
|
+ if len(s.artist.Artists) == 0 {
|
|
|
|
+ return "", fmt.Errorf("could not find any artist")
|
|
|
|
+ }
|
|
|
|
+ return s.artist.Artists[0].Name, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//GetArtistTags returns the tags in order of relevance of the current artist
|
|
|
|
+func (s Search) GetArtistTags() []Tags {
|
|
|
|
+ var t []Tags
|
|
|
|
+ for _, elem := range s.artist.Artists[0].Tags {
|
|
|
|
+ if int(elem.Count) > 0 {
|
|
|
|
+ t = append(t, elem)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ sort.Sort(ByInverseCount(t))
|
|
|
|
+ return t
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type artistSearch struct {
|
|
|
|
+ Created time.Time `json:"created"`
|
|
|
|
+ Count int `json:"count"`
|
|
|
|
+ Offset int `json:"offset"`
|
|
|
|
+ Artists []artists `json:"artists"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type area struct {
|
|
|
|
+ ID string `json:"id"`
|
|
|
|
+ Type string `json:"type"`
|
|
|
|
+ TypeID string `json:"type-id"`
|
|
|
|
+ Name string `json:"name"`
|
|
|
|
+ SortName string `json:"sort-name"`
|
|
|
|
+ LifeSpan struct {
|
|
|
|
+ Ended interface{} `json:"ended"`
|
|
|
|
+ } `json:"life-span"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type alias struct {
|
|
|
|
+ SortName string `json:"sort-name"`
|
|
|
|
+ TypeID string `json:"type-id,omitempty"`
|
|
|
|
+ Name string `json:"name"`
|
|
|
|
+ Locale interface{} `json:"locale"`
|
|
|
|
+ Type string `json:"type"`
|
|
|
|
+ Primary interface{} `json:"primary"`
|
|
|
|
+ BeginDate interface{} `json:"begin-date"`
|
|
|
|
+ EndDate interface{} `json:"end-date"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type artists struct {
|
|
|
|
+ ID string `json:"id"`
|
|
|
|
+ Type string `json:"type,omitempty"`
|
|
|
|
+ TypeID string `json:"type-id,omitempty"`
|
|
|
|
+ Score int `json:"score"`
|
|
|
|
+ Name string `json:"name"`
|
|
|
|
+ SortName string `json:"sort-name"`
|
|
|
|
+ Country string `json:"country,omitempty"`
|
|
|
|
+ Area area `json:"area,omitempty"`
|
|
|
|
+ BeginArea struct {
|
|
|
|
+ ID string `json:"id"`
|
|
|
|
+ Type string `json:"type"`
|
|
|
|
+ TypeID string `json:"type-id"`
|
|
|
|
+ Name string `json:"name"`
|
|
|
|
+ SortName string `json:"sort-name"`
|
|
|
|
+ LifeSpan struct {
|
|
|
|
+ Ended interface{} `json:"ended"`
|
|
|
|
+ } `json:"life-span"`
|
|
|
|
+ } `json:"begin-area,omitempty"`
|
|
|
|
+ Isnis []string `json:"isnis,omitempty"`
|
|
|
|
+ LifeSpan struct {
|
|
|
|
+ Begin string `json:"begin"`
|
|
|
|
+ Ended interface{} `json:"ended"`
|
|
|
|
+ } `json:"life-span,omitempty"`
|
|
|
|
+ Aliases []alias `json:"aliases,omitempty"`
|
|
|
|
+ Tags []Tags `json:"tags,omitempty"`
|
|
|
|
+ Disambiguation string `json:"disambiguation,omitempty"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//Tags represent Artist tags (eg. Rock, Punk, Late 2000s)
|
|
|
|
+type Tags struct {
|
|
|
|
+ Count int `json:"count"`
|
|
|
|
+ Name string `json:"name"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//ByInverseCount return the tags in relevance order
|
|
|
|
+type ByInverseCount []Tags
|
|
|
|
+
|
|
|
|
+func (t ByInverseCount) Len() int { return len(t) }
|
|
|
|
+func (t ByInverseCount) Less(i, j int) bool { return t[i].Count >= t[j].Count }
|
|
|
|
+func (t ByInverseCount) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|