beta: Get translated lyrics

This commit is contained in:
itouakirai
2025-07-12 18:07:29 +08:00
parent 85c1c9fc60
commit db3f537e9e

View File

@@ -16,6 +16,7 @@ type SongLyrics struct {
Type string `json:"type"` Type string `json:"type"`
Attributes struct { Attributes struct {
Ttml string `json:"ttml"` Ttml string `json:"ttml"`
TtmlLocalizations string `json:"ttmlLocalizations"`
PlayParams struct { PlayParams struct {
Id string `json:"id"` Id string `json:"id"`
Kind string `json:"kind"` Kind string `json:"kind"`
@@ -49,7 +50,7 @@ func Get(storefront, songId, lrcType, language, lrcFormat, token, mediaUserToken
} }
func getSongLyrics(songId string, storefront string, token string, userToken string, lrcType string, language string) (string, error) { func getSongLyrics(songId string, storefront string, token string, userToken string, lrcType string, language 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/%s?l=%s", storefront, songId, lrcType, language), nil) fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s/%s?l=%s&extend=ttmlLocalizations", storefront, songId, lrcType, language), nil)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -66,7 +67,10 @@ func getSongLyrics(songId string, storefront string, token string, userToken str
obj := new(SongLyrics) obj := new(SongLyrics)
_ = json.NewDecoder(do.Body).Decode(&obj) _ = json.NewDecoder(do.Body).Decode(&obj)
if obj.Data != nil { if obj.Data != nil {
if len(obj.Data[0].Attributes.Ttml) > 0 {
return obj.Data[0].Attributes.Ttml, nil return obj.Data[0].Attributes.Ttml, nil
}
return obj.Data[0].Attributes.TtmlLocalizations, nil
} else { } else {
return "", errors.New("failed to get lyrics") return "", errors.New("failed to get lyrics")
} }
@@ -165,7 +169,7 @@ func conventSyllableTTMLToLRC(ttml string) (string, error) {
return "", err return "", err
} }
var lrcLines []string var lrcLines []string
parseTime := func(timeValue string, newLine bool) (string, error) { parseTime := func(timeValue string, newLine int) (string, error) {
var h, m, s, ms int var h, m, s, ms int
if strings.Contains(timeValue, ":") { if strings.Contains(timeValue, ":") {
_, err = fmt.Sscanf(timeValue, "%d:%d:%d.%d", &h, &m, &s, &ms) _, err = fmt.Sscanf(timeValue, "%d:%d:%d.%d", &h, &m, &s, &ms)
@@ -182,34 +186,22 @@ func conventSyllableTTMLToLRC(ttml string) (string, error) {
} }
m += h * 60 m += h * 60
ms = ms / 10 ms = ms / 10
if newLine { if newLine == 0 {
return fmt.Sprintf("[%02d:%02d.%02d]<%02d:%02d.%02d>", m, s, ms, m, s, ms), nil return fmt.Sprintf("[%02d:%02d.%02d]<%02d:%02d.%02d>", m, s, ms, m, s, ms), nil
} else if newLine == -1 {
return fmt.Sprintf("[%02d:%02d.%02d]", m, s, ms), nil
} else { } else {
return fmt.Sprintf("<%02d:%02d.%02d>", m, s, ms), nil return fmt.Sprintf("<%02d:%02d.%02d>", m, s, ms), nil
} }
} }
divs := parsedTTML.FindElement("tt").FindElement("body").FindElements("div") divs := parsedTTML.FindElement("tt").FindElement("body").FindElements("div")
//get trans
if len(parsedTTML.FindElement("tt").FindElements("head")) > 0 {
if len(parsedTTML.FindElement("tt").FindElement("head").FindElements("metadata")) > 0 {
Metadata := parsedTTML.FindElement("tt").FindElement("head").FindElement("metadata")
if len(Metadata.FindElements("iTunesMetadata")) > 0 {
iTunesMetadata := Metadata.FindElement("iTunesMetadata")
if len(iTunesMetadata.FindElements("translations")) > 0 {
if len(iTunesMetadata.FindElement("translations").FindElements("translation")) > 0 {
divs = iTunesMetadata.FindElement("translations").FindElements("translation")
}
}
}
}
}
for _, div := range divs { for _, div := range divs {
for _, item := range div.ChildElements() { for _, item := range div.ChildElements() { //LINES
var lrcSyllables []string var lrcSyllables []string
var i int = 0 var i int = 0
var endTime string var endTime, transLine string
for _, lyrics := range item.Child { for _, lyrics := range item.Child { //WORDS
if _, ok := lyrics.(*etree.CharData); ok { if _, ok := lyrics.(*etree.CharData); ok { //是否为span之间的空格
if i > 0 { if i > 0 {
lrcSyllables = append(lrcSyllables, " ") lrcSyllables = append(lrcSyllables, " ")
continue continue
@@ -220,11 +212,12 @@ func conventSyllableTTMLToLRC(ttml string) (string, error) {
if lyric.SelectAttr("begin") == nil { if lyric.SelectAttr("begin") == nil {
continue continue
} }
beginTime, err := parseTime(lyric.SelectAttr("begin").Value, i == 0) beginTime, err := parseTime(lyric.SelectAttr("begin").Value, i)
if err != nil { if err != nil {
return "", err return "", err
} }
endTime, err = parseTime(lyric.SelectAttr("end").Value, false)
endTime, err = parseTime(lyric.SelectAttr("end").Value, i)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -243,6 +236,39 @@ func conventSyllableTTMLToLRC(ttml string) (string, error) {
text = lyric.SelectAttr("text").Value text = lyric.SelectAttr("text").Value
} }
lrcSyllables = append(lrcSyllables, fmt.Sprintf("%s%s", beginTime, text)) lrcSyllables = append(lrcSyllables, fmt.Sprintf("%s%s", beginTime, text))
if i == 0 {
transBeginTime, _ := parseTime(lyric.SelectAttr("begin").Value, -1)
if len(parsedTTML.FindElement("tt").FindElements("head")) > 0 {
if len(parsedTTML.FindElement("tt").FindElement("head").FindElements("metadata")) > 0 {
Metadata := parsedTTML.FindElement("tt").FindElement("head").FindElement("metadata")
if len(Metadata.FindElements("iTunesMetadata")) > 0 {
iTunesMetadata := Metadata.FindElement("iTunesMetadata")
if len(iTunesMetadata.FindElements("translations")) > 0 {
if len(iTunesMetadata.FindElement("translations").FindElements("translation")) > 0 {
xpath := fmt.Sprintf("//text[@for='%s']", item.SelectAttr("itunes:key").Value)
trans := iTunesMetadata.FindElement("translations").FindElement("translation").FindElement(xpath)
var transTxt string
if trans.SelectAttr("text") == nil {
var textTmp []string
for _, span := range trans.Child {
if _, ok := span.(*etree.CharData); ok {
textTmp = append(textTmp, span.(*etree.CharData).Data)
} else {
textTmp = append(textTmp, span.(*etree.Element).Text())
}
}
transTxt = strings.Join(textTmp, "")
} else {
transTxt = lyric.SelectAttr("text").Value
}
//fmt.Println(transTxt)
transLine = transBeginTime + transTxt
}
}
}
}
}
}
i += 1 i += 1
} }
//endTime, err := parseTime(item.SelectAttr("end").Value) //endTime, err := parseTime(item.SelectAttr("end").Value)
@@ -250,6 +276,9 @@ func conventSyllableTTMLToLRC(ttml string) (string, error) {
// return "", err // return "", err
//} //}
lrcLines = append(lrcLines, strings.Join(lrcSyllables, "")+endTime) lrcLines = append(lrcLines, strings.Join(lrcSyllables, "")+endTime)
if len(transLine) > 0 {
lrcLines = append(lrcLines, transLine)
}
} }
} }
return strings.Join(lrcLines, "\n"), nil return strings.Join(lrcLines, "\n"), nil