test: 重构

This commit is contained in:
itouakirai
2025-03-02 23:44:05 +08:00
parent 2db22524b6
commit b2bcdfde88
12 changed files with 1823 additions and 1060 deletions

172
utils/ampapi/album.go Normal file
View File

@@ -0,0 +1,172 @@
package ampapi
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
)
func GetAlbumResp(storefront string, id string, language string, token string) (*AlbumResp, error) {
var err error
if token == "" {
token, err = GetToken()
if err != nil {
return nil, err
}
}
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/albums/%s", storefront, id), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Origin", "https://music.apple.com")
query := url.Values{}
query.Set("omit[resource]", "autos")
query.Set("include", "tracks,artists,record-labels")
query.Set("include[songs]", "artists")
//query.Set("fields[artists]", "name,artwork")
//query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url")
//query.Set("fields[record-labels]", "name")
query.Set("extend", "editorialVideo,extendedAssetUrls")
query.Set("l", language)
req.URL.RawQuery = query.Encode()
do, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer do.Body.Close()
if do.StatusCode != http.StatusOK {
return nil, errors.New(do.Status)
}
obj := new(AlbumResp)
err = json.NewDecoder(do.Body).Decode(&obj)
if err != nil {
return nil, err
}
return obj, nil
}
func GetAlbumRespByHref(href string, language string, token string) (*AlbumResp, error) {
var err error
if token == "" {
token, err = GetToken()
if err != nil {
return nil, err
}
}
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com%s/albums", href), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Origin", "https://music.apple.com")
query := url.Values{}
query.Set("omit[resource]", "autos")
query.Set("include", "tracks,artists,record-labels")
query.Set("include[songs]", "artists")
//query.Set("fields[artists]", "name,artwork")
//query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url")
//query.Set("fields[record-labels]", "name")
query.Set("extend", "editorialVideo,extendedAssetUrls")
query.Set("l", language)
req.URL.RawQuery = query.Encode()
do, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer do.Body.Close()
if do.StatusCode != http.StatusOK {
return nil, errors.New(do.Status)
}
obj := new(AlbumResp)
err = json.NewDecoder(do.Body).Decode(&obj)
if err != nil {
return nil, err
}
return obj, nil
}
type AlbumResp struct {
Href string `json:"href"`
Next string `json:"next"`
Data []AlbumRespData `json:"data"`
}
type AlbumRespData struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
ArtistName string `json:"artistName"`
IsSingle bool `json:"isSingle"`
URL string `json:"url"`
IsComplete bool `json:"isComplete"`
GenreNames []string `json:"genreNames"`
TrackCount int `json:"trackCount"`
IsMasteredForItunes bool `json:"isMasteredForItunes"`
IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"`
ContentRating string `json:"contentRating"`
ReleaseDate string `json:"releaseDate"`
Name string `json:"name"`
RecordLabel string `json:"recordLabel"`
Upc string `json:"upc"`
AudioTraits []string `json:"audioTraits"`
Copyright string `json:"copyright"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
IsCompilation bool `json:"isCompilation"`
EditorialVideo struct {
MotionTall struct {
Video string `json:"video"`
} `json:"motionTallVideo3x4"`
MotionSquare struct {
Video string `json:"video"`
} `json:"motionSquareVideo1x1"`
MotionDetailTall struct {
Video string `json:"video"`
} `json:"motionDetailTall"`
MotionDetailSquare struct {
Video string `json:"video"`
} `json:"motionDetailSquare"`
} `json:"editorialVideo"`
} `json:"attributes"`
Relationships struct {
RecordLabels struct {
Href string `json:"href"`
Data []interface{} `json:"data"`
} `json:"record-labels"`
Artists struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Name string `json:"name"`
Artwork struct {
Url string `json:"url"`
} `json:"artwork"`
} `json:"attributes"`
} `json:"data"`
} `json:"artists"`
Tracks TrackResp `json:"tracks"`
} `json:"relationships"`
}

1
utils/ampapi/artist.go Normal file
View File

@@ -0,0 +1 @@
package ampapi

145
utils/ampapi/musicvideo.go Normal file
View File

@@ -0,0 +1,145 @@
package ampapi
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
)
func GetMusicVideoResp(storefront string, id string, language string, token string) (*MusicVideoResp, error) {
var err error
if token == "" {
token, err = GetToken()
if err != nil {
return nil, err
}
}
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/music-videos/%s", storefront, id), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Origin", "https://music.apple.com")
query := url.Values{}
//query.Set("omit[resource]", "autos")
query.Set("include", "albums,artists")
//query.Set("extend", "extendedAssetUrls")
//query.Set("include[songs]", "artists")
//query.Set("fields[artists]", "name,artwork")
//query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url")
//query.Set("fields[record-labels]", "name")
//query.Set("extend", "editorialVideo")
query.Set("l", language)
req.URL.RawQuery = query.Encode()
do, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer do.Body.Close()
if do.StatusCode != http.StatusOK {
return nil, errors.New(do.Status)
}
obj := new(MusicVideoResp)
err = json.NewDecoder(do.Body).Decode(&obj)
if err != nil {
return nil, err
}
return obj, nil
}
type MusicVideoResp struct {
Href string `json:"href"`
Next string `json:"next"`
Data []MusicVideoRespData `json:"data"`
}
type MusicVideoRespData struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Previews []struct {
URL string `json:"url"`
} `json:"previews"`
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
AlbumName string `json:"albumName"`
ArtistName string `json:"artistName"`
URL string `json:"url"`
GenreNames []string `json:"genreNames"`
DurationInMillis int `json:"durationInMillis"`
Isrc string `json:"isrc"`
TrackNumber int `json:"trackNumber"`
DiscNumber int `json:"discNumber"`
ContentRating string `json:"contentRating"`
ReleaseDate string `json:"releaseDate"`
Name string `json:"name"`
Has4K bool `json:"has4K"`
HasHDR bool `json:"hasHDR"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
} `json:"attributes"`
Relationships struct {
Artists struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Name string `json:"name"`
} `json:"attributes"`
} `json:"data"`
} `json:"artists"`
Albums struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
ArtistName string `json:"artistName"`
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
GenreNames []string `json:"genreNames"`
IsCompilation bool `json:"isCompilation"`
IsComplete bool `json:"isComplete"`
IsMasteredForItunes bool `json:"isMasteredForItunes"`
IsPrerelease bool `json:"isPrerelease"`
IsSingle bool `json:"isSingle"`
Name string `json:"name"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
ReleaseDate string `json:"releaseDate"`
TrackCount int `json:"trackCount"`
Upc string `json:"upc"`
URL string `json:"url"`
} `json:"attributes"`
} `json:"data"`
} `json:"albums"`
} `json:"relationships"`
}

170
utils/ampapi/playlist.go Normal file
View File

@@ -0,0 +1,170 @@
package ampapi
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
)
func GetPlaylistResp(storefront string, id string, language string, token string) (*PlaylistResp, error) {
var err error
if token == "" {
token, err = GetToken()
if err != nil {
return nil, err
}
}
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/playlists/%s", storefront, id), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Origin", "https://music.apple.com")
query := url.Values{}
query.Set("omit[resource]", "autos")
query.Set("include", "tracks,artists,record-labels")
query.Set("include[songs]", "artists")
//query.Set("fields[artists]", "name,artwork")
//query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url")
//query.Set("fields[record-labels]", "name")
query.Set("extend", "editorialVideo,extendedAssetUrls")
query.Set("l", language)
req.URL.RawQuery = query.Encode()
do, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer do.Body.Close()
if do.StatusCode != http.StatusOK {
return nil, errors.New(do.Status)
}
obj := new(PlaylistResp)
err = json.NewDecoder(do.Body).Decode(&obj)
if err != nil {
return nil, err
}
if len(obj.Data[0].Relationships.Tracks.Next) > 0 {
next := obj.Data[0].Relationships.Tracks.Next
for {
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/%s", next), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Origin", "https://music.apple.com")
query := url.Values{}
query.Set("omit[resource]", "autos")
query.Set("include", "tracks,artists,record-labels,albums")
query.Set("include[songs]", "artists")
//query.Set("fields[artists]", "name,artwork")
//query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url")
//query.Set("fields[record-labels]", "name")
query.Set("extend", "editorialVideo,extendedAssetUrls")
query.Set("l", language)
req.URL.RawQuery = query.Encode()
do, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer do.Body.Close()
if do.StatusCode != http.StatusOK {
return nil, errors.New(do.Status)
}
obj2 := new(TrackResp)
err = json.NewDecoder(do.Body).Decode(&obj2)
if err != nil {
return nil, err
}
obj.Data[0].Relationships.Tracks.Data = append(obj.Data[0].Relationships.Tracks.Data, obj2.Data...)
next = obj2.Next
if len(next) == 0 {
break
}
}
}
return obj, nil
}
type PlaylistResp struct {
Href string `json:"href"`
Next string `json:"next"`
Data []PlaylistRespData `json:"data"`
}
type PlaylistRespData struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
ArtistName string `json:"artistName"`
IsSingle bool `json:"isSingle"`
URL string `json:"url"`
IsComplete bool `json:"isComplete"`
GenreNames []string `json:"genreNames"`
TrackCount int `json:"trackCount"`
IsMasteredForItunes bool `json:"isMasteredForItunes"`
IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"`
ContentRating string `json:"contentRating"`
ReleaseDate string `json:"releaseDate"`
Name string `json:"name"`
RecordLabel string `json:"recordLabel"`
Upc string `json:"upc"`
AudioTraits []string `json:"audioTraits"`
Copyright string `json:"copyright"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
IsCompilation bool `json:"isCompilation"`
EditorialVideo struct {
MotionTall struct {
Video string `json:"video"`
} `json:"motionTallVideo3x4"`
MotionSquare struct {
Video string `json:"video"`
} `json:"motionSquareVideo1x1"`
MotionDetailTall struct {
Video string `json:"video"`
} `json:"motionDetailTall"`
MotionDetailSquare struct {
Video string `json:"video"`
} `json:"motionDetailSquare"`
} `json:"editorialVideo"`
} `json:"attributes"`
Relationships struct {
RecordLabels struct {
Href string `json:"href"`
Data []interface{} `json:"data"`
} `json:"record-labels"`
Artists struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Name string `json:"name"`
Artwork struct {
Url string `json:"url"`
} `json:"artwork"`
} `json:"attributes"`
} `json:"data"`
} `json:"artists"`
Tracks TrackResp `json:"tracks"`
} `json:"relationships"`
}

153
utils/ampapi/song.go Normal file
View File

@@ -0,0 +1,153 @@
package ampapi
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
)
func GetSongResp(storefront string, id string, language string, token string) (*SongResp, error) {
var err error
if token == "" {
token, err = GetToken()
if err != nil {
return nil, err
}
}
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s", storefront, id), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Origin", "https://music.apple.com")
query := url.Values{}
//query.Set("omit[resource]", "autos")
query.Set("include", "albums,artists")
query.Set("extend", "extendedAssetUrls")
//query.Set("include[songs]", "artists")
//query.Set("fields[artists]", "name,artwork")
//query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url")
//query.Set("fields[record-labels]", "name")
//query.Set("extend", "editorialVideo")
query.Set("l", language)
req.URL.RawQuery = query.Encode()
do, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer do.Body.Close()
if do.StatusCode != http.StatusOK {
return nil, errors.New(do.Status)
}
obj := new(SongResp)
err = json.NewDecoder(do.Body).Decode(&obj)
if err != nil {
return nil, err
}
return obj, nil
}
type SongResp struct {
Href string `json:"href"`
Next string `json:"next"`
Data []SongRespData `json:"data"`
}
type SongRespData struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Previews []struct {
URL string `json:"url"`
} `json:"previews"`
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
ArtistName string `json:"artistName"`
URL string `json:"url"`
DiscNumber int `json:"discNumber"`
GenreNames []string `json:"genreNames"`
HasTimeSyncedLyrics bool `json:"hasTimeSyncedLyrics"`
IsMasteredForItunes bool `json:"isMasteredForItunes"`
IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"`
ContentRating string `json:"contentRating"`
DurationInMillis int `json:"durationInMillis"`
ReleaseDate string `json:"releaseDate"`
Name string `json:"name"`
ExtendedAssetUrls struct {
EnhancedHls string `json:"enhancedHls"`
} `json:"extendedAssetUrls"`
Isrc string `json:"isrc"`
AudioTraits []string `json:"audioTraits"`
HasLyrics bool `json:"hasLyrics"`
AlbumName string `json:"albumName"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
TrackNumber int `json:"trackNumber"`
AudioLocale string `json:"audioLocale"`
ComposerName string `json:"composerName"`
} `json:"attributes"`
Relationships struct {
Artists struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Name string `json:"name"`
} `json:"attributes"`
} `json:"data"`
} `json:"artists"`
Albums struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
ArtistName string `json:"artistName"`
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
GenreNames []string `json:"genreNames"`
IsCompilation bool `json:"isCompilation"`
IsComplete bool `json:"isComplete"`
IsMasteredForItunes bool `json:"isMasteredForItunes"`
IsPrerelease bool `json:"isPrerelease"`
IsSingle bool `json:"isSingle"`
Name string `json:"name"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
ReleaseDate string `json:"releaseDate"`
TrackCount int `json:"trackCount"`
Upc string `json:"upc"`
URL string `json:"url"`
} `json:"attributes"`
} `json:"data"`
} `json:"albums"`
} `json:"relationships"`
}

49
utils/ampapi/token.go Normal file
View File

@@ -0,0 +1,49 @@
package ampapi
import (
"io"
"net/http"
"regexp"
)
func GetToken() (string, error) {
req, err := http.NewRequest("GET", "https://beta.music.apple.com", nil)
if err != nil {
return "", err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
regex := regexp.MustCompile(`/assets/index-legacy-[^/]+\.js`)
indexJsUri := regex.FindString(string(body))
req, err = http.NewRequest("GET", "https://beta.music.apple.com"+indexJsUri, nil)
if err != nil {
return "", err
}
resp, err = http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
return "", err
}
regex = regexp.MustCompile(`eyJh([^"]*)`)
token := regex.FindString(string(body))
return token, nil
}

103
utils/ampapi/track.go Normal file
View File

@@ -0,0 +1,103 @@
package ampapi
type TrackResp struct {
Href string `json:"href"`
Next string `json:"next"`
Data []TrackRespData `json:"data"`
}
// 类型为song 或者 music-video
type TrackRespData struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Previews []struct {
URL string `json:"url"`
} `json:"previews"`
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
ArtistName string `json:"artistName"`
URL string `json:"url"`
DiscNumber int `json:"discNumber"`
GenreNames []string `json:"genreNames"`
ExtendedAssetUrls struct {
EnhancedHls string `json:"enhancedHls"`
} `json:"extendedAssetUrls"`
HasTimeSyncedLyrics bool `json:"hasTimeSyncedLyrics"`
IsMasteredForItunes bool `json:"isMasteredForItunes"`
IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"`
ContentRating string `json:"contentRating"`
DurationInMillis int `json:"durationInMillis"`
ReleaseDate string `json:"releaseDate"`
Name string `json:"name"`
Isrc string `json:"isrc"`
AudioTraits []string `json:"audioTraits"`
HasLyrics bool `json:"hasLyrics"`
AlbumName string `json:"albumName"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
TrackNumber int `json:"trackNumber"`
AudioLocale string `json:"audioLocale"`
ComposerName string `json:"composerName"`
} `json:"attributes"`
Relationships struct {
Artists struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
Name string `json:"name"`
} `json:"attributes"`
} `json:"data"`
} `json:"artists"`
Albums struct {
Href string `json:"href"`
Data []struct {
ID string `json:"id"`
Type string `json:"type"`
Href string `json:"href"`
Attributes struct {
ArtistName string `json:"artistName"`
Artwork struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
BgColor string `json:"bgColor"`
TextColor1 string `json:"textColor1"`
TextColor2 string `json:"textColor2"`
TextColor3 string `json:"textColor3"`
TextColor4 string `json:"textColor4"`
} `json:"artwork"`
GenreNames []string `json:"genreNames"`
IsCompilation bool `json:"isCompilation"`
IsComplete bool `json:"isComplete"`
IsMasteredForItunes bool `json:"isMasteredForItunes"`
IsPrerelease bool `json:"isPrerelease"`
IsSingle bool `json:"isSingle"`
Name string `json:"name"`
PlayParams struct {
ID string `json:"id"`
Kind string `json:"kind"`
} `json:"playParams"`
ReleaseDate string `json:"releaseDate"`
TrackCount int `json:"trackCount"`
Upc string `json:"upc"`
URL string `json:"url"`
} `json:"attributes"`
} `json:"data"`
} `json:"albums"`
} `json:"relationships"`
}