Add support: use MP4box mux Dolby Atmos

pull/2/head
zhaarey 5 months ago
parent 6fbb0e0408
commit 712d548a63
  1. 9
      README.md
  2. 16
      main.go
  3. 58
      main_atmos.go
  4. 10
      main_select.go

@ -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.

@ -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) {

@ -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) {

@ -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 {

Loading…
Cancel
Save