add: MV tags
This commit is contained in:
parent
f7fcf41b13
commit
591049cedc
135
main.go
135
main.go
@ -155,11 +155,11 @@ func getUrlSong(songUrl string, token string) (string, error) {
|
|||||||
songAlbumUrl := fmt.Sprintf("https://music.apple.com/%s/album/1/%s?i=%s", storefront, albumId, songId)
|
songAlbumUrl := fmt.Sprintf("https://music.apple.com/%s/album/1/%s?i=%s", storefront, albumId, songId)
|
||||||
return songAlbumUrl, nil
|
return songAlbumUrl, nil
|
||||||
}
|
}
|
||||||
func getUrlArtistName(artistUrl string, token string) (string, error) {
|
func getUrlArtistName(artistUrl string, token string) (string, string, error) {
|
||||||
storefront, artistId := checkUrlArtist(artistUrl)
|
storefront, artistId := checkUrlArtist(artistUrl)
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/artists/%s", storefront, artistId), nil)
|
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/artists/%s", storefront, artistId), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
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("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")
|
||||||
@ -168,18 +168,18 @@ func getUrlArtistName(artistUrl string, token string) (string, error) {
|
|||||||
query.Set("l", Config.Language)
|
query.Set("l", Config.Language)
|
||||||
do, err := http.DefaultClient.Do(req)
|
do, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
defer do.Body.Close()
|
defer do.Body.Close()
|
||||||
if do.StatusCode != http.StatusOK {
|
if do.StatusCode != http.StatusOK {
|
||||||
return "", errors.New(do.Status)
|
return "", "", errors.New(do.Status)
|
||||||
}
|
}
|
||||||
obj := new(structs.AutoGeneratedArtist)
|
obj := new(structs.AutoGeneratedArtist)
|
||||||
err = json.NewDecoder(do.Body).Decode(&obj)
|
err = json.NewDecoder(do.Body).Decode(&obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return obj.Data[0].Attributes.Name, nil
|
return obj.Data[0].Attributes.Name, obj.Data[0].ID , nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkArtist(artistUrl string, token string) ([]string, error) {
|
func checkArtist(artistUrl string, token string) ([]string, error) {
|
||||||
@ -479,7 +479,7 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr
|
|||||||
counter.Success++
|
counter.Success++
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := mvDownloader(track.ID, sanAlbumFolder, token, storefront, mediaUserToken)
|
err := mvDownloader(track.ID, sanAlbumFolder, token, storefront, mediaUserToken, meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("\u26A0 Failed to dl MV:", err)
|
fmt.Println("\u26A0 Failed to dl MV:", err)
|
||||||
counter.Error++
|
counter.Error++
|
||||||
@ -1132,7 +1132,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
os.Args = args
|
os.Args = args
|
||||||
if strings.Contains(os.Args[0], "/artist/") {
|
if strings.Contains(os.Args[0], "/artist/") {
|
||||||
urlArtistName, err := getUrlArtistName(os.Args[0], token)
|
urlArtistName, urlArtistID, err := getUrlArtistName(os.Args[0], token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to get artistname.")
|
fmt.Println("Failed to get artistname.")
|
||||||
return
|
return
|
||||||
@ -1140,6 +1140,7 @@ func main() {
|
|||||||
//fmt.Println("get artistname:", urlArtistName)
|
//fmt.Println("get artistname:", urlArtistName)
|
||||||
Config.ArtistFolderFormat = strings.NewReplacer(
|
Config.ArtistFolderFormat = strings.NewReplacer(
|
||||||
"{UrlArtistName}", LimitString(urlArtistName),
|
"{UrlArtistName}", LimitString(urlArtistName),
|
||||||
|
"{ArtistId}", urlArtistID,
|
||||||
).Replace(Config.ArtistFolderFormat)
|
).Replace(Config.ArtistFolderFormat)
|
||||||
newArgs, err := checkArtist(os.Args[0], token)
|
newArgs, err := checkArtist(os.Args[0], token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1169,8 +1170,18 @@ func main() {
|
|||||||
counter.Success++
|
counter.Success++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
mvSaveDir := strings.NewReplacer(
|
||||||
|
"{ArtistName}", "",
|
||||||
|
"{UrlArtistName}", "",
|
||||||
|
"{ArtistId}", "",
|
||||||
|
).Replace(Config.ArtistFolderFormat)
|
||||||
|
if mvSaveDir != "" {
|
||||||
|
mvSaveDir = filepath.Join(Config.AlacSaveFolder, mvSaveDir)
|
||||||
|
} else {
|
||||||
|
mvSaveDir = Config.AlacSaveFolder
|
||||||
|
}
|
||||||
storefront, albumId = checkUrlMv(urlRaw)
|
storefront, albumId = checkUrlMv(urlRaw)
|
||||||
err := mvDownloader(albumId, Config.AlacSaveFolder, token, storefront, Config.MediaUserToken)
|
err := mvDownloader(albumId, mvSaveDir, token, storefront, Config.MediaUserToken, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("\u26A0 Failed to dl MV:", err)
|
fmt.Println("\u26A0 Failed to dl MV:", err)
|
||||||
counter.Error++
|
counter.Error++
|
||||||
@ -1216,48 +1227,109 @@ func main() {
|
|||||||
counter = structs.Counter{}
|
counter = structs.Counter{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func mvDownloader(adamID string, saveDir string, token string, storefront string, mediaUserToken string) error {
|
func mvDownloader(adamID string, saveDir string, token string, storefront string, mediaUserToken string, meta *structs.AutoGenerated) error {
|
||||||
MVInfo, err := getMVInfoFromAdam(adamID, token, storefront)
|
MVInfo, err := getMVInfoFromAdam(adamID, token, storefront)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("\u26A0 Failed to get MV manifest:", err)
|
fmt.Println("\u26A0 Failed to get MV manifest:", err)
|
||||||
counter.NotSong++
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
vidPath := filepath.Join(saveDir, fmt.Sprintf("%s_vid.mp4", adamID))
|
vidPath := filepath.Join(saveDir, fmt.Sprintf("%s_vid.mp4", adamID))
|
||||||
audPath := filepath.Join(saveDir, fmt.Sprintf("%s_aud.mp4", adamID))
|
audPath := filepath.Join(saveDir, fmt.Sprintf("%s_aud.mp4", adamID))
|
||||||
mvOutPath := filepath.Join(saveDir, fmt.Sprintf("%s.mp4", forbiddenNames.ReplaceAllString(MVInfo.Data[0].Attributes.Name, "_")))
|
mvOutPath := filepath.Join(saveDir, fmt.Sprintf("%s.mp4", forbiddenNames.ReplaceAllString(MVInfo.Data[0].Attributes.Name, "_")))
|
||||||
|
|
||||||
|
fmt.Println(MVInfo.Data[0].Attributes.Name)
|
||||||
|
|
||||||
exists, _ := fileExists(mvOutPath)
|
exists, _ := fileExists(mvOutPath)
|
||||||
if exists {
|
if exists {
|
||||||
fmt.Println("MV already exists locally.")
|
fmt.Println("MV already exists locally.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mvm3u8url, _, _ := runv3.GetWebplayback(adamID, token, mediaUserToken, true)
|
|
||||||
//videom3u8url, audiom3u8url , _ := mvQualitySelect(mvm3u8url)
|
|
||||||
videom3u8url, _ := extractVideo(mvm3u8url)
|
|
||||||
audiom3u8url, _ := extractMvAudio(mvm3u8url)
|
|
||||||
//fmt.Println(audiom3u8url)
|
|
||||||
|
|
||||||
videokeyAndUrls, _ := runv3.Run(adamID, videom3u8url, token, mediaUserToken, true)
|
mvm3u8url, _, _ := runv3.GetWebplayback(adamID, token, mediaUserToken, true)
|
||||||
audiokeyAndUrls, _ := runv3.Run(adamID, audiom3u8url, token, mediaUserToken, true)
|
|
||||||
//fmt.Println(videokeyAndUrls)
|
|
||||||
//fmt.Println(audiokeyAndUrls)
|
|
||||||
|
|
||||||
os.MkdirAll(saveDir, os.ModePerm)
|
os.MkdirAll(saveDir, os.ModePerm)
|
||||||
fmt.Println(MVInfo.Data[0].Attributes.Name + "-VIDEO")
|
//video
|
||||||
|
videom3u8url, _ := extractVideo(mvm3u8url)
|
||||||
|
videokeyAndUrls, _ := runv3.Run(adamID, videom3u8url, token, mediaUserToken, true)
|
||||||
_ = runv3.ExtMvData(videokeyAndUrls, vidPath)
|
_ = runv3.ExtMvData(videokeyAndUrls, vidPath)
|
||||||
|
//audio
|
||||||
fmt.Println(MVInfo.Data[0].Attributes.Name + "-AUDIO")
|
audiom3u8url, _ := extractMvAudio(mvm3u8url)
|
||||||
|
audiokeyAndUrls, _ := runv3.Run(adamID, audiom3u8url, token, mediaUserToken, true)
|
||||||
_ = runv3.ExtMvData(audiokeyAndUrls, audPath)
|
_ = runv3.ExtMvData(audiokeyAndUrls, audPath)
|
||||||
|
|
||||||
muxCmd := exec.Command("MP4Box", "-quiet", "-add", vidPath, "-add", audPath, "-keep-utc", "-new", mvOutPath)
|
|
||||||
|
//tags
|
||||||
|
tags := []string{
|
||||||
|
"tool=",
|
||||||
|
fmt.Sprintf("artist=%s", MVInfo.Data[0].Attributes.ArtistName),
|
||||||
|
fmt.Sprintf("title=%s", MVInfo.Data[0].Attributes.Name),
|
||||||
|
fmt.Sprintf("genre=%s", MVInfo.Data[0].Attributes.GenreNames),
|
||||||
|
fmt.Sprintf("created=%s", MVInfo.Data[0].Attributes.ReleaseDate),
|
||||||
|
fmt.Sprintf("ISRC=%s", MVInfo.Data[0].Attributes.Isrc),
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentRating tag
|
||||||
|
if MVInfo.Data[0].Attributes.ContentRating == "explicit" {
|
||||||
|
tags = append(tags, "rating=1")
|
||||||
|
} else if MVInfo.Data[0].Attributes.ContentRating == "clean" {
|
||||||
|
tags = append(tags, "rating=2")
|
||||||
|
} else {
|
||||||
|
tags = append(tags, "rating=0")
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取传入的专辑信息当中该mv所在的位置
|
||||||
|
var trackTotal int
|
||||||
|
var trackNum int
|
||||||
|
var index int
|
||||||
|
if meta !=nil {
|
||||||
|
trackTotal = len(meta.Data[0].Relationships.Tracks.Data)
|
||||||
|
for i, track := range meta.Data[0].Relationships.Tracks.Data {
|
||||||
|
if adamID == track.ID {
|
||||||
|
index = i
|
||||||
|
trackNum = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//根据情况额外添加可使用的tags
|
||||||
|
if meta != nil {
|
||||||
|
if meta.Data[0].Type == "playlists" && !Config.UseSongInfoForPlaylist {
|
||||||
|
tags = append(tags, "disk=1/1")
|
||||||
|
tags = append(tags, fmt.Sprintf("album=%s", meta.Data[0].Attributes.Name))
|
||||||
|
tags = append(tags, fmt.Sprintf("track=%d", trackNum))
|
||||||
|
tags = append(tags, fmt.Sprintf("tracknum=%d/%d", trackNum, trackTotal))
|
||||||
|
tags = append(tags, fmt.Sprintf("album_artist=%s", meta.Data[0].Attributes.ArtistName))
|
||||||
|
tags = append(tags, fmt.Sprintf("performer=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName))
|
||||||
|
tags = append(tags, fmt.Sprintf("copyright=%s", meta.Data[0].Attributes.Copyright))
|
||||||
|
tags = append(tags, fmt.Sprintf("UPC=%s", meta.Data[0].Attributes.Upc))
|
||||||
|
} else {
|
||||||
|
tags = append(tags, fmt.Sprintf("album=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.AlbumName))
|
||||||
|
tags = append(tags, fmt.Sprintf("disk=%d/%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.DiscNumber, meta.Data[0].Relationships.Tracks.Data[trackTotal-1].Attributes.DiscNumber))
|
||||||
|
tags = append(tags, fmt.Sprintf("track=%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber))
|
||||||
|
tags = append(tags, fmt.Sprintf("tracknum=%d/%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber, trackTotal))
|
||||||
|
tags = append(tags, fmt.Sprintf("album_artist=%s", meta.Data[0].Attributes.ArtistName))
|
||||||
|
tags = append(tags, fmt.Sprintf("performer=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName))
|
||||||
|
tags = append(tags, fmt.Sprintf("copyright=%s", meta.Data[0].Attributes.Copyright))
|
||||||
|
tags = append(tags, fmt.Sprintf("UPC=%s", meta.Data[0].Attributes.Upc))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tags = append(tags, fmt.Sprintf("album=%s", MVInfo.Data[0].Attributes.AlbumName))
|
||||||
|
tags = append(tags, fmt.Sprintf("disk=%d", MVInfo.Data[0].Attributes.DiscNumber))
|
||||||
|
tags = append(tags, fmt.Sprintf("track=%d", MVInfo.Data[0].Attributes.TrackNumber))
|
||||||
|
tags = append(tags, fmt.Sprintf("tracknum=%d", MVInfo.Data[0].Attributes.TrackNumber))
|
||||||
|
//tags = append(tags, fmt.Sprintf("album_artist=%s", MVInfo.Data[0].Attributes.ArtistName))
|
||||||
|
tags = append(tags, fmt.Sprintf("performer=%s", MVInfo.Data[0].Attributes.ArtistName))
|
||||||
|
}
|
||||||
|
//mux and add tag
|
||||||
|
tagsString := strings.Join(tags, ":")
|
||||||
|
muxCmd := exec.Command("MP4Box", "-itags", tagsString, "-quiet", "-add", vidPath, "-add", audPath, "-keep-utc", "-new", mvOutPath)
|
||||||
fmt.Printf("MV Remuxing...")
|
fmt.Printf("MV Remuxing...")
|
||||||
if err := muxCmd.Run(); err != nil {
|
if err := muxCmd.Run(); err != nil {
|
||||||
fmt.Printf("MV mux failed: %v\n", err)
|
fmt.Printf("MV mux failed: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("\rMV Remuxed. \n")
|
fmt.Printf("\rMV Remuxed. \n")
|
||||||
_ = os.Remove(vidPath)
|
defer os.Remove(vidPath)
|
||||||
_ = os.Remove(audPath)
|
defer os.Remove(audPath)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1283,13 +1355,13 @@ func extractMvAudio(c string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
videoString := string(body)
|
audioString := string(body)
|
||||||
from, listType, err := m3u8.DecodeFrom(strings.NewReader(videoString), true)
|
from, listType, err := m3u8.DecodeFrom(strings.NewReader(audioString), true)
|
||||||
if err != nil || listType != m3u8.MASTER {
|
if err != nil || listType != m3u8.MASTER {
|
||||||
return "", errors.New("m3u8 not of media type")
|
return "", errors.New("m3u8 not of media type")
|
||||||
}
|
}
|
||||||
|
|
||||||
video := from.(*m3u8.MasterPlaylist)
|
audio := from.(*m3u8.MasterPlaylist)
|
||||||
|
|
||||||
var audioPriority = []string{"audio-atmos", "audio-ac3", "audio-stereo-256"}
|
var audioPriority = []string{"audio-atmos", "audio-ac3", "audio-stereo-256"}
|
||||||
if Config.MVAudioType == "ac3" {
|
if Config.MVAudioType == "ac3" {
|
||||||
@ -1307,7 +1379,7 @@ func extractMvAudio(c string) (string, error) {
|
|||||||
}
|
}
|
||||||
var audioStreams []AudioStream
|
var audioStreams []AudioStream
|
||||||
|
|
||||||
for _, variant := range video.Variants {
|
for _, variant := range audio.Variants {
|
||||||
for _, audiov := range variant.Alternatives {
|
for _, audiov := range variant.Alternatives {
|
||||||
if audiov.URI != "" {
|
if audiov.URI != "" {
|
||||||
for _, priority := range audioPriority {
|
for _, priority := range audioPriority {
|
||||||
@ -1336,7 +1408,7 @@ func extractMvAudio(c string) (string, error) {
|
|||||||
sort.Slice(audioStreams, func(i, j int) bool {
|
sort.Slice(audioStreams, func(i, j int) bool {
|
||||||
return audioStreams[i].Rank > audioStreams[j].Rank
|
return audioStreams[i].Rank > audioStreams[j].Rank
|
||||||
})
|
})
|
||||||
|
fmt.Println("Audio: " + audioStreams[0].GroupID)
|
||||||
return audioStreams[0].URL, nil
|
return audioStreams[0].URL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1874,6 +1946,7 @@ func extractVideo(c string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
fmt.Println("Video: " + variant.Resolution + "-" + variant.VideoRange)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,10 @@ func ExtMvData (keyAndUrls string, savePath string)(error) {
|
|||||||
fmt.Printf("下载链接 %s 失败:%v\n", url, err)
|
fmt.Printf("下载链接 %s 失败:%v\n", url, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
fmt.Printf("链接 %s 响应失败:%v\n", url, resp.Status)
|
||||||
|
return errors.New(resp.Status)
|
||||||
|
}
|
||||||
// 将响应体写入输出文件
|
// 将响应体写入输出文件
|
||||||
_, err = io.Copy(barWriter, resp.Body)
|
_, err = io.Copy(barWriter, resp.Body)
|
||||||
defer resp.Body.Close() // 注意及时关闭响应体,避免资源泄露
|
defer resp.Body.Close() // 注意及时关闭响应体,避免资源泄露
|
||||||
|
@ -437,11 +437,15 @@ type AutoGeneratedMusicVideo struct {
|
|||||||
TextColor3 string `json:"textColor3"`
|
TextColor3 string `json:"textColor3"`
|
||||||
TextColor4 string `json:"textColor4"`
|
TextColor4 string `json:"textColor4"`
|
||||||
} `json:"artwork"`
|
} `json:"artwork"`
|
||||||
|
AlbumName string `json:"albumName"`
|
||||||
ArtistName string `json:"artistName"`
|
ArtistName string `json:"artistName"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
GenreNames []string `json:"genreNames"`
|
GenreNames []string `json:"genreNames"`
|
||||||
DurationInMillis int `json:"durationInMillis"`
|
DurationInMillis int `json:"durationInMillis"`
|
||||||
Isrc string `json:"isrc"`
|
Isrc string `json:"isrc"`
|
||||||
|
TrackNumber int `json:"trackNumber"`
|
||||||
|
DiscNumber int `json:"discNumber"`
|
||||||
|
ContentRating string `json:"contentRating"`
|
||||||
ReleaseDate string `json:"releaseDate"`
|
ReleaseDate string `json:"releaseDate"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Has4K bool `json:"has4K"`
|
Has4K bool `json:"has4K"`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user