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
|
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 {
|
type AutoGeneratedArtist struct {
|
||||||
Next string `json:"next"`
|
Next string `json:"next"`
|
||||||
Data []struct {
|
Data []struct {
|
||||||
@@ -465,59 +92,3 @@ type AutoGeneratedArtist struct {
|
|||||||
} `json:"attributes"`
|
} `json:"attributes"`
|
||||||
} `json:"data"`
|
} `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