mirror of
https://github.com/zhaarey/apple-music-downloader.git
synced 2025-10-23 15:11:05 +00:00
test: 重构
This commit is contained in:
172
utils/ampapi/album.go
Normal file
172
utils/ampapi/album.go
Normal 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
1
utils/ampapi/artist.go
Normal file
@@ -0,0 +1 @@
|
||||
package ampapi
|
||||
145
utils/ampapi/musicvideo.go
Normal file
145
utils/ampapi/musicvideo.go
Normal 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
170
utils/ampapi/playlist.go
Normal 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
153
utils/ampapi/song.go
Normal 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
49
utils/ampapi/token.go
Normal 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
103
utils/ampapi/track.go
Normal 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"`
|
||||
}
|
||||
@@ -46,380 +46,7 @@ type Counter struct {
|
||||
Total int
|
||||
}
|
||||
|
||||
type ApiResult struct {
|
||||
Data []SongData `json:"data"`
|
||||
}
|
||||
|
||||
type SongAttributes struct {
|
||||
ArtistName string `json:"artistName"`
|
||||
DiscNumber int `json:"discNumber"`
|
||||
GenreNames []string `json:"genreNames"`
|
||||
ExtendedAssetUrls struct {
|
||||
EnhancedHls string `json:"enhancedHls"`
|
||||
} `json:"extendedAssetUrls"`
|
||||
IsMasteredForItunes bool `json:"isMasteredForItunes"`
|
||||
IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"`
|
||||
ContentRating string `json:"contentRating"`
|
||||
ReleaseDate string `json:"releaseDate"`
|
||||
Name string `json:"name"`
|
||||
Isrc string `json:"isrc"`
|
||||
AlbumName string `json:"albumName"`
|
||||
TrackNumber int `json:"trackNumber"`
|
||||
ComposerName string `json:"composerName"`
|
||||
}
|
||||
|
||||
type AlbumAttributes struct {
|
||||
ArtistName string `json:"artistName"`
|
||||
IsSingle bool `json:"isSingle"`
|
||||
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"`
|
||||
Copyright string `json:"copyright"`
|
||||
IsCompilation bool `json:"isCompilation"`
|
||||
}
|
||||
|
||||
type SongData struct {
|
||||
ID string `json:"id"`
|
||||
Attributes SongAttributes `json:"attributes"`
|
||||
Relationships struct {
|
||||
Albums struct {
|
||||
Data []struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Href string `json:"href"`
|
||||
Attributes AlbumAttributes `json:"attributes"`
|
||||
} `json:"data"`
|
||||
} `json:"albums"`
|
||||
Artists struct {
|
||||
Href string `json:"href"`
|
||||
Data []struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Href string `json:"href"`
|
||||
} `json:"data"`
|
||||
} `json:"artists"`
|
||||
} `json:"relationships"`
|
||||
}
|
||||
|
||||
type SongResult struct {
|
||||
Artwork struct {
|
||||
Width int `json:"width"`
|
||||
URL string `json:"url"`
|
||||
Height int `json:"height"`
|
||||
TextColor3 string `json:"textColor3"`
|
||||
TextColor2 string `json:"textColor2"`
|
||||
TextColor4 string `json:"textColor4"`
|
||||
HasAlpha bool `json:"hasAlpha"`
|
||||
TextColor1 string `json:"textColor1"`
|
||||
BgColor string `json:"bgColor"`
|
||||
HasP3 bool `json:"hasP3"`
|
||||
SupportsLayeredImage bool `json:"supportsLayeredImage"`
|
||||
} `json:"artwork"`
|
||||
ArtistName string `json:"artistName"`
|
||||
CollectionID string `json:"collectionId"`
|
||||
DiscNumber int `json:"discNumber"`
|
||||
GenreNames []string `json:"genreNames"`
|
||||
ID string `json:"id"`
|
||||
DurationInMillis int `json:"durationInMillis"`
|
||||
ReleaseDate string `json:"releaseDate"`
|
||||
ContentRatingsBySystem struct {
|
||||
} `json:"contentRatingsBySystem"`
|
||||
Name string `json:"name"`
|
||||
Composer struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
} `json:"composer"`
|
||||
EditorialArtwork struct {
|
||||
} `json:"editorialArtwork"`
|
||||
CollectionName string `json:"collectionName"`
|
||||
AssetUrls struct {
|
||||
Plus string `json:"plus"`
|
||||
Lightweight string `json:"lightweight"`
|
||||
SuperLightweight string `json:"superLightweight"`
|
||||
LightweightPlus string `json:"lightweightPlus"`
|
||||
EnhancedHls string `json:"enhancedHls"`
|
||||
} `json:"assetUrls"`
|
||||
AudioTraits []string `json:"audioTraits"`
|
||||
Kind string `json:"kind"`
|
||||
Copyright string `json:"copyright"`
|
||||
ArtistID string `json:"artistId"`
|
||||
Genres []struct {
|
||||
GenreID string `json:"genreId"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
MediaType string `json:"mediaType"`
|
||||
} `json:"genres"`
|
||||
TrackNumber int `json:"trackNumber"`
|
||||
AudioLocale string `json:"audioLocale"`
|
||||
Offers []struct {
|
||||
ActionText struct {
|
||||
Short string `json:"short"`
|
||||
Medium string `json:"medium"`
|
||||
Long string `json:"long"`
|
||||
Downloaded string `json:"downloaded"`
|
||||
Downloading string `json:"downloading"`
|
||||
} `json:"actionText"`
|
||||
Type string `json:"type"`
|
||||
PriceFormatted string `json:"priceFormatted"`
|
||||
Price float64 `json:"price"`
|
||||
BuyParams string `json:"buyParams"`
|
||||
Variant string `json:"variant,omitempty"`
|
||||
Assets []struct {
|
||||
Flavor string `json:"flavor"`
|
||||
Preview struct {
|
||||
Duration int `json:"duration"`
|
||||
URL string `json:"url"`
|
||||
} `json:"preview"`
|
||||
Size int `json:"size"`
|
||||
Duration int `json:"duration"`
|
||||
} `json:"assets"`
|
||||
} `json:"offers"`
|
||||
}
|
||||
|
||||
type TrackData 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"`
|
||||
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 []AlbumData `json:"data"`
|
||||
}
|
||||
} `json:"relationships"`
|
||||
}
|
||||
|
||||
type AlbumData 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"`
|
||||
}
|
||||
}
|
||||
|
||||
type AutoGenerated struct {
|
||||
Data []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 struct {
|
||||
Href string `json:"href"`
|
||||
Next string `json:"next"`
|
||||
Data []TrackData `json:"data"`
|
||||
} `json:"tracks"`
|
||||
} `json:"relationships"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type AutoGeneratedTrack struct {
|
||||
Href string `json:"href"`
|
||||
Next string `json:"next"`
|
||||
Data []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"`
|
||||
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 []AlbumData `json:"data"`
|
||||
}
|
||||
} `json:"relationships"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// 艺术家页面
|
||||
type AutoGeneratedArtist struct {
|
||||
Next string `json:"next"`
|
||||
Data []struct {
|
||||
@@ -465,59 +92,3 @@ type AutoGeneratedArtist struct {
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type AutoGeneratedMusicVideo struct {
|
||||
Data []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"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type SongLyrics struct {
|
||||
Data []struct {
|
||||
Id string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Attributes struct {
|
||||
Ttml string `json:"ttml"`
|
||||
PlayParams struct {
|
||||
Id string `json:"id"`
|
||||
Kind string `json:"kind"`
|
||||
CatalogId string `json:"catalogId"`
|
||||
DisplayType int `json:"displayType"`
|
||||
} `json:"playParams"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
193
utils/task/album.go
Normal file
193
utils/task/album.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"main/utils/ampapi"
|
||||
)
|
||||
|
||||
type Album struct {
|
||||
Storefront string
|
||||
ID string
|
||||
|
||||
SaveDir string
|
||||
SaveName string
|
||||
Codec string
|
||||
CoverPath string
|
||||
|
||||
Language string
|
||||
Resp ampapi.AlbumResp
|
||||
Name string
|
||||
Tracks []Track
|
||||
}
|
||||
|
||||
func NewAlbum(st string, id string) *Album {
|
||||
a := new(Album)
|
||||
a.Storefront = st
|
||||
a.ID = id
|
||||
|
||||
//fmt.Println("Album created")
|
||||
return a
|
||||
|
||||
}
|
||||
|
||||
func (a *Album) GetResp(token, l string) error {
|
||||
var err error
|
||||
a.Language = l
|
||||
resp, err := ampapi.GetAlbumResp(a.Storefront, a.ID, a.Language, token)
|
||||
if err != nil {
|
||||
return errors.New("error getting album response")
|
||||
}
|
||||
a.Resp = *resp
|
||||
//简化高频调用名称
|
||||
a.Name = a.Resp.Data[0].Attributes.Name
|
||||
//fmt.Println("Getting album response")
|
||||
//从resp中的Tracks数据中提取trackData信息到新的Track结构体中
|
||||
for i, trackData := range a.Resp.Data[0].Relationships.Tracks.Data {
|
||||
len := len(a.Resp.Data[0].Relationships.Tracks.Data)
|
||||
a.Tracks = append(a.Tracks, Track{
|
||||
ID: trackData.ID,
|
||||
Type: trackData.Type,
|
||||
Name: trackData.Attributes.Name,
|
||||
Language: a.Language,
|
||||
Storefront: a.Storefront,
|
||||
|
||||
//SaveDir: filepath.Join(a.SaveDir, a.SaveName),
|
||||
//Codec: a.Codec,
|
||||
TaskNum: i + 1,
|
||||
TaskTotal: len,
|
||||
M3u8: trackData.Attributes.ExtendedAssetUrls.EnhancedHls,
|
||||
WebM3u8: trackData.Attributes.ExtendedAssetUrls.EnhancedHls,
|
||||
//CoverPath: a.CoverPath,
|
||||
|
||||
Resp: trackData,
|
||||
PreType: "albums",
|
||||
DiscTotal: a.Resp.Data[0].Relationships.Tracks.Data[len-1].Attributes.DiscNumber,
|
||||
PreID: a.ID,
|
||||
AlbumData: a.Resp.Data[0],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Album) GetArtwork() string {
|
||||
return a.Resp.Data[0].Attributes.Artwork.URL
|
||||
}
|
||||
|
||||
func (a *Album) ShowSelect() []int {
|
||||
meta := a.Resp
|
||||
trackTotal := len(meta.Data[0].Relationships.Tracks.Data)
|
||||
arr := make([]int, trackTotal)
|
||||
for i := 0; i < trackTotal; i++ {
|
||||
arr[i] = i + 1
|
||||
}
|
||||
selected := []int{}
|
||||
var data [][]string
|
||||
for trackNum, track := range meta.Data[0].Relationships.Tracks.Data {
|
||||
trackNum++
|
||||
trackName := fmt.Sprintf("%02d. %s", track.Attributes.TrackNumber, track.Attributes.Name)
|
||||
data = append(data, []string{fmt.Sprint(trackNum),
|
||||
trackName,
|
||||
track.Attributes.ContentRating,
|
||||
track.Type})
|
||||
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"", "Track Name", "Rating", "Type"})
|
||||
//table.SetFooter([]string{"", "", "Footer", "Footer4"})
|
||||
table.SetRowLine(false)
|
||||
//table.SetAutoMergeCells(true)
|
||||
table.SetCaption(true, fmt.Sprintf("Storefront: %s, %d tracks missing", strings.ToUpper(a.Storefront), meta.Data[0].Attributes.TrackCount-trackTotal))
|
||||
table.SetHeaderColor(tablewriter.Colors{},
|
||||
tablewriter.Colors{tablewriter.FgRedColor, tablewriter.Bold},
|
||||
tablewriter.Colors{tablewriter.FgBlackColor, tablewriter.Bold},
|
||||
tablewriter.Colors{tablewriter.FgBlackColor, tablewriter.Bold})
|
||||
|
||||
table.SetColumnColor(tablewriter.Colors{tablewriter.FgCyanColor},
|
||||
tablewriter.Colors{tablewriter.Bold, tablewriter.FgRedColor},
|
||||
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor},
|
||||
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})
|
||||
for _, row := range data {
|
||||
if row[2] == "explicit" {
|
||||
row[2] = "E"
|
||||
} else if row[2] == "clean" {
|
||||
row[2] = "C"
|
||||
} else {
|
||||
row[2] = "None"
|
||||
}
|
||||
if row[3] == "music-videos" {
|
||||
row[3] = "MV"
|
||||
} else if row[3] == "songs" {
|
||||
row[3] = "SONG"
|
||||
}
|
||||
table.Append(row)
|
||||
}
|
||||
//table.AppendBulk(data)
|
||||
table.Render()
|
||||
fmt.Println("Please select from the track options above (multiple options separated by commas, ranges supported, or type 'all' to select all)")
|
||||
cyanColor := color.New(color.FgCyan)
|
||||
cyanColor.Print("select: ")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "all" {
|
||||
fmt.Println("You have selected all options:")
|
||||
selected = arr
|
||||
} else {
|
||||
selectedOptions := [][]string{}
|
||||
parts := strings.Split(input, ",")
|
||||
for _, part := range parts {
|
||||
if strings.Contains(part, "-") { // Range setting
|
||||
rangeParts := strings.Split(part, "-")
|
||||
selectedOptions = append(selectedOptions, rangeParts)
|
||||
} else { // Single option
|
||||
selectedOptions = append(selectedOptions, []string{part})
|
||||
}
|
||||
}
|
||||
//
|
||||
for _, opt := range selectedOptions {
|
||||
if len(opt) == 1 { // Single option
|
||||
num, err := strconv.Atoi(opt[0])
|
||||
if err != nil {
|
||||
fmt.Println("Invalid option:", opt[0])
|
||||
continue
|
||||
}
|
||||
if num > 0 && num <= len(arr) {
|
||||
selected = append(selected, num)
|
||||
//args = append(args, urls[num-1])
|
||||
} else {
|
||||
fmt.Println("Option out of range:", opt[0])
|
||||
}
|
||||
} else if len(opt) == 2 { // Range
|
||||
start, err1 := strconv.Atoi(opt[0])
|
||||
end, err2 := strconv.Atoi(opt[1])
|
||||
if err1 != nil || err2 != nil {
|
||||
fmt.Println("Invalid range:", opt)
|
||||
continue
|
||||
}
|
||||
if start < 1 || end > len(arr) || start > end {
|
||||
fmt.Println("Range out of range:", opt)
|
||||
continue
|
||||
}
|
||||
for i := start; i <= end; i++ {
|
||||
//fmt.Println(options[i-1])
|
||||
selected = append(selected, i)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Invalid option:", opt)
|
||||
}
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
||||
195
utils/task/playlist.go
Normal file
195
utils/task/playlist.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"main/utils/ampapi"
|
||||
)
|
||||
|
||||
type Playlist struct {
|
||||
Storefront string
|
||||
ID string
|
||||
|
||||
SaveDir string
|
||||
SaveName string
|
||||
Codec string
|
||||
CoverPath string
|
||||
|
||||
Language string
|
||||
Resp ampapi.PlaylistResp
|
||||
Name string
|
||||
Tracks []Track
|
||||
}
|
||||
|
||||
func NewPlaylist(st string, id string) *Playlist {
|
||||
a := new(Playlist)
|
||||
a.Storefront = st
|
||||
a.ID = id
|
||||
|
||||
//fmt.Println("Album created")
|
||||
return a
|
||||
|
||||
}
|
||||
|
||||
func (a *Playlist) GetResp(token, l string) error {
|
||||
var err error
|
||||
a.Language = l
|
||||
resp, err := ampapi.GetPlaylistResp(a.Storefront, a.ID, a.Language, token)
|
||||
if err != nil {
|
||||
return errors.New("error getting album response")
|
||||
}
|
||||
a.Resp = *resp
|
||||
|
||||
a.Resp.Data[0].Attributes.ArtistName = "Apple Music"
|
||||
//简化高频调用名称
|
||||
a.Name = a.Resp.Data[0].Attributes.Name
|
||||
//fmt.Println("Getting album response")
|
||||
//从resp中的Tracks数据中提取trackData信息到新的Track结构体中
|
||||
for i, trackData := range a.Resp.Data[0].Relationships.Tracks.Data {
|
||||
len := len(a.Resp.Data[0].Relationships.Tracks.Data)
|
||||
a.Tracks = append(a.Tracks, Track{
|
||||
ID: trackData.ID,
|
||||
Type: trackData.Type,
|
||||
Name: trackData.Attributes.Name,
|
||||
Language: a.Language,
|
||||
Storefront: a.Storefront,
|
||||
|
||||
//SaveDir: filepath.Join(a.SaveDir, a.SaveName),
|
||||
//Codec: a.Codec,
|
||||
TaskNum: i + 1,
|
||||
TaskTotal: len,
|
||||
M3u8: trackData.Attributes.ExtendedAssetUrls.EnhancedHls,
|
||||
WebM3u8: trackData.Attributes.ExtendedAssetUrls.EnhancedHls,
|
||||
//CoverPath: a.CoverPath,
|
||||
|
||||
Resp: trackData,
|
||||
PreType: "playlists",
|
||||
//DiscTotal: a.Resp.Data[0].Relationships.Tracks.Data[len-1].Attributes.DiscNumber, 在它处获取
|
||||
PreID: a.ID,
|
||||
PlaylistData: a.Resp.Data[0],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Playlist) GetArtwork() string {
|
||||
return a.Resp.Data[0].Attributes.Artwork.URL
|
||||
}
|
||||
|
||||
func (a *Playlist) ShowSelect() []int {
|
||||
meta := a.Resp
|
||||
trackTotal := len(meta.Data[0].Relationships.Tracks.Data)
|
||||
arr := make([]int, trackTotal)
|
||||
for i := 0; i < trackTotal; i++ {
|
||||
arr[i] = i + 1
|
||||
}
|
||||
selected := []int{}
|
||||
var data [][]string
|
||||
for trackNum, track := range meta.Data[0].Relationships.Tracks.Data {
|
||||
trackNum++
|
||||
trackName := fmt.Sprintf("%s - %s", track.Attributes.Name, track.Attributes.ArtistName)
|
||||
data = append(data, []string{fmt.Sprint(trackNum),
|
||||
trackName,
|
||||
track.Attributes.ContentRating,
|
||||
track.Type})
|
||||
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"", "Track Name", "Rating", "Type"})
|
||||
//table.SetFooter([]string{"", "", "Footer", "Footer4"})
|
||||
table.SetRowLine(false)
|
||||
//table.SetAutoMergeCells(true)
|
||||
table.SetCaption(true, fmt.Sprintf("Playlists: %d tracks", trackTotal))
|
||||
table.SetHeaderColor(tablewriter.Colors{},
|
||||
tablewriter.Colors{tablewriter.FgRedColor, tablewriter.Bold},
|
||||
tablewriter.Colors{tablewriter.FgBlackColor, tablewriter.Bold},
|
||||
tablewriter.Colors{tablewriter.FgBlackColor, tablewriter.Bold})
|
||||
|
||||
table.SetColumnColor(tablewriter.Colors{tablewriter.FgCyanColor},
|
||||
tablewriter.Colors{tablewriter.Bold, tablewriter.FgRedColor},
|
||||
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor},
|
||||
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})
|
||||
for _, row := range data {
|
||||
if row[2] == "explicit" {
|
||||
row[2] = "E"
|
||||
} else if row[2] == "clean" {
|
||||
row[2] = "C"
|
||||
} else {
|
||||
row[2] = "None"
|
||||
}
|
||||
if row[3] == "music-videos" {
|
||||
row[3] = "MV"
|
||||
} else if row[3] == "songs" {
|
||||
row[3] = "SONG"
|
||||
}
|
||||
table.Append(row)
|
||||
}
|
||||
//table.AppendBulk(data)
|
||||
table.Render()
|
||||
fmt.Println("Please select from the track options above (multiple options separated by commas, ranges supported, or type 'all' to select all)")
|
||||
cyanColor := color.New(color.FgCyan)
|
||||
cyanColor.Print("select: ")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "all" {
|
||||
fmt.Println("You have selected all options:")
|
||||
selected = arr
|
||||
} else {
|
||||
selectedOptions := [][]string{}
|
||||
parts := strings.Split(input, ",")
|
||||
for _, part := range parts {
|
||||
if strings.Contains(part, "-") { // Range setting
|
||||
rangeParts := strings.Split(part, "-")
|
||||
selectedOptions = append(selectedOptions, rangeParts)
|
||||
} else { // Single option
|
||||
selectedOptions = append(selectedOptions, []string{part})
|
||||
}
|
||||
}
|
||||
//
|
||||
for _, opt := range selectedOptions {
|
||||
if len(opt) == 1 { // Single option
|
||||
num, err := strconv.Atoi(opt[0])
|
||||
if err != nil {
|
||||
fmt.Println("Invalid option:", opt[0])
|
||||
continue
|
||||
}
|
||||
if num > 0 && num <= len(arr) {
|
||||
selected = append(selected, num)
|
||||
//args = append(args, urls[num-1])
|
||||
} else {
|
||||
fmt.Println("Option out of range:", opt[0])
|
||||
}
|
||||
} else if len(opt) == 2 { // Range
|
||||
start, err1 := strconv.Atoi(opt[0])
|
||||
end, err2 := strconv.Atoi(opt[1])
|
||||
if err1 != nil || err2 != nil {
|
||||
fmt.Println("Invalid range:", opt)
|
||||
continue
|
||||
}
|
||||
if start < 1 || end > len(arr) || start > end {
|
||||
fmt.Println("Range out of range:", opt)
|
||||
continue
|
||||
}
|
||||
for i := start; i <= end; i++ {
|
||||
//fmt.Println(options[i-1])
|
||||
selected = append(selected, i)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Invalid option:", opt)
|
||||
}
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
||||
44
utils/task/track.go
Normal file
44
utils/task/track.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"main/utils/ampapi"
|
||||
)
|
||||
|
||||
type Track struct {
|
||||
ID string
|
||||
Type string
|
||||
Name string
|
||||
Storefront string
|
||||
Language string
|
||||
|
||||
SaveDir string
|
||||
SaveName string
|
||||
SavePath string
|
||||
Codec string
|
||||
TaskNum int
|
||||
TaskTotal int
|
||||
M3u8 string
|
||||
WebM3u8 string
|
||||
DeviceM3u8 string
|
||||
Quality string
|
||||
CoverPath string
|
||||
|
||||
Resp ampapi.TrackRespData
|
||||
PreType string // 上级类型 专辑或者歌单
|
||||
PreID string // 上级ID
|
||||
DiscTotal int
|
||||
AlbumData ampapi.AlbumRespData
|
||||
PlaylistData ampapi.PlaylistRespData
|
||||
}
|
||||
|
||||
func (t *Track) GetAlbumData(token string) error {
|
||||
var err error
|
||||
resp, err := ampapi.GetAlbumRespByHref(t.Resp.Href, t.Language, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.AlbumData = resp.Data[0]
|
||||
//len := len(resp.Data[0].Relationships.Tracks.Data)
|
||||
//t.DiscTotal = resp.Data[0].Relationships.Tracks.Data[len-1].Attributes.DiscNumber
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user