|
|
@ -44,6 +44,7 @@ var artist_select = false |
|
|
|
type Config struct { |
|
|
|
type Config struct { |
|
|
|
MediaUserToken string `yaml:"media-user-token"` |
|
|
|
MediaUserToken string `yaml:"media-user-token"` |
|
|
|
SaveLrcFile bool `yaml:"save-lrc-file"` |
|
|
|
SaveLrcFile bool `yaml:"save-lrc-file"` |
|
|
|
|
|
|
|
LrcFormat string `yaml:"lrc-format"` |
|
|
|
SaveAnimatedArtwork bool `yaml:"save-animated-artwork"` |
|
|
|
SaveAnimatedArtwork bool `yaml:"save-animated-artwork"` |
|
|
|
EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` |
|
|
|
EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` |
|
|
|
EmbedLrc bool `yaml:"embed-lrc"` |
|
|
|
EmbedLrc bool `yaml:"embed-lrc"` |
|
|
@ -1258,7 +1259,7 @@ func getMeta(albumId string, token string, storefront string) (*AutoGenerated, e |
|
|
|
|
|
|
|
|
|
|
|
func getSongLyrics(songId string, storefront string, token string, userToken string) (string, error) { |
|
|
|
func getSongLyrics(songId string, storefront string, token string, userToken string) (string, error) { |
|
|
|
req, err := http.NewRequest("GET", |
|
|
|
req, err := http.NewRequest("GET", |
|
|
|
fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s/lyrics", storefront, songId), nil) |
|
|
|
fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s/%s", storefront, songId, config.LrcFormat), nil) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return "", err |
|
|
|
return "", err |
|
|
|
} |
|
|
|
} |
|
|
@ -1826,13 +1827,95 @@ func main() { |
|
|
|
fmt.Printf("======= Completed %d/%d ###### %d errors!! =======\n", oktrackNum, trackTotalnum, trackTotalnum-oktrackNum) |
|
|
|
fmt.Printf("======= Completed %d/%d ###### %d errors!! =======\n", oktrackNum, trackTotalnum, trackTotalnum-oktrackNum) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func conventSyllableTTMLToLRC(ttml string) (string, error) { |
|
|
|
|
|
|
|
parsedTTML := etree.NewDocument() |
|
|
|
|
|
|
|
err := parsedTTML.ReadFromString(ttml) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var lrcLines []string |
|
|
|
|
|
|
|
parseTime := func(timeValue string) (string, error) { |
|
|
|
|
|
|
|
var h, m, s, ms int |
|
|
|
|
|
|
|
if strings.Contains(timeValue, ":") { |
|
|
|
|
|
|
|
_, err = fmt.Sscanf(timeValue, "%d:%d:%d.%d", &h, &m, &s, &ms) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
_, err = fmt.Sscanf(timeValue, "%d:%d.%d", &m, &s, &ms) |
|
|
|
|
|
|
|
h = 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
_, err = fmt.Sscanf(timeValue, "%d.%d", &s, &ms) |
|
|
|
|
|
|
|
h, m = 0, 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
m += h * 60 |
|
|
|
|
|
|
|
ms = ms / 10 |
|
|
|
|
|
|
|
return fmt.Sprintf("[%02d:%02d.%02d]", m, s, ms), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for _, div := range parsedTTML.FindElement("tt").FindElement("body").FindElements("div") { |
|
|
|
|
|
|
|
for _, item := range div.ChildElements() { |
|
|
|
|
|
|
|
var lrcSyllables []string |
|
|
|
|
|
|
|
for _, lyric := range item.ChildElements() { |
|
|
|
|
|
|
|
if lyric.SelectAttr("begin") == nil { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
beginTime, err := parseTime(lyric.SelectAttr("begin").Value) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var text string |
|
|
|
|
|
|
|
if lyric.SelectAttr("text") == nil { |
|
|
|
|
|
|
|
var textTmp []string |
|
|
|
|
|
|
|
for _, span := range lyric.Child { |
|
|
|
|
|
|
|
if _, ok := span.(*etree.CharData); ok { |
|
|
|
|
|
|
|
textTmp = append(textTmp, span.(*etree.CharData).Data) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
textTmp = append(textTmp, span.(*etree.Element).Text()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
text = strings.Join(textTmp, "") |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
text = lyric.SelectAttr("text").Value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
lrcSyllables = append(lrcSyllables, fmt.Sprintf("%s%s", beginTime, text)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
endTime, err := parseTime(item.SelectAttr("end").Value) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
lrcLines = append(lrcLines, strings.Join(lrcSyllables, "")+endTime) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return strings.Join(lrcLines, "\n"), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func conventTTMLToLRC(ttml string) (string, error) { |
|
|
|
func conventTTMLToLRC(ttml string) (string, error) { |
|
|
|
parsedTTML := etree.NewDocument() |
|
|
|
parsedTTML := etree.NewDocument() |
|
|
|
err := parsedTTML.ReadFromString(ttml) |
|
|
|
err := parsedTTML.ReadFromString(ttml) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return "", err |
|
|
|
return "", err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var lrcLines []string |
|
|
|
var lrcLines []string |
|
|
|
|
|
|
|
timingAttr := parsedTTML.FindElement("tt").SelectAttr("itunes:timing") |
|
|
|
|
|
|
|
if timingAttr != nil { |
|
|
|
|
|
|
|
if timingAttr.Value == "Word" { |
|
|
|
|
|
|
|
lrc, err := conventSyllableTTMLToLRC(ttml) |
|
|
|
|
|
|
|
return lrc, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if timingAttr.Value == "None" { |
|
|
|
|
|
|
|
for _, p := range parsedTTML.FindElements("//p") { |
|
|
|
|
|
|
|
line := p.Text() |
|
|
|
|
|
|
|
line = strings.TrimSpace(line) |
|
|
|
|
|
|
|
if line != "" { |
|
|
|
|
|
|
|
lrcLines = append(lrcLines, line) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return strings.Join(lrcLines, "\n"), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for _, item := range parsedTTML.FindElement("tt").FindElement("body").ChildElements() { |
|
|
|
for _, item := range parsedTTML.FindElement("tt").FindElement("body").ChildElements() { |
|
|
|
for _, lyric := range item.ChildElements() { |
|
|
|
for _, lyric := range item.ChildElements() { |
|
|
|
var h, m, s, ms int |
|
|
|
var h, m, s, ms int |
|
|
|