test: 重构

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

1041
main.go

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -1,523 +1,94 @@
package structs package structs
type ConfigSet struct { type ConfigSet struct {
MediaUserToken string `yaml:"media-user-token"` MediaUserToken string `yaml:"media-user-token"`
AuthorizationToken string `yaml:"authorization-token"` AuthorizationToken string `yaml:"authorization-token"`
Language string `yaml:"language"` Language string `yaml:"language"`
SaveLrcFile bool `yaml:"save-lrc-file"` SaveLrcFile bool `yaml:"save-lrc-file"`
LrcType string `yaml:"lrc-type"` LrcType string `yaml:"lrc-type"`
LrcFormat string `yaml:"lrc-format"` LrcFormat string `yaml:"lrc-format"`
SaveAnimatedArtwork bool `yaml:"save-animated-artwork"` SaveAnimatedArtwork bool `yaml:"save-animated-artwork"`
EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"`
EmbedLrc bool `yaml:"embed-lrc"` EmbedLrc bool `yaml:"embed-lrc"`
EmbedCover bool `yaml:"embed-cover"` EmbedCover bool `yaml:"embed-cover"`
SaveArtistCover bool `yaml:"save-artist-cover"` SaveArtistCover bool `yaml:"save-artist-cover"`
CoverSize string `yaml:"cover-size"` CoverSize string `yaml:"cover-size"`
CoverFormat string `yaml:"cover-format"` CoverFormat string `yaml:"cover-format"`
AlacSaveFolder string `yaml:"alac-save-folder"` AlacSaveFolder string `yaml:"alac-save-folder"`
AtmosSaveFolder string `yaml:"atmos-save-folder"` AtmosSaveFolder string `yaml:"atmos-save-folder"`
AlbumFolderFormat string `yaml:"album-folder-format"` AlbumFolderFormat string `yaml:"album-folder-format"`
PlaylistFolderFormat string `yaml:"playlist-folder-format"` PlaylistFolderFormat string `yaml:"playlist-folder-format"`
ArtistFolderFormat string `yaml:"artist-folder-format"` ArtistFolderFormat string `yaml:"artist-folder-format"`
SongFileFormat string `yaml:"song-file-format"` SongFileFormat string `yaml:"song-file-format"`
ExplicitChoice string `yaml:"explicit-choice"` ExplicitChoice string `yaml:"explicit-choice"`
CleanChoice string `yaml:"clean-choice"` CleanChoice string `yaml:"clean-choice"`
AppleMasterChoice string `yaml:"apple-master-choice"` AppleMasterChoice string `yaml:"apple-master-choice"`
MaxMemoryLimit int `yaml:"max-memory-limit"` MaxMemoryLimit int `yaml:"max-memory-limit"`
DecryptM3u8Port string `yaml:"decrypt-m3u8-port"` DecryptM3u8Port string `yaml:"decrypt-m3u8-port"`
GetM3u8Port string `yaml:"get-m3u8-port"` GetM3u8Port string `yaml:"get-m3u8-port"`
GetM3u8Mode string `yaml:"get-m3u8-mode"` GetM3u8Mode string `yaml:"get-m3u8-mode"`
GetM3u8FromDevice bool `yaml:"get-m3u8-from-device"` GetM3u8FromDevice bool `yaml:"get-m3u8-from-device"`
AacType string `yaml:"aac-type"` AacType string `yaml:"aac-type"`
AlacMax int `yaml:"alac-max"` AlacMax int `yaml:"alac-max"`
AtmosMax int `yaml:"atmos-max"` AtmosMax int `yaml:"atmos-max"`
LimitMax int `yaml:"limit-max"` LimitMax int `yaml:"limit-max"`
UseSongInfoForPlaylist bool `yaml:"use-songinfo-for-playlist"` UseSongInfoForPlaylist bool `yaml:"use-songinfo-for-playlist"`
DlAlbumcoverForPlaylist bool `yaml:"dl-albumcover-for-playlist"` DlAlbumcoverForPlaylist bool `yaml:"dl-albumcover-for-playlist"`
MVAudioType string `yaml:"mv-audio-type"` MVAudioType string `yaml:"mv-audio-type"`
MVMax int `yaml:"mv-max"` MVMax int `yaml:"mv-max"`
} }
type Counter struct { type Counter struct {
Unavailable int Unavailable int
NotSong int NotSong int
Error int Error int
Success int Success int
Total int Total int
} }
type ApiResult struct { // 艺术家页面
Data []SongData `json:"data"` type AutoGeneratedArtist struct {
} Next string `json:"next"`
Data []struct {
type SongAttributes struct { ID string `json:"id"`
ArtistName string `json:"artistName"` Type string `json:"type"`
DiscNumber int `json:"discNumber"` Href string `json:"href"`
GenreNames []string `json:"genreNames"` Attributes struct {
ExtendedAssetUrls struct { Previews []struct {
EnhancedHls string `json:"enhancedHls"` URL string `json:"url"`
} `json:"extendedAssetUrls"` } `json:"previews"`
IsMasteredForItunes bool `json:"isMasteredForItunes"` Artwork struct {
IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"` Width int `json:"width"`
ContentRating string `json:"contentRating"` Height int `json:"height"`
ReleaseDate string `json:"releaseDate"` URL string `json:"url"`
Name string `json:"name"` BgColor string `json:"bgColor"`
Isrc string `json:"isrc"` TextColor1 string `json:"textColor1"`
AlbumName string `json:"albumName"` TextColor2 string `json:"textColor2"`
TrackNumber int `json:"trackNumber"` TextColor3 string `json:"textColor3"`
ComposerName string `json:"composerName"` TextColor4 string `json:"textColor4"`
} } `json:"artwork"`
ArtistName string `json:"artistName"`
type AlbumAttributes struct { URL string `json:"url"`
ArtistName string `json:"artistName"` DiscNumber int `json:"discNumber"`
IsSingle bool `json:"isSingle"` GenreNames []string `json:"genreNames"`
IsComplete bool `json:"isComplete"` HasTimeSyncedLyrics bool `json:"hasTimeSyncedLyrics"`
GenreNames []string `json:"genreNames"` IsMasteredForItunes bool `json:"isMasteredForItunes"`
TrackCount int `json:"trackCount"` IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"`
IsMasteredForItunes bool `json:"isMasteredForItunes"` ContentRating string `json:"contentRating"`
IsAppleDigitalMaster bool `json:"isAppleDigitalMaster"` DurationInMillis int `json:"durationInMillis"`
ContentRating string `json:"contentRating"` ReleaseDate string `json:"releaseDate"`
ReleaseDate string `json:"releaseDate"` Name string `json:"name"`
Name string `json:"name"` Isrc string `json:"isrc"`
RecordLabel string `json:"recordLabel"` AudioTraits []string `json:"audioTraits"`
Upc string `json:"upc"` HasLyrics bool `json:"hasLyrics"`
Copyright string `json:"copyright"` AlbumName string `json:"albumName"`
IsCompilation bool `json:"isCompilation"` PlayParams struct {
} ID string `json:"id"`
Kind string `json:"kind"`
type SongData struct { } `json:"playParams"`
ID string `json:"id"` TrackNumber int `json:"trackNumber"`
Attributes SongAttributes `json:"attributes"` AudioLocale string `json:"audioLocale"`
Relationships struct { ComposerName string `json:"composerName"`
Albums struct { } `json:"attributes"`
Data []struct { } `json:"data"`
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 {
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"`
} `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
View 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
View 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
View 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
}