|
|
@ -53,6 +53,8 @@ type Config struct { |
|
|
|
CleanChoice string `yaml:"clean-choice"` |
|
|
|
CleanChoice string `yaml:"clean-choice"` |
|
|
|
AppleMasterChoice string `yaml:"apple-master-choice"` |
|
|
|
AppleMasterChoice string `yaml:"apple-master-choice"` |
|
|
|
AlacMax int `yaml:"alac-max"` |
|
|
|
AlacMax int `yaml:"alac-max"` |
|
|
|
|
|
|
|
UseSongInfoForPlaylist bool `yaml:"use-songinfo-for-playlist"` |
|
|
|
|
|
|
|
DlAlbumcoverForPlaylist bool `yaml:"dl-albumcover-for-playlist"` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var config Config |
|
|
|
var config Config |
|
|
@ -679,7 +681,22 @@ func writeM4a(w *mp4.Writer, info *SongInfo, meta *AutoGenerated, data []byte, t |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'\251', 'a', 'l', 'b'}, meta.Data[0].Attributes.Name) |
|
|
|
err = addMeta(mp4.BoxType{'s', 'o', 'n', 'm'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.Name) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
AlbumName:=meta.Data[0].Relationships.Tracks.Data[index].Attributes.AlbumName |
|
|
|
|
|
|
|
if strings.Contains(meta.Data[0].ID, "pl."){ |
|
|
|
|
|
|
|
if !config.UseSongInfoForPlaylist { |
|
|
|
|
|
|
|
AlbumName=meta.Data[0].Attributes.Name |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'\251', 'a', 'l', 'b'}, AlbumName) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'s', 'o', 'a', 'l'}, AlbumName) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
@ -689,25 +706,45 @@ func writeM4a(w *mp4.Writer, info *SongInfo, meta *AutoGenerated, data []byte, t |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'s', 'o', 'a', 'r'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'\251', 'p', 'r', 'f'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = addExtendedMeta("PERFORMER", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'\251', 'w', 'r', 't'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.ComposerName) |
|
|
|
err = addMeta(mp4.BoxType{'\251', 'w', 'r', 't'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.ComposerName) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'\251', 'd', 'a', 'y'}, strings.Split(meta.Data[0].Attributes.ReleaseDate, "-")[0]) |
|
|
|
err = addMeta(mp4.BoxType{'s', 'o', 'c', 'o'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.ComposerName) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// cnID, err := strconv.ParseUint(meta.Data[0].Relationships.Tracks.Data[index].ID, 10, 32)
|
|
|
|
err = addMeta(mp4.BoxType{'\251', 'd', 'a', 'y'}, meta.Data[0].Attributes.ReleaseDate) |
|
|
|
// if err != nil {
|
|
|
|
if err != nil { |
|
|
|
// return err
|
|
|
|
return err |
|
|
|
// }
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cnID, err := strconv.ParseUint(meta.Data[0].Relationships.Tracks.Data[index].ID, 10, 32) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// err = addMeta(mp4.BoxType{'c', 'n', 'I', 'D'}, uint32(cnID))
|
|
|
|
err = addMeta(mp4.BoxType{'c', 'n', 'I', 'D'}, uint32(cnID)) |
|
|
|
// if err != nil {
|
|
|
|
if err != nil { |
|
|
|
// return err
|
|
|
|
return err |
|
|
|
// }
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
err = addExtendedMeta("ISRC", meta.Data[0].Relationships.Tracks.Data[index].Attributes.Isrc) |
|
|
|
err = addExtendedMeta("ISRC", meta.Data[0].Relationships.Tracks.Data[index].Attributes.Isrc) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -724,7 +761,12 @@ func writeM4a(w *mp4.Writer, info *SongInfo, meta *AutoGenerated, data []byte, t |
|
|
|
if len(meta.Data) > 0 { |
|
|
|
if len(meta.Data) > 0 { |
|
|
|
album := meta.Data[0] |
|
|
|
album := meta.Data[0] |
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'a', 'A', 'R', 'T'}, album.Attributes.ArtistName) |
|
|
|
err = addMeta(mp4.BoxType{'a', 'A', 'R', 'T'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'s', 'o', 'a', 'a'}, meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
@ -743,6 +785,11 @@ func writeM4a(w *mp4.Writer, info *SongInfo, meta *AutoGenerated, data []byte, t |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'\251', 'p', 'u', 'b'}, album.Attributes.RecordLabel) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
err = addExtendedMeta("LABEL", album.Attributes.RecordLabel) |
|
|
|
err = addExtendedMeta("LABEL", album.Attributes.RecordLabel) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -752,44 +799,53 @@ func writeM4a(w *mp4.Writer, info *SongInfo, meta *AutoGenerated, data []byte, t |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// plID, err := strconv.ParseUint(album.ID, 10, 32)
|
|
|
|
if !strings.Contains(meta.Data[0].ID, "pl."){ |
|
|
|
// if err != nil {
|
|
|
|
plID, err := strconv.ParseUint(meta.Data[0].ID, 10, 32) |
|
|
|
// return err
|
|
|
|
if err != nil { |
|
|
|
// }
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
// err = addMeta(mp4.BoxType{'p', 'l', 'I', 'D'}, uint32(plID))
|
|
|
|
|
|
|
|
// if err != nil {
|
|
|
|
err = addMeta(mp4.BoxType{'p', 'l', 'I', 'D'}, uint32(plID)) |
|
|
|
// return err
|
|
|
|
if err != nil { |
|
|
|
// }
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// if len(meta.Data[0].Relationships.Artists.Data) > 0 {
|
|
|
|
if len(meta.Data[0].Relationships.Tracks.Data[index].Relationships.Artists.Data[0].ID) > 0 { |
|
|
|
// atID, err := strconv.ParseUint(meta.Data[0].Relationships.Artists.Data[index].ID, 10, 32)
|
|
|
|
atID, err := strconv.ParseUint(meta.Data[0].Relationships.Tracks.Data[index].Relationships.Artists.Data[0].ID, 10, 32) |
|
|
|
// if err != nil {
|
|
|
|
if err != nil { |
|
|
|
// return err
|
|
|
|
return err |
|
|
|
// }
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// err = addMeta(mp4.BoxType{'a', 't', 'I', 'D'}, uint32(atID))
|
|
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
|
|
// return err
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'a', 't', 'I', 'D'}, uint32(atID)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
trkn := make([]byte, 8) |
|
|
|
trkn := make([]byte, 8) |
|
|
|
binary.BigEndian.PutUint32(trkn, uint32(trackNum)) |
|
|
|
disk := make([]byte, 8) |
|
|
|
|
|
|
|
binary.BigEndian.PutUint32(trkn, uint32(meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber)) |
|
|
|
binary.BigEndian.PutUint16(trkn[4:], uint16(trackTotal)) |
|
|
|
binary.BigEndian.PutUint16(trkn[4:], uint16(trackTotal)) |
|
|
|
|
|
|
|
binary.BigEndian.PutUint32(disk, uint32(meta.Data[0].Relationships.Tracks.Data[index].Attributes.DiscNumber)) |
|
|
|
|
|
|
|
binary.BigEndian.PutUint16(disk[4:], uint16(meta.Data[0].Relationships.Tracks.Data[trackTotal - 1].Attributes.DiscNumber)) |
|
|
|
|
|
|
|
if strings.Contains(meta.Data[0].ID, "pl."){ |
|
|
|
|
|
|
|
if !config.UseSongInfoForPlaylist { |
|
|
|
|
|
|
|
binary.BigEndian.PutUint32(trkn, uint32(trackNum)) |
|
|
|
|
|
|
|
binary.BigEndian.PutUint16(trkn[4:], uint16(trackTotal)) |
|
|
|
|
|
|
|
binary.BigEndian.PutUint32(disk, uint32(1)) |
|
|
|
|
|
|
|
binary.BigEndian.PutUint16(disk[4:], uint16(1)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
err = addMeta(mp4.BoxType{'t', 'r', 'k', 'n'}, trkn) |
|
|
|
err = addMeta(mp4.BoxType{'t', 'r', 'k', 'n'}, trkn) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
err = addMeta(mp4.BoxType{'d', 'i', 's', 'k'}, disk) |
|
|
|
// disk := make([]byte, 8)
|
|
|
|
if err != nil { |
|
|
|
// binary.BigEndian.PutUint32(disk, uint32(meta.Attributes.DiscNumber))
|
|
|
|
return err |
|
|
|
// err = addMeta(mp4.BoxType{'d', 'i', 's', 'k'}, disk)
|
|
|
|
} |
|
|
|
// if err != nil {
|
|
|
|
|
|
|
|
// return err
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ctx.UnderIlst = false |
|
|
|
ctx.UnderIlst = false |
|
|
|
|
|
|
|
|
|
|
@ -1054,8 +1110,8 @@ func getSongLyrics(songId string, storefront string, token string, userToken str |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func writeCover(sanAlbumFolder, url string) error { |
|
|
|
func writeCover(sanAlbumFolder,name string, url string) error { |
|
|
|
covPath := filepath.Join(sanAlbumFolder, "cover." + config.CoverFormat) |
|
|
|
covPath := filepath.Join(sanAlbumFolder, name+"." + config.CoverFormat) |
|
|
|
exists, err := fileExists(covPath) |
|
|
|
exists, err := fileExists(covPath) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
fmt.Println("Failed to check if cover exists.") |
|
|
|
fmt.Println("Failed to check if cover exists.") |
|
|
@ -1162,13 +1218,27 @@ func rip(albumId string, token string, storefront string, userToken string) erro |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Tag_string := strings.Join(stringsToJoin, " ") |
|
|
|
Tag_string := strings.Join(stringsToJoin, " ") |
|
|
|
|
|
|
|
manifest1, err := getInfoFromAdam(meta.Data[0].Relationships.Tracks.Data[0].ID, token, storefront) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println("Failed to get manifest.\n", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if manifest1.Attributes.ExtendedAssetUrls.EnhancedHls == "" { |
|
|
|
|
|
|
|
fmt.Println("Unavailable in ALAC.") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var Quality string |
|
|
|
|
|
|
|
if strings.Contains(config.AlbumFolderFormat, "Quality"){ |
|
|
|
|
|
|
|
Quality,err = extractMediaQuality(manifest1.Attributes.ExtendedAssetUrls.EnhancedHls) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println("Failed to extract quality from manifest.\n", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
var albumFolder string |
|
|
|
var albumFolder string |
|
|
|
if strings.Contains(albumId, "pl.") { |
|
|
|
if strings.Contains(albumId, "pl.") { |
|
|
|
albumFolder = strings.NewReplacer( |
|
|
|
albumFolder = strings.NewReplacer( |
|
|
|
"{ArtistName}", "Apple Music", |
|
|
|
"{ArtistName}", "Apple Music", |
|
|
|
"{PlaylistName}", meta.Data[0].Attributes.Name, |
|
|
|
"{PlaylistName}", meta.Data[0].Attributes.Name, |
|
|
|
"{PlaylistId}", albumId, |
|
|
|
"{PlaylistId}", albumId, |
|
|
|
"{Quality}","", |
|
|
|
"{Quality}",Quality, |
|
|
|
"{Codec}", "ALAC", |
|
|
|
"{Codec}", "ALAC", |
|
|
|
"{Tag}",Tag_string, |
|
|
|
"{Tag}",Tag_string, |
|
|
|
).Replace(config.PlaylistFolderFormat) |
|
|
|
).Replace(config.PlaylistFolderFormat) |
|
|
@ -1181,7 +1251,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro |
|
|
|
"{UPC}", meta.Data[0].Attributes.Upc, |
|
|
|
"{UPC}", meta.Data[0].Attributes.Upc, |
|
|
|
"{Copyright}", meta.Data[0].Attributes.Copyright, |
|
|
|
"{Copyright}", meta.Data[0].Attributes.Copyright, |
|
|
|
"{AlbumId}", albumId, |
|
|
|
"{AlbumId}", albumId, |
|
|
|
"{Quality}", "", |
|
|
|
"{Quality}", Quality, |
|
|
|
"{Codec}", "ALAC", |
|
|
|
"{Codec}", "ALAC", |
|
|
|
"{Tag}",Tag_string, |
|
|
|
"{Tag}",Tag_string, |
|
|
|
).Replace(config.AlbumFolderFormat) |
|
|
|
).Replace(config.AlbumFolderFormat) |
|
|
@ -1193,7 +1263,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro |
|
|
|
sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) |
|
|
|
sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) |
|
|
|
os.MkdirAll(sanAlbumFolder, os.ModePerm) |
|
|
|
os.MkdirAll(sanAlbumFolder, os.ModePerm) |
|
|
|
fmt.Println(albumFolder) |
|
|
|
fmt.Println(albumFolder) |
|
|
|
err = writeCover(sanAlbumFolder, meta.Data[0].Attributes.Artwork.URL) |
|
|
|
err = writeCover(sanAlbumFolder,"cover", meta.Data[0].Attributes.Artwork.URL) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
fmt.Println("Failed to write cover.") |
|
|
|
fmt.Println("Failed to write cover.") |
|
|
|
} |
|
|
|
} |
|
|
@ -1255,6 +1325,14 @@ func rip(albumId string, token string, storefront string, userToken string) erro |
|
|
|
fmt.Println("Unavailable in ALAC.") |
|
|
|
fmt.Println("Unavailable in ALAC.") |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var Quality string |
|
|
|
|
|
|
|
if strings.Contains(config.SongFileFormat, "Quality"){ |
|
|
|
|
|
|
|
Quality,err = extractMediaQuality(manifest.Attributes.ExtendedAssetUrls.EnhancedHls) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println("Failed to extract quality from manifest.\n", err) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
stringsToJoin := []string{} |
|
|
|
stringsToJoin := []string{} |
|
|
|
if track.Attributes.IsAppleDigitalMaster{ |
|
|
|
if track.Attributes.IsAppleDigitalMaster{ |
|
|
|
if config.AppleMasterChoice != ""{ |
|
|
|
if config.AppleMasterChoice != ""{ |
|
|
@ -1278,7 +1356,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro |
|
|
|
"{SongName}", track.Attributes.Name, |
|
|
|
"{SongName}", track.Attributes.Name, |
|
|
|
"{DiscNumber}", string(track.Attributes.DiscNumber), |
|
|
|
"{DiscNumber}", string(track.Attributes.DiscNumber), |
|
|
|
"{TrackNumber}", fmt.Sprintf("%02d", track.Attributes.TrackNumber), |
|
|
|
"{TrackNumber}", fmt.Sprintf("%02d", track.Attributes.TrackNumber), |
|
|
|
"{Quality}","", |
|
|
|
"{Quality}",Quality, |
|
|
|
"{Codec}", "ALAC", |
|
|
|
"{Codec}", "ALAC", |
|
|
|
"{Tag}",Tag_string, |
|
|
|
"{Tag}",Tag_string, |
|
|
|
).Replace(config.SongFileFormat) |
|
|
|
).Replace(config.SongFileFormat) |
|
|
@ -1378,7 +1456,15 @@ func rip(albumId string, token string, storefront string, userToken string) erro |
|
|
|
fmt.Sprintf("lyrics=%s", lrc), |
|
|
|
fmt.Sprintf("lyrics=%s", lrc), |
|
|
|
} |
|
|
|
} |
|
|
|
if config.EmbedCover { |
|
|
|
if config.EmbedCover { |
|
|
|
tags = append(tags, fmt.Sprintf("cover=%s/cover.%s", sanAlbumFolder, config.CoverFormat)) |
|
|
|
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, ":") |
|
|
|
tagsString := strings.Join(tags, ":") |
|
|
|
cmd := exec.Command("MP4Box", "-itags", tagsString, trackPath) |
|
|
|
cmd := exec.Command("MP4Box", "-itags", tagsString, trackPath) |
|
|
@ -1386,6 +1472,12 @@ func rip(albumId string, token string, storefront string, userToken string) erro |
|
|
|
fmt.Printf("Embed failed: %v\n", err) |
|
|
|
fmt.Printf("Embed failed: %v\n", err) |
|
|
|
continue |
|
|
|
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 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return err |
|
|
|
return err |
|
|
@ -1471,6 +1563,50 @@ func conventTTMLToLRC(ttml string) (string, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
return strings.Join(lrcLines, "\n"), nil |
|
|
|
return strings.Join(lrcLines, "\n"), nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func extractMediaQuality(b string) (string, error) { |
|
|
|
|
|
|
|
resp, err := http.Get(b) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
defer resp.Body.Close() |
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK { |
|
|
|
|
|
|
|
return "", errors.New(resp.Status) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
masterString := string(body) |
|
|
|
|
|
|
|
from, listType, err := m3u8.DecodeFrom(strings.NewReader(masterString), true) |
|
|
|
|
|
|
|
if err != nil || listType != m3u8.MASTER { |
|
|
|
|
|
|
|
return "", errors.New("m3u8 not of master type") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
master := from.(*m3u8.MasterPlaylist) |
|
|
|
|
|
|
|
sort.Slice(master.Variants, func(i, j int) bool { |
|
|
|
|
|
|
|
return master.Variants[i].AverageBandwidth > master.Variants[j].AverageBandwidth |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
var Quality string |
|
|
|
|
|
|
|
for _, variant := range master.Variants { |
|
|
|
|
|
|
|
if variant.Codecs == "alac" { |
|
|
|
|
|
|
|
split := strings.Split(variant.Audio, "-") |
|
|
|
|
|
|
|
length := len(split) |
|
|
|
|
|
|
|
length_int,err := strconv.Atoi(split[length-2]) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if length_int <= config.AlacMax{ |
|
|
|
|
|
|
|
HZ,err:=strconv.Atoi(split[length-2]) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
KHZ:=float64(HZ) / 1000.0 |
|
|
|
|
|
|
|
Quality = fmt.Sprintf("%sB-%.1fkHz", split[length-1], KHZ) |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return Quality, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func extractMedia(b string) (string, []string, error) { |
|
|
|
func extractMedia(b string) (string, []string, error) { |
|
|
|
masterUrl, err := url.Parse(b) |
|
|
|
masterUrl, err := url.Parse(b) |
|
|
|