Merge pull request #81 from itouakirai/main

fix:部分stream类型的station下载失败
This commit is contained in:
ZHAAREY
2025-09-23 00:10:20 +08:00
committed by GitHub
3 changed files with 39 additions and 35 deletions

View File

@@ -45,18 +45,18 @@ func GetStationResp(storefront string, id string, language string, token string)
return obj, nil
}
func GetStationAssetsUrl(id string, mutoken string, token string) (string, error) {
func GetStationAssetsUrlAndServerUrl(id string, mutoken string, token string) (string, string, error) {
var err error
if token == "" {
token, err = GetToken()
if err != nil {
return "", err
return "", "", err
}
}
req, err := http.NewRequest("GET", "https://amp-api.music.apple.com/v1/play/assets", nil)
if err != nil {
return "", err
return "", "", 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")
@@ -71,18 +71,18 @@ func GetStationAssetsUrl(id string, mutoken string, token string) (string, error
req.URL.RawQuery = query.Encode()
do, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
return "", "", err
}
defer do.Body.Close()
if do.StatusCode != http.StatusOK {
return "", errors.New(do.Status)
return "", "", errors.New(do.Status)
}
obj := new(StationAssets)
err = json.NewDecoder(do.Body).Decode(&obj)
if err != nil {
return "", err
return "", "", err
}
return obj.Results.Assets[0].Url, nil
return obj.Results.Assets[0].Url, obj.Results.Assets[0].KeyServerUrl, nil
}
func GetStationNextTracks(id, mutoken, language, token string) (*TrackResp, error) {

View File

@@ -64,7 +64,7 @@ func BeforeRequest(cl *resty.Client, ctx context.Context, url string, body []byt
jsondata := map[string]interface{}{
"challenge": base64.StdEncoding.EncodeToString(body), // 'body' is passed in directly
"key-system": "com.widevine.alpha",
"uri": "data:;base64," + ctx.Value("pssh").(string),
"uri": ctx.Value("uriPrefix").(string) + "," + ctx.Value("pssh").(string),
"adamId": ctx.Value("adamId").(string),
"isLibrary": false,
"user-initiated": true,
@@ -102,7 +102,7 @@ func AfterRequest(response *resty.Response) ([]byte, error) {
return license, nil
}
func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool) (string, string, error) {
func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool) (string, string, string, error) {
url := "https://play.music.apple.com/WebObjects/MZPlay.woa/wa/webPlayback"
postData := map[string]string{
"salableAdamId": adamId,
@@ -110,12 +110,12 @@ func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool
jsonData, err := json.Marshal(postData)
if err != nil {
fmt.Println("Error encoding JSON:", err)
return "", "", err
return "", "", "", err
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(jsonData)))
if err != nil {
fmt.Println("Error creating request:", err)
return "", "", err
return "", "", "", err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Origin", "https://music.apple.com")
@@ -130,7 +130,7 @@ func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool
//resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return "", "", err
return "", "", "", err
}
defer resp.Body.Close()
//fmt.Println("Response Status:", resp.Status)
@@ -138,25 +138,25 @@ func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool
err = json.NewDecoder(resp.Body).Decode(&obj)
if err != nil {
fmt.Println("json err:", err)
return "", "", err
return "", "", "", err
}
if len(obj.List) > 0 {
if mvmode {
return obj.List[0].HlsPlaylistUrl, "", nil
return obj.List[0].HlsPlaylistUrl, "", "", nil
}
// 遍历 Assets
for i := range obj.List[0].Assets {
if obj.List[0].Assets[i].Flavor == "28:ctrp256" {
kidBase64, fileurl, err := extractKidBase64(obj.List[0].Assets[i].URL, false)
kidBase64, fileurl, uriPrefix, err := extractKidBase64(obj.List[0].Assets[i].URL, false)
if err != nil {
return "", "", err
return "", "", "", err
}
return fileurl, kidBase64, nil
return fileurl, kidBase64, uriPrefix, nil
}
continue
}
}
return "", "", errors.New("Unavailable")
return "", "", "", errors.New("Unavailable")
}
type Songlist struct {
@@ -171,30 +171,32 @@ type Songlist struct {
Status int `json:"status"`
}
func extractKidBase64(b string, mvmode bool) (string, string, error) {
func extractKidBase64(b string, mvmode bool) (string, string, string, error) {
resp, err := http.Get(b)
if err != nil {
return "", "", err
return "", "", "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", "", errors.New(resp.Status)
return "", "", "", errors.New(resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", "", err
return "", "", "", err
}
masterString := string(body)
from, listType, err := m3u8.DecodeFrom(strings.NewReader(masterString), true)
if err != nil {
return "", "", err
return "", "", "", err
}
var kidbase64 string
var uriPrefix string
var urlBuilder strings.Builder
if listType == m3u8.MEDIA {
mediaPlaylist := from.(*m3u8.MediaPlaylist)
if mediaPlaylist.Key != nil {
split := strings.Split(mediaPlaylist.Key.URI, ",")
uriPrefix = split[0]
kidbase64 = split[1]
lastSlashIndex := strings.LastIndex(b, "/")
// 截取最后一个斜杠之前的部分
@@ -221,7 +223,7 @@ func extractKidBase64(b string, mvmode bool) (string, string, error) {
} else {
fmt.Println("Not a media playlist")
}
return kidbase64, urlBuilder.String(), nil
return kidbase64, urlBuilder.String(), uriPrefix, nil
}
func extsong(b string) bytes.Buffer {
resp, err := http.Get(b)
@@ -251,18 +253,19 @@ func extsong(b string) bytes.Buffer {
io.Copy(io.MultiWriter(&buffer, bar), resp.Body)
return buffer
}
func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmode bool) (string, error) {
func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmode bool, serverUrl string) (string, error) {
var keystr string //for mv key
var fileurl string
var kidBase64 string
var uriPrefix string
var err error
if mvmode {
kidBase64, fileurl, err = extractKidBase64(trackpath, true)
kidBase64, fileurl, uriPrefix, err = extractKidBase64(trackpath, true)
if err != nil {
return "", err
}
} else {
fileurl, kidBase64, err = GetWebplayback(adamId, authtoken, mutoken, false)
fileurl, kidBase64, uriPrefix, err = GetWebplayback(adamId, authtoken, mutoken, false)
if err != nil {
return "", err
}
@@ -270,6 +273,7 @@ func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmo
ctx := context.Background()
ctx = context.WithValue(ctx, "pssh", kidBase64)
ctx = context.WithValue(ctx, "adamId", adamId)
ctx = context.WithValue(ctx, "uriPrefix", uriPrefix)
pssh, err := getPSSH("", kidBase64)
//fmt.Println(pssh)
if err != nil {
@@ -289,8 +293,8 @@ func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmo
}
key.CdmInit()
var keybt []byte
if strings.Contains(adamId, "ra.") {
keystr, keybt, err = key.GetKey(ctx, "https://play.itunes.apple.com/WebObjects/MZPlay.woa/web/radio/versions/1/license", pssh, nil)
if serverUrl != "" {
keystr, keybt, err = key.GetKey(ctx, serverUrl, pssh, nil)
if err != nil {
fmt.Println(err)
return "", err