From 712d548a6360678ccc484bbe4425e4df426be95d Mon Sep 17 00:00:00 2001 From: zhaarey Date: Sun, 21 Apr 2024 14:37:04 +0800 Subject: [PATCH] Add support: use MP4box mux Dolby Atmos --- README.md | 9 ++++++++ main.go | 18 ++++++++++++--- main_atmos.go | 60 +++++++++++++++++++++++++++++++++++++++++++++----- main_select.go | 12 +++++++--- 4 files changed, 88 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c2f2716..839548d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ +### !!封装杜比全景声必须先安装[MP4Box](https://gpac.io/downloads/gpac-nightly-builds/),并确认[MP4Box](https://gpac.io/downloads/gpac-nightly-builds/)已正确添加到环境变量 + +### 添加功能 +1. 调用外部MP4Box自动封装ec3为m4a +2. 更改目录结构为 歌手名\专辑名 ;Atmos下载文件则另外移动到AM-DL-Atmos downloads,并更改目录结构为 歌手名\专辑名 [Atmos] +3. 运行结束后显示总体完成情况 + + + # Apple Music ALAC / Dolby Atmos Downloader Original script by Sorrow. Modified by me to include some fixes and improvements. diff --git a/main.go b/main.go index e7ffdd1..6de2694 100644 --- a/main.go +++ b/main.go @@ -32,6 +32,8 @@ const ( var ( forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`) ) +var oktrackNum int = 0 +var trackTotalnum int = 0 type SampleInfo struct { data []byte @@ -1038,12 +1040,18 @@ func rip(albumId string, token string, storefront string) error { fmt.Println("Failed to get album metadata.\n") return err } - albumFolder := fmt.Sprintf("%s - %s", meta.Data[0].Attributes.ArtistName, meta.Data[0].Attributes.Name) + singerFoldername := fmt.Sprintf("%s", meta.Data[0].Attributes.ArtistName) + if strings.HasSuffix(singerFoldername, ".") { + singerFoldername = strings.ReplaceAll(singerFoldername, ".", "") + } + singerFolder := filepath.Join("AM-DL downloads", forbiddenNames.ReplaceAllString(singerFoldername, "_")) + albumFolder := fmt.Sprintf("%s", meta.Data[0].Attributes.Name) if strings.HasSuffix(albumFolder, ".") { albumFolder = strings.ReplaceAll(albumFolder, ".", "") } - sanAlbumFolder := filepath.Join("AM-DL downloads", forbiddenNames.ReplaceAllString(albumFolder, "_")) + sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) os.MkdirAll(sanAlbumFolder, os.ModePerm) + fmt.Println(singerFoldername) fmt.Println(albumFolder) err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL) if err != nil { @@ -1052,6 +1060,7 @@ func rip(albumId string, token string, storefront string) error { trackTotal := len(meta.Data[0].Relationships.Tracks.Data) for trackNum, track := range meta.Data[0].Relationships.Tracks.Data { trackNum++ + trackTotalnum += 1 fmt.Printf("Track %d of %d:\n", trackNum, trackTotal) manifest, err := getInfoFromAdam(track.ID, token, storefront) if err != nil { @@ -1070,6 +1079,7 @@ func rip(albumId string, token string, storefront string) error { } if exists { fmt.Println("Track already exists locally.") + oktrackNum += 1 continue } trackUrl, keys, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls) @@ -1100,6 +1110,7 @@ func rip(albumId string, token string, storefront string) error { fmt.Println("Failed to decrypt track.\n", err) continue } + oktrackNum += 1 } return err } @@ -1130,6 +1141,7 @@ func main() { fmt.Println(err) } } + fmt.Printf("======= Completed %d/%d ###### %d errors!! =======\n", oktrackNum, trackTotalnum, trackTotalnum-oktrackNum) } func extractMedia(b string) (string, []string, error) { @@ -1765,4 +1777,4 @@ type AutoGeneratedTrack struct { } `json:"artists"` } `json:"relationships"` } `json:"data"` -} +} \ No newline at end of file diff --git a/main_atmos.go b/main_atmos.go index c44842f..6c7a281 100644 --- a/main_atmos.go +++ b/main_atmos.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "os" + "os/exec" "path/filepath" "regexp" "sort" @@ -32,6 +33,8 @@ const ( var ( forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`) ) +var oktrackNum int = 0 +var trackTotalnum int = 0 type SampleInfo struct { data []byte @@ -1039,12 +1042,18 @@ func rip(albumId string, token string, storefront string) error { fmt.Println("Failed to get album metadata.\n") return err } - albumFolder := fmt.Sprintf("%s - %s", meta.Data[0].Attributes.ArtistName, meta.Data[0].Attributes.Name) + singerFoldername := fmt.Sprintf("%s", meta.Data[0].Attributes.ArtistName) + if strings.HasSuffix(singerFoldername, ".") { + singerFoldername = strings.ReplaceAll(singerFoldername, ".", "") + } + singerFolder := filepath.Join("AM-DL-Atmos downloads", forbiddenNames.ReplaceAllString(singerFoldername, "_")) + albumFolder := fmt.Sprintf("%s [Atmos]", meta.Data[0].Attributes.Name) if strings.HasSuffix(albumFolder, ".") { albumFolder = strings.ReplaceAll(albumFolder, ".", "") } - sanAlbumFolder := filepath.Join("AM-DL downloads", forbiddenNames.ReplaceAllString(albumFolder, "_")) + sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) os.MkdirAll(sanAlbumFolder, os.ModePerm) + fmt.Println(singerFoldername) fmt.Println(albumFolder) err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL) if err != nil { @@ -1053,6 +1062,7 @@ func rip(albumId string, token string, storefront string) error { trackTotal := len(meta.Data[0].Relationships.Tracks.Data) for trackNum, track := range meta.Data[0].Relationships.Tracks.Data { trackNum++ + trackTotalnum += 1 fmt.Printf("Track %d of %d:\n", trackNum, trackTotal) manifest, err := getInfoFromAdam(track.ID, token, storefront) if err != nil { @@ -1064,13 +1074,20 @@ func rip(albumId string, token string, storefront string) error { continue } filename := fmt.Sprintf("%02d. %s.ec3", trackNum, forbiddenNames.ReplaceAllString(track.Attributes.Name, "_")) + m4afilename := fmt.Sprintf("%02d. %s.m4a", trackNum, forbiddenNames.ReplaceAllString(track.Attributes.Name, "_")) trackPath := filepath.Join(sanAlbumFolder, filename) + m4atrackPath := filepath.Join(sanAlbumFolder, m4afilename) exists, err := fileExists(trackPath) + m4aexists, errs := fileExists(m4atrackPath) if err != nil { fmt.Println("Failed to check if track exists.") } - if exists { - fmt.Println("Track already exists locally.") + if errs != nil { + fmt.Println("Failed to check if m4atrack exists.") + } + if exists || m4aexists { + fmt.Println("Track or M4atrack already exists locally.") + oktrackNum += 1 continue } trackUrl, keys, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls) @@ -1101,10 +1118,42 @@ func rip(albumId string, token string, storefront string) error { fmt.Println("Failed to decrypt track.\n", err) continue } + index := trackNum - 1 + tags := []string{ + "tool=", + fmt.Sprintf("title=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.Name), + fmt.Sprintf("album=%s", meta.Data[0].Attributes.Name), + fmt.Sprintf("artist=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName), + fmt.Sprintf("genre=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.GenreNames[0]), + fmt.Sprintf("created=%s", meta.Data[0].Attributes.ReleaseDate), + fmt.Sprintf("album_artist=%s", meta.Data[0].Attributes.ArtistName), + fmt.Sprintf("composer=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ComposerName), + fmt.Sprintf("copyright=%s", meta.Data[0].Attributes.Copyright), + fmt.Sprintf("ISRC=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.Isrc), + fmt.Sprintf("UPC=%s", meta.Data[0].Attributes.Upc), + fmt.Sprintf("track=%d/%d", trackNum, trackTotal), + } + + tagsString := strings.Join(tags, ":") + cmd := exec.Command("MP4Box", "-add", trackPath,"-name",fmt.Sprintf("1=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.Name),"-itags",tagsString, "-brand", "mp42", "-ab", "dby1", m4atrackPath) + fmt.Printf("Encapsulating %s into %s\n", filepath.Base(trackPath), filepath.Base(m4atrackPath)) + if err := cmd.Run(); err != nil { + fmt.Printf("Error encapsulating file: %v\n", err) + continue + } + fmt.Printf("Deleting original EC3 file: %s\n", filepath.Base(trackPath)) + if err := os.Remove(trackPath); err != nil { + fmt.Printf("Error deleting file: %v\n", err) + continue + } + fmt.Printf("Successfully processed and deleted %s\n", filepath.Base(trackPath)) + oktrackNum += 1 + } return err } + func main() { token, err := getToken() if err != nil { @@ -1130,6 +1179,7 @@ func main() { fmt.Println(err) } } + fmt.Printf("======= Completed %d/%d ###### %d errors!! =======\n", oktrackNum, trackTotalnum, trackTotalnum-oktrackNum) } func extractMedia(b string) (string, []string, error) { @@ -1764,4 +1814,4 @@ type AutoGeneratedTrack struct { } `json:"artists"` } `json:"relationships"` } `json:"data"` -} +} \ No newline at end of file diff --git a/main_select.go b/main_select.go index c4292c1..b3eccaa 100644 --- a/main_select.go +++ b/main_select.go @@ -1047,12 +1047,18 @@ func rip(albumId string, token string, storefront string) error { fmt.Println("Failed to get album metadata.\n") return err } - albumFolder := fmt.Sprintf("%s - %s", meta.Data[0].Attributes.ArtistName, meta.Data[0].Attributes.Name) + singerFoldername := fmt.Sprintf("%s", meta.Data[0].Attributes.ArtistName) + if strings.HasSuffix(singerFoldername, ".") { + singerFoldername = strings.ReplaceAll(singerFoldername, ".", "") + } + singerFolder := filepath.Join("AM-DL downloads", forbiddenNames.ReplaceAllString(singerFoldername, "_")) + albumFolder := fmt.Sprintf("%s", meta.Data[0].Attributes.Name) if strings.HasSuffix(albumFolder, ".") { albumFolder = strings.ReplaceAll(albumFolder, ".", "") } - sanAlbumFolder := filepath.Join("AM-DL downloads", forbiddenNames.ReplaceAllString(albumFolder, "_")) + sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) os.MkdirAll(sanAlbumFolder, os.ModePerm) + fmt.Println(singerFoldername) fmt.Println(albumFolder) err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL) if err != nil { @@ -1803,4 +1809,4 @@ type AutoGeneratedTrack struct { } `json:"artists"` } `json:"relationships"` } `json:"data"` -} +} \ No newline at end of file