add atmos and select to main.go
--atmos,--select
This commit is contained in:
parent
af1a310cb6
commit
c1c62ab94c
539
main.go
539
main.go
@ -37,6 +37,8 @@ const (
|
|||||||
var (
|
var (
|
||||||
forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`)
|
forbiddenNames = regexp.MustCompile(`[/\\<>:"|?*]`)
|
||||||
)
|
)
|
||||||
|
var dl_atmos = false
|
||||||
|
var dl_select = false
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
MediaUserToken string `yaml:"media-user-token"`
|
MediaUserToken string `yaml:"media-user-token"`
|
||||||
@ -63,6 +65,7 @@ type Config struct {
|
|||||||
GetM3u8Port string `yaml:"get-m3u8-port"`
|
GetM3u8Port string `yaml:"get-m3u8-port"`
|
||||||
GetM3u8FromDevice bool `yaml:"get-m3u8-from-device"`
|
GetM3u8FromDevice bool `yaml:"get-m3u8-from-device"`
|
||||||
AlacMax int `yaml:"alac-max"`
|
AlacMax int `yaml:"alac-max"`
|
||||||
|
AtmosMax int `yaml:"atmos-max"`
|
||||||
UseSongInfoForPlaylist bool `yaml:"use-songinfo-for-playlist"`
|
UseSongInfoForPlaylist bool `yaml:"use-songinfo-for-playlist"`
|
||||||
DlAlbumcoverForPlaylist bool `yaml:"dl-albumcover-for-playlist"`
|
DlAlbumcoverForPlaylist bool `yaml:"dl-albumcover-for-playlist"`
|
||||||
}
|
}
|
||||||
@ -109,6 +112,15 @@ func (*Alac) GetType() mp4.BoxType {
|
|||||||
return BoxTypeAlac()
|
return BoxTypeAlac()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isInArray(arr []int, target int) bool {
|
||||||
|
for _, num := range arr {
|
||||||
|
if num == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func fileExists(path string) (bool, error) {
|
func fileExists(path string) (bool, error) {
|
||||||
f, err := os.Stat(path)
|
f, err := os.Stat(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -1009,6 +1021,14 @@ func decryptSong(info *SongInfo, keys []string, manifest *AutoGenerated, filenam
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer create.Close()
|
defer create.Close()
|
||||||
|
if dl_atmos {
|
||||||
|
_, err = create.Write(decrypted)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return writeM4a(mp4.NewWriter(create), info, manifest, decrypted, trackNum, trackTotal)
|
return writeM4a(mp4.NewWriter(create), info, manifest, decrypted, trackNum, trackTotal)
|
||||||
}
|
}
|
||||||
@ -1304,6 +1324,12 @@ func writeLyrics(sanAlbumFolder, filename string, lrc string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rip(albumId string, token string, storefront string, userToken string) error {
|
func rip(albumId string, token string, storefront string, userToken string) error {
|
||||||
|
var Codec string
|
||||||
|
if dl_atmos {
|
||||||
|
Codec = "Atmos"
|
||||||
|
} else {
|
||||||
|
Codec = "ALAC"
|
||||||
|
}
|
||||||
meta, err := getMeta(albumId, token, storefront)
|
meta, err := getMeta(albumId, token, storefront)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to get album metadata.\n")
|
fmt.Println("Failed to get album metadata.\n")
|
||||||
@ -1336,20 +1362,24 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
singerFolder := filepath.Join(config.AlacSaveFolder, forbiddenNames.ReplaceAllString(singerFoldername, "_"))
|
singerFolder := filepath.Join(config.AlacSaveFolder, forbiddenNames.ReplaceAllString(singerFoldername, "_"))
|
||||||
var Quality string
|
var Quality string
|
||||||
if strings.Contains(config.AlbumFolderFormat, "Quality") {
|
if strings.Contains(config.AlbumFolderFormat, "Quality") {
|
||||||
manifest1, err := getInfoFromAdam(meta.Data[0].Relationships.Tracks.Data[0].ID, token, storefront)
|
if dl_atmos {
|
||||||
if err != nil {
|
Quality = fmt.Sprintf("%dkbps", config.AtmosMax-2000)
|
||||||
fmt.Println("Failed to get manifest.\n", err)
|
|
||||||
} else {
|
} else {
|
||||||
if manifest1.Attributes.ExtendedAssetUrls.EnhancedHls == "" {
|
manifest1, err := getInfoFromAdam(meta.Data[0].Relationships.Tracks.Data[0].ID, token, storefront)
|
||||||
fmt.Println("Unavailable in ALAC.\n")
|
if err != nil {
|
||||||
|
fmt.Println("Failed to get manifest.\n", err)
|
||||||
} else {
|
} else {
|
||||||
EnhancedHls_m3u8, err := checkM3u8(meta.Data[0].Relationships.Tracks.Data[0].ID, "album")
|
if manifest1.Attributes.ExtendedAssetUrls.EnhancedHls == "" {
|
||||||
if strings.HasPrefix(EnhancedHls_m3u8, "http") {
|
fmt.Println("Unavailable.\n")
|
||||||
manifest1.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8
|
} else {
|
||||||
}
|
EnhancedHls_m3u8, err := checkM3u8(meta.Data[0].Relationships.Tracks.Data[0].ID, "album")
|
||||||
Quality, err = extractMediaQuality(manifest1.Attributes.ExtendedAssetUrls.EnhancedHls)
|
if strings.HasPrefix(EnhancedHls_m3u8, "http") {
|
||||||
if err != nil {
|
manifest1.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8
|
||||||
fmt.Println("Failed to extract quality from manifest.\n", err)
|
}
|
||||||
|
Quality, err = extractMediaQuality(manifest1.Attributes.ExtendedAssetUrls.EnhancedHls)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to extract quality from manifest.\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1378,7 +1408,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
"{PlaylistName}", meta.Data[0].Attributes.Name,
|
"{PlaylistName}", meta.Data[0].Attributes.Name,
|
||||||
"{PlaylistId}", albumId,
|
"{PlaylistId}", albumId,
|
||||||
"{Quality}", Quality,
|
"{Quality}", Quality,
|
||||||
"{Codec}", "ALAC",
|
"{Codec}", Codec,
|
||||||
"{Tag}", Tag_string,
|
"{Tag}", Tag_string,
|
||||||
).Replace(config.PlaylistFolderFormat)
|
).Replace(config.PlaylistFolderFormat)
|
||||||
} else {
|
} else {
|
||||||
@ -1392,7 +1422,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
"{Copyright}", meta.Data[0].Attributes.Copyright,
|
"{Copyright}", meta.Data[0].Attributes.Copyright,
|
||||||
"{AlbumId}", albumId,
|
"{AlbumId}", albumId,
|
||||||
"{Quality}", Quality,
|
"{Quality}", Quality,
|
||||||
"{Codec}", "ALAC",
|
"{Codec}", Codec,
|
||||||
"{Tag}", Tag_string,
|
"{Tag}", Tag_string,
|
||||||
).Replace(config.AlbumFolderFormat)
|
).Replace(config.AlbumFolderFormat)
|
||||||
}
|
}
|
||||||
@ -1448,157 +1478,260 @@ func rip(albumId string, token string, storefront string, userToken string) erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
arr := make([]int, trackTotal)
|
||||||
trackNum++
|
for i := 0; i < trackTotal; i++ {
|
||||||
trackTotalnum += 1
|
arr[i] = i + 1
|
||||||
fmt.Printf("Track %d of %d:\n", trackNum, trackTotal)
|
}
|
||||||
manifest, err := getInfoFromAdam(track.ID, token, storefront)
|
selected := []int{}
|
||||||
|
|
||||||
|
if !dl_select {
|
||||||
|
selected = arr
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fmt.Print("select: ")
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
input, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to get manifest.\n", err)
|
fmt.Println(err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if manifest.Attributes.ExtendedAssetUrls.EnhancedHls == "" {
|
input = strings.TrimSpace(input)
|
||||||
fmt.Println("Unavailable in ALAC.")
|
inputs := strings.Fields(input)
|
||||||
continue
|
|
||||||
}
|
for _, str := range inputs {
|
||||||
EnhancedHls_m3u8, err := checkM3u8(track.ID, "song")
|
num, err := strconv.Atoi(str)
|
||||||
if strings.HasPrefix(EnhancedHls_m3u8, "http") {
|
|
||||||
manifest.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8
|
|
||||||
}
|
|
||||||
var Quality string
|
|
||||||
if strings.Contains(config.SongFileFormat, "Quality") {
|
|
||||||
Quality, err = extractMediaQuality(manifest.Attributes.ExtendedAssetUrls.EnhancedHls)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to extract quality from manifest.\n", err)
|
fmt.Printf("wrong '%s', skip...\n", str)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
stringsToJoin := []string{}
|
|
||||||
if track.Attributes.IsAppleDigitalMaster {
|
|
||||||
if config.AppleMasterChoice != "" {
|
|
||||||
stringsToJoin = append(stringsToJoin, config.AppleMasterChoice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if track.Attributes.ContentRating == "explicit" {
|
|
||||||
if config.ExplicitChoice != "" {
|
|
||||||
stringsToJoin = append(stringsToJoin, config.ExplicitChoice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if track.Attributes.ContentRating == "clean" {
|
|
||||||
if config.CleanChoice != "" {
|
|
||||||
stringsToJoin = append(stringsToJoin, config.CleanChoice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Tag_string := strings.Join(stringsToJoin, " ")
|
|
||||||
|
|
||||||
songName := strings.NewReplacer(
|
found := false
|
||||||
"{SongId}", track.ID,
|
for i := 0; i < len(arr); i++ {
|
||||||
"{SongNumer}", fmt.Sprintf("%02d", trackNum),
|
if arr[i] == num {
|
||||||
"{SongName}", track.Attributes.Name,
|
selected = append(selected, num)
|
||||||
"{DiscNumber}", fmt.Sprintf("%0d", track.Attributes.DiscNumber),
|
found = true
|
||||||
"{TrackNumber}", fmt.Sprintf("%0d", track.Attributes.TrackNumber),
|
break
|
||||||
"{Quality}", Quality,
|
}
|
||||||
"{Tag}", Tag_string,
|
}
|
||||||
"{Codec}", "ALAC",
|
|
||||||
).Replace(config.SongFileFormat)
|
if !found {
|
||||||
fmt.Println(songName)
|
fmt.Printf("Option '%d' not found or already selected, skipping...\n", num)
|
||||||
filename := fmt.Sprintf("%s.m4a", forbiddenNames.ReplaceAllString(songName, "_"))
|
}
|
||||||
lrcFilename := fmt.Sprintf("%s.lrc", forbiddenNames.ReplaceAllString(songName, "_"))
|
}
|
||||||
trackPath := filepath.Join(sanAlbumFolder, filename)
|
|
||||||
var lrc string = ""
|
fmt.Println("Selected options:", selected)
|
||||||
if userToken != "your-media-user-token" && (config.EmbedLrc || config.SaveLrcFile) {
|
}
|
||||||
ttml, err := getSongLyrics(track.ID, storefront, token, userToken)
|
for trackNum, track := range meta.Data[0].Relationships.Tracks.Data {
|
||||||
|
trackNum++
|
||||||
|
if isInArray(selected, trackNum) {
|
||||||
|
trackTotalnum += 1
|
||||||
|
fmt.Printf("Track %d of %d:\n", trackNum, trackTotal)
|
||||||
|
manifest, err := getInfoFromAdam(track.ID, token, storefront)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to get lyrics")
|
fmt.Println("Failed to get manifest.\n", err)
|
||||||
} else {
|
continue
|
||||||
lrc, err = conventTTMLToLRC(ttml)
|
}
|
||||||
if err != nil {
|
if manifest.Attributes.ExtendedAssetUrls.EnhancedHls == "" {
|
||||||
fmt.Printf("Failed to parse lyrics: %s \n", err)
|
fmt.Println("Unavailable.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
EnhancedHls_m3u8, err := checkM3u8(track.ID, "song")
|
||||||
|
if strings.HasPrefix(EnhancedHls_m3u8, "http") {
|
||||||
|
manifest.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8
|
||||||
|
}
|
||||||
|
var Quality string
|
||||||
|
if strings.Contains(config.SongFileFormat, "Quality") {
|
||||||
|
if dl_atmos {
|
||||||
|
Quality = fmt.Sprintf("%dkbps", config.AtmosMax-2000)
|
||||||
} else {
|
} else {
|
||||||
if config.SaveLrcFile {
|
Quality, err = extractMediaQuality(manifest.Attributes.ExtendedAssetUrls.EnhancedHls)
|
||||||
err := writeLyrics(sanAlbumFolder, lrcFilename, lrc)
|
if err != nil {
|
||||||
if err != nil {
|
fmt.Println("Failed to extract quality from manifest.\n", err)
|
||||||
fmt.Printf("Failed to write lyrics")
|
continue
|
||||||
}
|
}
|
||||||
if !config.EmbedLrc {
|
}
|
||||||
lrc = ""
|
}
|
||||||
|
stringsToJoin := []string{}
|
||||||
|
if track.Attributes.IsAppleDigitalMaster {
|
||||||
|
if config.AppleMasterChoice != "" {
|
||||||
|
stringsToJoin = append(stringsToJoin, config.AppleMasterChoice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if track.Attributes.ContentRating == "explicit" {
|
||||||
|
if config.ExplicitChoice != "" {
|
||||||
|
stringsToJoin = append(stringsToJoin, config.ExplicitChoice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if track.Attributes.ContentRating == "clean" {
|
||||||
|
if config.CleanChoice != "" {
|
||||||
|
stringsToJoin = append(stringsToJoin, config.CleanChoice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag_string := strings.Join(stringsToJoin, " ")
|
||||||
|
|
||||||
|
songName := strings.NewReplacer(
|
||||||
|
"{SongId}", track.ID,
|
||||||
|
"{SongNumer}", fmt.Sprintf("%02d", trackNum),
|
||||||
|
"{SongName}", track.Attributes.Name,
|
||||||
|
"{DiscNumber}", fmt.Sprintf("%0d", track.Attributes.DiscNumber),
|
||||||
|
"{TrackNumber}", fmt.Sprintf("%0d", track.Attributes.TrackNumber),
|
||||||
|
"{Quality}", Quality,
|
||||||
|
"{Tag}", Tag_string,
|
||||||
|
"{Codec}", Codec,
|
||||||
|
).Replace(config.SongFileFormat)
|
||||||
|
fmt.Println(songName)
|
||||||
|
filename := fmt.Sprintf("%s.m4a", forbiddenNames.ReplaceAllString(songName, "_"))
|
||||||
|
if dl_atmos {
|
||||||
|
filename = fmt.Sprintf("%s.ec3", forbiddenNames.ReplaceAllString(songName, "_"))
|
||||||
|
}
|
||||||
|
m4afilename := fmt.Sprintf("%s.m4a", forbiddenNames.ReplaceAllString(songName, "_"))
|
||||||
|
lrcFilename := fmt.Sprintf("%s.lrc", forbiddenNames.ReplaceAllString(songName, "_"))
|
||||||
|
trackPath := filepath.Join(sanAlbumFolder, filename)
|
||||||
|
m4atrackPath := filepath.Join(sanAlbumFolder, m4afilename)
|
||||||
|
var lrc string = ""
|
||||||
|
if userToken != "your-media-user-token" && (config.EmbedLrc || config.SaveLrcFile) {
|
||||||
|
ttml, err := getSongLyrics(track.ID, storefront, token, userToken)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to get lyrics")
|
||||||
|
} else {
|
||||||
|
lrc, err = conventTTMLToLRC(ttml)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse lyrics: %s \n", err)
|
||||||
|
} else {
|
||||||
|
if config.SaveLrcFile {
|
||||||
|
err := writeLyrics(sanAlbumFolder, lrcFilename, lrc)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to write lyrics")
|
||||||
|
}
|
||||||
|
if !config.EmbedLrc {
|
||||||
|
lrc = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
exists, err := fileExists(trackPath)
|
||||||
exists, err := fileExists(trackPath)
|
if err != nil {
|
||||||
if err != nil {
|
fmt.Println("Failed to check if track exists.")
|
||||||
fmt.Println("Failed to check if track exists.")
|
|
||||||
}
|
|
||||||
if exists {
|
|
||||||
fmt.Println("Track already exists locally.")
|
|
||||||
oktrackNum += 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
trackUrl, keys, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to extract info from manifest.\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
info, err := extractSong(trackUrl)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to extract track.", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
samplesOk := true
|
|
||||||
for samplesOk {
|
|
||||||
for _, i := range info.samples {
|
|
||||||
if int(i.descIndex) >= len(keys) {
|
|
||||||
fmt.Println("Decryption size mismatch.")
|
|
||||||
samplesOk = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break
|
if exists {
|
||||||
}
|
fmt.Println("Track already exists locally.")
|
||||||
if !samplesOk {
|
oktrackNum += 1
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = decryptSong(info, keys, meta, trackPath, trackNum, trackTotal)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to decrypt track.\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tags := []string{
|
|
||||||
fmt.Sprintf("lyrics=%s", lrc),
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
err = writeCover(sanAlbumFolder, track.ID, track.Attributes.Artwork.URL)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to write cover.")
|
|
||||||
}
|
|
||||||
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", sanAlbumFolder, track.ID, config.CoverFormat))
|
|
||||||
} else {
|
|
||||||
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", sanAlbumFolder, "cover", config.CoverFormat))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tagsString := strings.Join(tags, ":")
|
|
||||||
cmd := exec.Command("MP4Box", "-itags", tagsString, trackPath)
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
fmt.Printf("Embed failed: %v\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.Contains(albumId, "pl.") && config.DlAlbumcoverForPlaylist {
|
|
||||||
if err := os.Remove(fmt.Sprintf("%s/%s.%s", sanAlbumFolder, track.ID, config.CoverFormat)); err != nil {
|
|
||||||
fmt.Printf("Error deleting file: %s/%s.%s\n", sanAlbumFolder, track.ID, config.CoverFormat)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
m4aexists, err := fileExists(m4atrackPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to check if track exists.")
|
||||||
|
}
|
||||||
|
if m4aexists {
|
||||||
|
fmt.Println("Track already exists locally.")
|
||||||
|
oktrackNum += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trackUrl, keys, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to extract info from manifest.\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info, err := extractSong(trackUrl)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to extract track.", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
samplesOk := true
|
||||||
|
for samplesOk {
|
||||||
|
for _, i := range info.samples {
|
||||||
|
if int(i.descIndex) >= len(keys) {
|
||||||
|
fmt.Println("Decryption size mismatch.")
|
||||||
|
samplesOk = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !samplesOk {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = decryptSong(info, keys, meta, trackPath, trackNum, trackTotal)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to decrypt track.\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tags := []string{
|
||||||
|
fmt.Sprintf("lyrics=%s", lrc),
|
||||||
|
}
|
||||||
|
|
||||||
|
index := trackNum - 1
|
||||||
|
if dl_atmos {
|
||||||
|
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].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=%s", 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=%s", 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 {
|
||||||
|
err = writeCover(sanAlbumFolder, track.ID, track.Attributes.Artwork.URL)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to write cover.")
|
||||||
|
}
|
||||||
|
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", sanAlbumFolder, track.ID, config.CoverFormat))
|
||||||
|
} else {
|
||||||
|
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", sanAlbumFolder, "cover", config.CoverFormat))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tagsString := strings.Join(tags, ":")
|
||||||
|
cmd := exec.Command("MP4Box", "-itags", tagsString, trackPath)
|
||||||
|
if dl_atmos {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
fmt.Printf("Embed failed: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(albumId, "pl.") && config.DlAlbumcoverForPlaylist {
|
||||||
|
if err := os.Remove(fmt.Sprintf("%s/%s.%s", sanAlbumFolder, track.ID, config.CoverFormat)); err != nil {
|
||||||
|
fmt.Printf("Error deleting file: %s/%s.%s\n", sanAlbumFolder, track.ID, config.CoverFormat)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dl_atmos {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
oktrackNum += 1
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1614,6 +1747,17 @@ func main() {
|
|||||||
fmt.Println("Failed to get token.")
|
fmt.Println("Failed to get token.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var dlArgs []string
|
||||||
|
for _, arg := range os.Args {
|
||||||
|
if strings.Contains(arg, "--atmos") {
|
||||||
|
dl_atmos = true
|
||||||
|
} else if strings.Contains(arg, "--select") {
|
||||||
|
dl_select = true
|
||||||
|
} else {
|
||||||
|
dlArgs = append(dlArgs, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Args = dlArgs
|
||||||
if strings.Contains(os.Args[1], "/artist/") {
|
if strings.Contains(os.Args[1], "/artist/") {
|
||||||
newArgs, err := checkArtist(os.Args[1], token)
|
newArgs, err := checkArtist(os.Args[1], token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1882,26 +2026,46 @@ func extractMedia(b string) (string, []string, error) {
|
|||||||
return master.Variants[i].AverageBandwidth > master.Variants[j].AverageBandwidth
|
return master.Variants[i].AverageBandwidth > master.Variants[j].AverageBandwidth
|
||||||
})
|
})
|
||||||
for _, variant := range master.Variants {
|
for _, variant := range master.Variants {
|
||||||
if variant.Codecs == "alac" {
|
if dl_atmos {
|
||||||
split := strings.Split(variant.Audio, "-")
|
if variant.Codecs == "ec-3" {
|
||||||
length := len(split)
|
split := strings.Split(variant.Audio, "-")
|
||||||
length_int, err := strconv.Atoi(split[length-2])
|
length := len(split)
|
||||||
if err != nil {
|
length_int, err := strconv.Atoi(split[length-1])
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
if length_int <= config.AlacMax {
|
|
||||||
fmt.Printf("%s-bit / %s Hz\n", split[length-1], split[length-2])
|
|
||||||
streamUrlTemp, err := masterUrl.Parse(variant.URI)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", nil, err
|
||||||
|
}
|
||||||
|
if length_int <= config.AtmosMax {
|
||||||
|
fmt.Printf("%s\n", variant.Audio)
|
||||||
|
streamUrlTemp, err := masterUrl.Parse(variant.URI)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
streamUrl = streamUrlTemp
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if variant.Codecs == "alac" {
|
||||||
|
split := strings.Split(variant.Audio, "-")
|
||||||
|
length := len(split)
|
||||||
|
length_int, err := strconv.Atoi(split[length-2])
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
if length_int <= config.AlacMax {
|
||||||
|
fmt.Printf("%s-bit / %s Hz\n", split[length-1], split[length-2])
|
||||||
|
streamUrlTemp, err := masterUrl.Parse(variant.URI)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
streamUrl = streamUrlTemp
|
||||||
|
break
|
||||||
}
|
}
|
||||||
streamUrl = streamUrlTemp
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if streamUrl == nil {
|
if streamUrl == nil {
|
||||||
return "", nil, errors.New("no alac codec found")
|
return "", nil, errors.New("no codec found")
|
||||||
}
|
}
|
||||||
var keys []string
|
var keys []string
|
||||||
keys = append(keys, prefetchKey)
|
keys = append(keys, prefetchKey)
|
||||||
@ -1909,8 +2073,14 @@ func extractMedia(b string) (string, []string, error) {
|
|||||||
regex := regexp.MustCompile(`"(skd?://[^"]*)"`)
|
regex := regexp.MustCompile(`"(skd?://[^"]*)"`)
|
||||||
matches := regex.FindAllStringSubmatch(masterString, -1)
|
matches := regex.FindAllStringSubmatch(masterString, -1)
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
if strings.HasSuffix(match[1], "c23") || strings.HasSuffix(match[1], "c6") {
|
if dl_atmos {
|
||||||
keys = append(keys, match[1])
|
if strings.HasSuffix(match[1], "c24") || strings.HasSuffix(match[1], "c6") {
|
||||||
|
keys = append(keys, match[1])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if strings.HasSuffix(match[1], "c23") || strings.HasSuffix(match[1], "c6") {
|
||||||
|
keys = append(keys, match[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return streamUrl.String(), keys, nil
|
return streamUrl.String(), keys, nil
|
||||||
@ -1992,23 +2162,30 @@ func extractSong(url string) (*SongInfo, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
enca, err := mp4.ExtractBoxWithPayload(f, stbl[0], []mp4.BoxType{
|
var extracted *SongInfo
|
||||||
mp4.BoxTypeStsd(),
|
if !dl_atmos {
|
||||||
mp4.BoxTypeEnca(),
|
enca, err := mp4.ExtractBoxWithPayload(f, stbl[0], []mp4.BoxType{
|
||||||
})
|
mp4.BoxTypeStsd(),
|
||||||
if err != nil {
|
mp4.BoxTypeEnca(),
|
||||||
return nil, err
|
})
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
aalac, err := mp4.ExtractBoxWithPayload(f, &enca[0].Info,
|
aalac, err := mp4.ExtractBoxWithPayload(f, &enca[0].Info,
|
||||||
[]mp4.BoxType{BoxTypeAlac()})
|
[]mp4.BoxType{BoxTypeAlac()})
|
||||||
if err != nil || len(aalac) != 1 {
|
if err != nil || len(aalac) != 1 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
extracted = &SongInfo{
|
||||||
extracted := &SongInfo{
|
r: f,
|
||||||
r: f,
|
alacParam: aalac[0].Payload.(*Alac),
|
||||||
alacParam: aalac[0].Payload.(*Alac),
|
}
|
||||||
|
} else {
|
||||||
|
extracted = &SongInfo{
|
||||||
|
r: f,
|
||||||
|
// alacParam: aalac[0].Payload.(*Alac),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
moofs, err := mp4.ExtractBox(f, nil, []mp4.BoxType{
|
moofs, err := mp4.ExtractBox(f, nil, []mp4.BoxType{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user