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
|
||||
|
||||
go 1.17
|
||||
go 1.21.6
|
||||
|
||||
toolchain go1.22.2
|
||||
|
||||
require (
|
||||
github.com/Eyevinn/mp4ff v0.46.0
|
||||
@ -11,6 +13,7 @@ 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/rivo/uniseg v0.4.7 // 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/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/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
|
||||
github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
|
||||
|
150
main.go
150
main.go
@ -19,6 +19,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sorrow446/go-mp4tag"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
@ -28,24 +29,22 @@ import (
|
||||
|
||||
"main/utils/runv2"
|
||||
"main/utils/structs"
|
||||
|
||||
)
|
||||
|
||||
var (
|
||||
forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`)
|
||||
dl_atmos bool
|
||||
dl_aac bool
|
||||
dl_select bool
|
||||
artist_select bool
|
||||
alac_max *int
|
||||
atmos_max *int
|
||||
Config structs.ConfigSet
|
||||
counter structs.Counter
|
||||
okDict = make(map[string][]int)
|
||||
dl_atmos bool
|
||||
dl_aac bool
|
||||
dl_select bool
|
||||
artist_select bool
|
||||
alac_max *int
|
||||
atmos_max *int
|
||||
aac_type *string
|
||||
Config structs.ConfigSet
|
||||
counter structs.Counter
|
||||
okDict = make(map[string][]int)
|
||||
)
|
||||
|
||||
|
||||
|
||||
func loadConfig() error {
|
||||
// 读取config.yaml文件内容
|
||||
data, err := ioutil.ReadFile("config.yaml")
|
||||
@ -86,7 +85,6 @@ func fileExists(path string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
||||
func checkUrl(url string) (string, string) {
|
||||
pat := regexp.MustCompile(`^(?:https:\/\/(?:beta\.music|music)\.apple\.com\/(\w{2})(?:\/album|\/album\/.+))\/(?:id)?(\d[^\D]+)(?:$|\?)`)
|
||||
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)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to get lyrics")
|
||||
} else if Config.LrcFormat == "ttml"{
|
||||
} else if Config.LrcFormat == "ttml" {
|
||||
if Config.SaveLrcFile {
|
||||
lrc = ttml
|
||||
err := writeLyrics(sanAlbumFolder, lrcFilename, lrc)
|
||||
@ -777,41 +775,9 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
//add tags
|
||||
index := trackNum - 1
|
||||
tags := []string{
|
||||
"tool=",
|
||||
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 strings.Contains(albumId, "pl.") && Config.DlAlbumcoverForPlaylist {
|
||||
@ -838,6 +804,8 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
||||
continue
|
||||
}
|
||||
}
|
||||
err = writeMP4Tags(trackPath, meta, trackNum, trackTotal)
|
||||
|
||||
counter.Success++
|
||||
okDict[albumId] = append(okDict[albumId], trackNum)
|
||||
}
|
||||
@ -845,6 +813,82 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
||||
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() {
|
||||
err := loadConfig()
|
||||
if err != nil {
|
||||
@ -865,8 +909,9 @@ func main() {
|
||||
pflag.BoolVar(&dl_aac, "aac", false, "Enable adm-aac download mode")
|
||||
pflag.BoolVar(&dl_select, "select", false, "Enable selective download")
|
||||
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")
|
||||
atmos_max = pflag.Int("atmos-max", -1, "Specify the max quality for download atmos")
|
||||
alac_max = pflag.Int("alac-max", Config.AlacMax, "Specify the max quality for download alac")
|
||||
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
|
||||
pflag.Usage = func() {
|
||||
@ -874,16 +919,13 @@ func main() {
|
||||
fmt.Println("Options:")
|
||||
pflag.PrintDefaults()
|
||||
}
|
||||
Config.AlacMax = *alac_max
|
||||
Config.AtmosMax = *atmos_max
|
||||
Config.AacType = *aac_type
|
||||
|
||||
// Parse the flag arguments
|
||||
pflag.Parse()
|
||||
|
||||
if *alac_max != -1 {
|
||||
Config.AlacMax = *alac_max
|
||||
}
|
||||
if *atmos_max != -1 {
|
||||
Config.AtmosMax = *atmos_max
|
||||
}
|
||||
args := pflag.Args()
|
||||
if len(args) == 0 {
|
||||
fmt.Println("No URLs provided. Please provide at least one URL.")
|
||||
|
Reference in New Issue
Block a user