forked from sim1222-mirror/apple-music-downloader
add use go-mp4tag
This commit is contained in:
5
go.mod
5
go.mod
@ -1,6 +1,8 @@
|
|||||||
module main
|
module main
|
||||||
|
|
||||||
go 1.17
|
go 1.21.6
|
||||||
|
|
||||||
|
toolchain go1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Eyevinn/mp4ff v0.46.0
|
github.com/Eyevinn/mp4ff v0.46.0
|
||||||
@ -11,6 +13,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Sorrow446/go-mp4tag v0.0.0-20240130220823-68ce31d53e37 // indirect
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -1,5 +1,7 @@
|
|||||||
github.com/Eyevinn/mp4ff v0.46.0 h1:A8oJA4A3C9fDbX38jEw/26utjNdvmRmrO37tVI5pDk0=
|
github.com/Eyevinn/mp4ff v0.46.0 h1:A8oJA4A3C9fDbX38jEw/26utjNdvmRmrO37tVI5pDk0=
|
||||||
github.com/Eyevinn/mp4ff v0.46.0/go.mod h1:hJNUUqOBryLAzUW9wpCJyw2HaI+TCd2rUPhafoS5lgg=
|
github.com/Eyevinn/mp4ff v0.46.0/go.mod h1:hJNUUqOBryLAzUW9wpCJyw2HaI+TCd2rUPhafoS5lgg=
|
||||||
|
github.com/Sorrow446/go-mp4tag v0.0.0-20240130220823-68ce31d53e37 h1:6X6U2D53ITfDGiyGN+sOVm/iFveFHrFRS7icGJ+u88M=
|
||||||
|
github.com/Sorrow446/go-mp4tag v0.0.0-20240130220823-68ce31d53e37/go.mod h1:l5rVvaRUrCot83416D6xggKCeFZQAXcv02tnJslG26s=
|
||||||
github.com/abema/go-mp4 v1.3.0 h1:vr0PX0jk3E4GO1c28fNRsyZdkLwz38R+XRVncIH1XDk=
|
github.com/abema/go-mp4 v1.3.0 h1:vr0PX0jk3E4GO1c28fNRsyZdkLwz38R+XRVncIH1XDk=
|
||||||
github.com/abema/go-mp4 v1.3.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
|
github.com/abema/go-mp4 v1.3.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
|
||||||
github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
|
github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
|
||||||
|
150
main.go
150
main.go
@ -19,6 +19,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sorrow446/go-mp4tag"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
@ -28,24 +29,22 @@ import (
|
|||||||
|
|
||||||
"main/utils/runv2"
|
"main/utils/runv2"
|
||||||
"main/utils/structs"
|
"main/utils/structs"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`)
|
forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`)
|
||||||
dl_atmos bool
|
dl_atmos bool
|
||||||
dl_aac bool
|
dl_aac bool
|
||||||
dl_select bool
|
dl_select bool
|
||||||
artist_select bool
|
artist_select bool
|
||||||
alac_max *int
|
alac_max *int
|
||||||
atmos_max *int
|
atmos_max *int
|
||||||
Config structs.ConfigSet
|
aac_type *string
|
||||||
counter structs.Counter
|
Config structs.ConfigSet
|
||||||
okDict = make(map[string][]int)
|
counter structs.Counter
|
||||||
|
okDict = make(map[string][]int)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func loadConfig() error {
|
func loadConfig() error {
|
||||||
// 读取config.yaml文件内容
|
// 读取config.yaml文件内容
|
||||||
data, err := ioutil.ReadFile("config.yaml")
|
data, err := ioutil.ReadFile("config.yaml")
|
||||||
@ -86,7 +85,6 @@ func fileExists(path string) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func checkUrl(url string) (string, string) {
|
func checkUrl(url string) (string, string) {
|
||||||
pat := regexp.MustCompile(`^(?:https:\/\/(?:beta\.music|music)\.apple\.com\/(\w{2})(?:\/album|\/album\/.+))\/(?:id)?(\d[^\D]+)(?:$|\?)`)
|
pat := regexp.MustCompile(`^(?:https:\/\/(?:beta\.music|music)\.apple\.com\/(\w{2})(?:\/album|\/album\/.+))\/(?:id)?(\d[^\D]+)(?:$|\?)`)
|
||||||
matches := pat.FindAllStringSubmatch(url, -1)
|
matches := pat.FindAllStringSubmatch(url, -1)
|
||||||
@ -726,7 +724,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
ttml, err := getSongLyrics(track.ID, storefront, token, userToken)
|
ttml, err := getSongLyrics(track.ID, storefront, token, userToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to get lyrics")
|
fmt.Println("Failed to get lyrics")
|
||||||
} else if Config.LrcFormat == "ttml"{
|
} else if Config.LrcFormat == "ttml" {
|
||||||
if Config.SaveLrcFile {
|
if Config.SaveLrcFile {
|
||||||
lrc = ttml
|
lrc = ttml
|
||||||
err := writeLyrics(sanAlbumFolder, lrcFilename, lrc)
|
err := writeLyrics(sanAlbumFolder, lrcFilename, lrc)
|
||||||
@ -777,41 +775,9 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//add tags
|
|
||||||
index := trackNum - 1
|
|
||||||
tags := []string{
|
tags := []string{
|
||||||
"tool=",
|
"tool=",
|
||||||
fmt.Sprintf("lyrics=%s", lrc),
|
fmt.Sprintf("lyrics=%s", lrc),
|
||||||
fmt.Sprintf("title=%s", meta.Data[0].Relationships.Tracks.Data[index].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("writer=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ComposerName),
|
|
||||||
fmt.Sprintf("performer=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName),
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
if strings.Contains(albumId, "pl.") && !Config.UseSongInfoForPlaylist {
|
|
||||||
tags = append(tags, "disk=1/1")
|
|
||||||
tags = append(tags, fmt.Sprintf("track=%d", trackNum))
|
|
||||||
tags = append(tags, fmt.Sprintf("tracknum=%d/%d", trackNum, trackTotal))
|
|
||||||
tags = append(tags, fmt.Sprintf("album=%s", meta.Data[0].Attributes.Name))
|
|
||||||
} else {
|
|
||||||
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=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.AlbumName))
|
|
||||||
}
|
|
||||||
if track.Attributes.ContentRating == "explicit" {
|
|
||||||
tags = append(tags, "rating=1")
|
|
||||||
} else if track.Attributes.ContentRating == "clean" {
|
|
||||||
tags = append(tags, "rating=2")
|
|
||||||
} else {
|
|
||||||
tags = append(tags, "rating=0")
|
|
||||||
}
|
}
|
||||||
if Config.EmbedCover {
|
if Config.EmbedCover {
|
||||||
if strings.Contains(albumId, "pl.") && Config.DlAlbumcoverForPlaylist {
|
if strings.Contains(albumId, "pl.") && Config.DlAlbumcoverForPlaylist {
|
||||||
@ -838,6 +804,8 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err = writeMP4Tags(trackPath, meta, trackNum, trackTotal)
|
||||||
|
|
||||||
counter.Success++
|
counter.Success++
|
||||||
okDict[albumId] = append(okDict[albumId], trackNum)
|
okDict[albumId] = append(okDict[albumId], trackNum)
|
||||||
}
|
}
|
||||||
@ -845,6 +813,82 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeMP4Tags(trackPath string, meta *structs.AutoGenerated, trackNum, trackTotal int) error {
|
||||||
|
index := trackNum - 1
|
||||||
|
artistID, err := strconv.ParseUint(meta.Data[0].Relationships.Tracks.Data[index].Relationships.Artists.Data[0].ID, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
albumID, err := strconv.ParseUint(meta.Data[0].ID, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
releaseYear, err := strconv.ParseUint(meta.Data[0].Attributes.ReleaseDate[:4], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t := &mp4tag.MP4Tags{
|
||||||
|
Title: meta.Data[0].Relationships.Tracks.Data[index].Attributes.Name,
|
||||||
|
TitleSort: meta.Data[0].Relationships.Tracks.Data[index].Attributes.Name,
|
||||||
|
AlbumArtist: meta.Data[0].Attributes.ArtistName,
|
||||||
|
AlbumArtistSort: meta.Data[0].Attributes.ArtistName,
|
||||||
|
Artist: meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName,
|
||||||
|
ArtistSort: meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName,
|
||||||
|
Custom: map[string]string{
|
||||||
|
"PERFORMER": meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName,
|
||||||
|
"RELEASETIME": meta.Data[0].Relationships.Tracks.Data[index].Attributes.ReleaseDate,
|
||||||
|
"ISRC": meta.Data[0].Relationships.Tracks.Data[index].Attributes.Isrc,
|
||||||
|
"LABEL": meta.Data[0].Attributes.RecordLabel,
|
||||||
|
"UPC": meta.Data[0].Attributes.Upc,
|
||||||
|
},
|
||||||
|
ItunesAlbumID: int32(albumID),
|
||||||
|
Composer: meta.Data[0].Relationships.Tracks.Data[index].Attributes.ComposerName,
|
||||||
|
ComposerSort: meta.Data[0].Relationships.Tracks.Data[index].Attributes.ComposerName,
|
||||||
|
Date: meta.Data[0].Attributes.ReleaseDate,
|
||||||
|
CustomGenre: meta.Data[0].Relationships.Tracks.Data[index].Attributes.GenreNames[0],
|
||||||
|
Copyright: meta.Data[0].Attributes.Copyright,
|
||||||
|
Publisher: meta.Data[0].Attributes.RecordLabel,
|
||||||
|
ItunesArtistID: int32(artistID),
|
||||||
|
Year: int32(releaseYear),
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(meta.Data[0].ID, "pl.") && !Config.UseSongInfoForPlaylist {
|
||||||
|
t.DiscNumber = 1
|
||||||
|
t.DiscTotal = 1
|
||||||
|
t.TrackNumber = int16(trackNum)
|
||||||
|
t.TrackTotal = int16(trackTotal)
|
||||||
|
t.Album = meta.Data[0].Attributes.Name
|
||||||
|
t.AlbumSort = meta.Data[0].Attributes.Name
|
||||||
|
} else {
|
||||||
|
t.DiscNumber = int16(meta.Data[0].Relationships.Tracks.Data[index].Attributes.DiscNumber)
|
||||||
|
t.DiscTotal = int16(meta.Data[0].Relationships.Tracks.Data[trackTotal-1].Attributes.DiscNumber)
|
||||||
|
t.TrackNumber = int16(meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber)
|
||||||
|
t.TrackTotal = int16(trackTotal)
|
||||||
|
t.Album = meta.Data[0].Relationships.Tracks.Data[index].Attributes.AlbumName
|
||||||
|
t.AlbumSort = meta.Data[0].Relationships.Tracks.Data[index].Attributes.AlbumName
|
||||||
|
}
|
||||||
|
|
||||||
|
if meta.Data[0].Relationships.Tracks.Data[index].Attributes.ContentRating == "explicit" {
|
||||||
|
t.ItunesAdvisory = mp4tag.ItunesAdvisoryExplicit
|
||||||
|
} else if meta.Data[0].Relationships.Tracks.Data[index].Attributes.ContentRating == "clean" {
|
||||||
|
t.ItunesAdvisory = mp4tag.ItunesAdvisoryClean
|
||||||
|
} else {
|
||||||
|
t.ItunesAdvisory = mp4tag.ItunesAdvisoryNone
|
||||||
|
}
|
||||||
|
|
||||||
|
mp4, err := mp4tag.Open(trackPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer mp4.Close()
|
||||||
|
err = mp4.Write(t, []string{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := loadConfig()
|
err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -865,8 +909,9 @@ func main() {
|
|||||||
pflag.BoolVar(&dl_aac, "aac", false, "Enable adm-aac download mode")
|
pflag.BoolVar(&dl_aac, "aac", false, "Enable adm-aac download mode")
|
||||||
pflag.BoolVar(&dl_select, "select", false, "Enable selective download")
|
pflag.BoolVar(&dl_select, "select", false, "Enable selective download")
|
||||||
pflag.BoolVar(&artist_select, "all-album", false, "Download all artist albums")
|
pflag.BoolVar(&artist_select, "all-album", false, "Download all artist albums")
|
||||||
alac_max = pflag.Int("alac-max", -1, "Specify the max quality for download alac")
|
alac_max = pflag.Int("alac-max", Config.AlacMax, "Specify the max quality for download alac")
|
||||||
atmos_max = pflag.Int("atmos-max", -1, "Specify the max quality for download atmos")
|
atmos_max = pflag.Int("atmos-max", Config.AtmosMax, "Specify the max quality for download atmos")
|
||||||
|
aac_type = pflag.String("aac-type", Config.AacType, "Select AAC type, aac aac-binaural aac-downmix")
|
||||||
|
|
||||||
// Custom usage message for help
|
// Custom usage message for help
|
||||||
pflag.Usage = func() {
|
pflag.Usage = func() {
|
||||||
@ -874,16 +919,13 @@ func main() {
|
|||||||
fmt.Println("Options:")
|
fmt.Println("Options:")
|
||||||
pflag.PrintDefaults()
|
pflag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
Config.AlacMax = *alac_max
|
||||||
|
Config.AtmosMax = *atmos_max
|
||||||
|
Config.AacType = *aac_type
|
||||||
|
|
||||||
// Parse the flag arguments
|
// Parse the flag arguments
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
|
|
||||||
if *alac_max != -1 {
|
|
||||||
Config.AlacMax = *alac_max
|
|
||||||
}
|
|
||||||
if *atmos_max != -1 {
|
|
||||||
Config.AtmosMax = *atmos_max
|
|
||||||
}
|
|
||||||
args := pflag.Args()
|
args := pflag.Args()
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
fmt.Println("No URLs provided. Please provide at least one URL.")
|
fmt.Println("No URLs provided. Please provide at least one URL.")
|
||||||
|
Reference in New Issue
Block a user