diff --git a/model/lyrics_lyricsfile.go b/model/lyrics_lyricsfile.go index 91e24947e..49d416f3f 100644 --- a/model/lyrics_lyricsfile.go +++ b/model/lyrics_lyricsfile.go @@ -20,7 +20,7 @@ import ( // overlapping lines are attributed to synthetic voice agents via lowest-free // voice ID assignment so the OpenSubsonic v2 enhanced response can split // parallel vocals. -func parseLyricsfile(_ string, contents []byte) (LyricList, error) { +func parseLyricsfile(lang string, contents []byte) (LyricList, error) { var doc lyricsfileDocument dec := yaml.NewDecoder(bytes.NewReader(contents)) dec.KnownFields(false) @@ -32,10 +32,16 @@ func parseLyricsfile(_ string, contents []byte) (LyricList, error) { return nil, nil } + // Fall back to the caller's language when the document omits its own, matching + // the SRT/TTML parsers; normalizeLyricLang yields "xxx" only if both are empty. + docLang := doc.Metadata.Language + if strings.TrimSpace(docLang) == "" { + docLang = lang + } lyrics := Lyrics{ DisplayArtist: str.SanitizeText(doc.Metadata.Artist), DisplayTitle: str.SanitizeText(doc.Metadata.Title), - Lang: normalizeLyricLang(doc.Metadata.Language), + Lang: normalizeLyricLang(docLang), Kind: LyricKindMain, } if doc.Metadata.OffsetMs != 0 { diff --git a/model/lyrics_lyricsfile_test.go b/model/lyrics_lyricsfile_test.go index 83751aace..45899cda7 100644 --- a/model/lyrics_lyricsfile_test.go +++ b/model/lyrics_lyricsfile_test.go @@ -66,6 +66,24 @@ lines: Expect(l.Line[1].Cue).To(BeNil()) }) + DescribeTable("resolves the lyric language", + func(metaLanguage, callerLang, want string) { + input := "version: '1.0'\nmetadata:\n title: 'T'\n" + if metaLanguage != "" { + input += " language: '" + metaLanguage + "'\n" + } + input += "lines:\n - text: \"line\"\n start_ms: 0\n" + + lyrics, err := parseLyricsfile(callerLang, []byte(input)) + Expect(err).ToNot(HaveOccurred()) + Expect(lyrics).To(HaveLen(1)) + Expect(lyrics[0].Lang).To(Equal(want)) + }, + Entry("prefers the document's own language", "eng", "deu", "eng"), + Entry("falls back to the caller language when metadata omits it", "", "deu", "deu"), + Entry("uses xxx when neither is provided", "", "", "xxx"), + ) + It("parses plain-only Lyricsfile lyrics as unsynced lines", func() { input := `version: '1.0' metadata: diff --git a/model/lyrics_ttml.go b/model/lyrics_ttml.go index c82887f6f..45e1d5725 100644 --- a/model/lyrics_ttml.go +++ b/model/lyrics_ttml.go @@ -118,7 +118,8 @@ func isTTMLDocument(contents []byte) bool { func parseTTMLWithDefaultLang(defaultLang string, contents []byte) (LyricList, error) { contents = xmlEncodingRegex.ReplaceAll(contents, []byte(``)) - // Skip non-TTML content so sniffing doesn't run the XML decoder on plain text. + // Skip non-TTML content so sniffing doesn't run the full TTML parse on plain + // text — isTTMLDocument does a cheap decode that stops at the first element. // Checked after the encoding fixup so UTF-16-declared documents are recognized. if !isTTMLDocument(contents) { return nil, nil