diff --git a/main.go b/main.go index dd83367..4fd435f 100644 --- a/main.go +++ b/main.go @@ -102,14 +102,14 @@ func checkUrl(url string) (string, string) { return matches[0][1], matches[0][2] } } -func checkUrlMv(url string) string { +func checkUrlMv(url string) (string, string) { pat := regexp.MustCompile(`^(?:https:\/\/(?:beta\.music|music)\.apple\.com\/(\w{2})(?:\/music-video|\/music-video\/.+))\/(?:id)?(\d[^\D]+)(?:$|\?)`) matches := pat.FindAllStringSubmatch(url, -1) if matches == nil { - return "" + return "", "" } else { - return matches[0][2] + return matches[0][1], matches[0][2] } } func checkUrlSong(url string) (string, string) { @@ -479,7 +479,7 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr counter.Success++ return } - err := mvDownloader(track.ID, sanAlbumFolder, token, mediaUserToken) + err := mvDownloader(track.ID, sanAlbumFolder, token, storefront, mediaUserToken) if err != nil { fmt.Println("\u26A0 Failed to dl MV:", err) counter.Error++ @@ -1169,7 +1169,8 @@ func main() { counter.Success++ continue } - err := mvDownloader(checkUrlMv(urlRaw), Config.AlacSaveFolder, token, Config.MediaUserToken) + storefront, albumId = checkUrlMv(urlRaw) + err := mvDownloader(albumId, Config.AlacSaveFolder, token, storefront, Config.MediaUserToken) if err != nil { fmt.Println("\u26A0 Failed to dl MV:", err) counter.Error++ @@ -1215,10 +1216,16 @@ func main() { counter = structs.Counter{} } } -func mvDownloader(adamID string, saveDir string, token string, mediaUserToken string) error { +func mvDownloader(adamID string, saveDir string, token string, storefront string, mediaUserToken string) error { + MVInfo, err := getMVInfoFromAdam(adamID, token, storefront) + if err != nil { + fmt.Println("\u26A0 Failed to get MV manifest:", err) + counter.NotSong++ + return nil + } vidPath := filepath.Join(saveDir, fmt.Sprintf("%s_vid.mp4", adamID)) audPath := filepath.Join(saveDir, fmt.Sprintf("%s_aud.mp4", adamID)) - mvOutPath := filepath.Join(saveDir, fmt.Sprintf("%s.mp4", adamID)) + mvOutPath := filepath.Join(saveDir, fmt.Sprintf("%s.mp4", forbiddenNames.ReplaceAllString(MVInfo.Data[0].Attributes.Name, "_"))) exists, _ := fileExists(mvOutPath) if exists { fmt.Println("MV already exists locally.") @@ -1236,10 +1243,10 @@ func mvDownloader(adamID string, saveDir string, token string, mediaUserToken st //fmt.Println(audiokeyAndUrls) os.MkdirAll(saveDir, os.ModePerm) - fmt.Println("MV-VIDEO") + fmt.Println(MVInfo.Data[0].Attributes.Name + "-VIDEO") _ = runv3.ExtMvData(videokeyAndUrls, vidPath) - fmt.Println("MV-AUDIO") + fmt.Println(MVInfo.Data[0].Attributes.Name + "-AUDIO") _ = runv3.ExtMvData(audiokeyAndUrls, audPath) muxCmd := exec.Command("MP4Box", "-quiet", "-add", vidPath, "-add", audPath, "-keep-utc", "-new", mvOutPath) @@ -1917,6 +1924,36 @@ func getInfoFromAdam(adamId string, token string, storefront string) (*structs.S return nil, nil } +func getMVInfoFromAdam(adamId string, token string, storefront string) (*structs.AutoGeneratedMusicVideo, error) { + request, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/music-videos/%s", storefront, adamId), nil) + if err != nil { + return nil, err + } + query := url.Values{} + query.Set("l", Config.Language) + request.URL.RawQuery = query.Encode() + request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + request.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") + request.Header.Set("Origin", "https://music.apple.com") + + do, err := http.DefaultClient.Do(request) + if err != nil { + return nil, err + } + defer do.Body.Close() + if do.StatusCode != http.StatusOK { + return nil, errors.New(do.Status) + } + + obj := new(structs.AutoGeneratedMusicVideo) + err = json.NewDecoder(do.Body).Decode(&obj) + if err != nil { + return nil, err + } + + return obj, nil +} + func getToken() (string, error) { req, err := http.NewRequest("GET", "https://beta.music.apple.com", nil) if err != nil { diff --git a/utils/structs/structs.go b/utils/structs/structs.go index 82902a5..b7b088c 100644 --- a/utils/structs/structs.go +++ b/utils/structs/structs.go @@ -418,6 +418,42 @@ type AutoGeneratedArtist struct { } `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"` + ArtistName string `json:"artistName"` + URL string `json:"url"` + GenreNames []string `json:"genreNames"` + DurationInMillis int `json:"durationInMillis"` + Isrc string `json:"isrc"` + 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"`