Add support: use MP4box mux Dolby Atmos

This commit is contained in:
zhaarey
2024-04-21 14:37:04 +08:00
parent 6fbb0e0408
commit 712d548a63
4 changed files with 88 additions and 11 deletions

View File

@@ -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 # Apple Music ALAC / Dolby Atmos Downloader
Original script by Sorrow. Modified by me to include some fixes and improvements. Original script by Sorrow. Modified by me to include some fixes and improvements.

16
main.go
View File

@@ -32,6 +32,8 @@ const (
var ( var (
forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`) forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`)
) )
var oktrackNum int = 0
var trackTotalnum int = 0
type SampleInfo struct { type SampleInfo struct {
data []byte data []byte
@@ -1038,12 +1040,18 @@ func rip(albumId string, token string, storefront string) error {
fmt.Println("Failed to get album metadata.\n") fmt.Println("Failed to get album metadata.\n")
return err 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, ".") { if strings.HasSuffix(albumFolder, ".") {
albumFolder = strings.ReplaceAll(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) os.MkdirAll(sanAlbumFolder, os.ModePerm)
fmt.Println(singerFoldername)
fmt.Println(albumFolder) fmt.Println(albumFolder)
err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL) err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL)
if err != nil { if err != nil {
@@ -1052,6 +1060,7 @@ func rip(albumId string, token string, storefront string) error {
trackTotal := len(meta.Data[0].Relationships.Tracks.Data) trackTotal := len(meta.Data[0].Relationships.Tracks.Data)
for trackNum, track := range meta.Data[0].Relationships.Tracks.Data { for trackNum, track := range meta.Data[0].Relationships.Tracks.Data {
trackNum++ trackNum++
trackTotalnum += 1
fmt.Printf("Track %d of %d:\n", trackNum, trackTotal) fmt.Printf("Track %d of %d:\n", trackNum, trackTotal)
manifest, err := getInfoFromAdam(track.ID, token, storefront) manifest, err := getInfoFromAdam(track.ID, token, storefront)
if err != nil { if err != nil {
@@ -1070,6 +1079,7 @@ func rip(albumId string, token string, storefront string) error {
} }
if exists { if exists {
fmt.Println("Track already exists locally.") fmt.Println("Track already exists locally.")
oktrackNum += 1
continue continue
} }
trackUrl, keys, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls) 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) fmt.Println("Failed to decrypt track.\n", err)
continue continue
} }
oktrackNum += 1
} }
return err return err
} }
@@ -1130,6 +1141,7 @@ func main() {
fmt.Println(err) fmt.Println(err)
} }
} }
fmt.Printf("======= Completed %d/%d ###### %d errors!! =======\n", oktrackNum, trackTotalnum, trackTotalnum-oktrackNum)
} }
func extractMedia(b string) (string, []string, error) { func extractMedia(b string) (string, []string, error) {

View File

@@ -13,6 +13,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort" "sort"
@@ -32,6 +33,8 @@ const (
var ( var (
forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`) forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`)
) )
var oktrackNum int = 0
var trackTotalnum int = 0
type SampleInfo struct { type SampleInfo struct {
data []byte data []byte
@@ -1039,12 +1042,18 @@ func rip(albumId string, token string, storefront string) error {
fmt.Println("Failed to get album metadata.\n") fmt.Println("Failed to get album metadata.\n")
return err 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, ".") { if strings.HasSuffix(albumFolder, ".") {
albumFolder = strings.ReplaceAll(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) os.MkdirAll(sanAlbumFolder, os.ModePerm)
fmt.Println(singerFoldername)
fmt.Println(albumFolder) fmt.Println(albumFolder)
err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL) err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL)
if err != nil { if err != nil {
@@ -1053,6 +1062,7 @@ func rip(albumId string, token string, storefront string) error {
trackTotal := len(meta.Data[0].Relationships.Tracks.Data) trackTotal := len(meta.Data[0].Relationships.Tracks.Data)
for trackNum, track := range meta.Data[0].Relationships.Tracks.Data { for trackNum, track := range meta.Data[0].Relationships.Tracks.Data {
trackNum++ trackNum++
trackTotalnum += 1
fmt.Printf("Track %d of %d:\n", trackNum, trackTotal) fmt.Printf("Track %d of %d:\n", trackNum, trackTotal)
manifest, err := getInfoFromAdam(track.ID, token, storefront) manifest, err := getInfoFromAdam(track.ID, token, storefront)
if err != nil { if err != nil {
@@ -1064,13 +1074,20 @@ func rip(albumId string, token string, storefront string) error {
continue continue
} }
filename := fmt.Sprintf("%02d. %s.ec3", trackNum, forbiddenNames.ReplaceAllString(track.Attributes.Name, "_")) 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) trackPath := filepath.Join(sanAlbumFolder, filename)
m4atrackPath := filepath.Join(sanAlbumFolder, m4afilename)
exists, err := fileExists(trackPath) exists, err := fileExists(trackPath)
m4aexists, errs := fileExists(m4atrackPath)
if err != nil { if err != nil {
fmt.Println("Failed to check if track exists.") fmt.Println("Failed to check if track exists.")
} }
if exists { if errs != nil {
fmt.Println("Track already exists locally.") fmt.Println("Failed to check if m4atrack exists.")
}
if exists || m4aexists {
fmt.Println("Track or M4atrack already exists locally.")
oktrackNum += 1
continue continue
} }
trackUrl, keys, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls) 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) fmt.Println("Failed to decrypt track.\n", err)
continue 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 return err
} }
func main() { func main() {
token, err := getToken() token, err := getToken()
if err != nil { if err != nil {
@@ -1130,6 +1179,7 @@ func main() {
fmt.Println(err) fmt.Println(err)
} }
} }
fmt.Printf("======= Completed %d/%d ###### %d errors!! =======\n", oktrackNum, trackTotalnum, trackTotalnum-oktrackNum)
} }
func extractMedia(b string) (string, []string, error) { func extractMedia(b string) (string, []string, error) {

View File

@@ -1047,12 +1047,18 @@ func rip(albumId string, token string, storefront string) error {
fmt.Println("Failed to get album metadata.\n") fmt.Println("Failed to get album metadata.\n")
return err 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, ".") { if strings.HasSuffix(albumFolder, ".") {
albumFolder = strings.ReplaceAll(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) os.MkdirAll(sanAlbumFolder, os.ModePerm)
fmt.Println(singerFoldername)
fmt.Println(albumFolder) fmt.Println(albumFolder)
err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL) err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL)
if err != nil { if err != nil {