refactor(lyrics): centralize lyric kind constants

The "main"/"translation"/"pronunciation" kind values were redefined in
three places (ttmlLyricKind* in the TTML parser, lyricsfileKindMain in the
Lyricsfile parser, and inline literals in the subsonic response builder).
Define them once as model.LyricKind* (kept as wire-compatible string values)
plus a LyricKindOrMain helper, and use them everywhere a kind is produced,
defaulted, or filtered. No wire-facing types change.
This commit is contained in:
Deluan 2026-06-18 21:04:34 -04:00
parent 7796faa1b2
commit 943cbc5f08
4 changed files with 30 additions and 24 deletions

View File

@ -45,6 +45,23 @@ type Lyrics struct {
Synced bool `structs:"synced" json:"synced"`
}
// Lyric kinds, as defined by the OpenSubsonic songLyrics v2 contract. These are
// the canonical wire values; keep them in sync with the spec.
const (
LyricKindMain = "main"
LyricKindTranslation = "translation"
LyricKindPronunciation = "pronunciation"
)
// LyricKindOrMain returns kind, defaulting to LyricKindMain when empty. A blank
// kind means an untyped (single-track) lyric, which the contract treats as main.
func LyricKindOrMain(kind string) string {
if strings.TrimSpace(kind) == "" {
return LyricKindMain
}
return kind
}
// support the standard [mm:ss.mm], as well as [hh:*] and [*.mmm]
const timeRegexString = `\[([0-9]{1,2}:)?([0-9]{1,2}):([0-9]{1,2})(\.[0-9]{1,3})?\]`

View File

@ -21,10 +21,7 @@ const (
defaultTTMLSubFrameRate = 1.0
defaultTTMLTickRate = 1.0
ttmlLyricKindMain = "main"
ttmlLyricKindTranslation = "translation"
ttmlLyricKindPronunciation = "pronunciation"
ttmlBackgroundAgentPrefix = "__nd_bg__|"
ttmlBackgroundAgentPrefix = "__nd_bg__|"
)
var offsetTimeRegex = regexp.MustCompile(`^([0-9]+(?:\.[0-9]+)?)(h|m|s|ms|f|t)$`)
@ -157,9 +154,9 @@ func (p *ttmlParser) parseElement(start xml.StartElement, parent ttmlTimingConte
switch local {
case "translation":
return p.parseMetadataTrack(start, parent, ttmlLyricKindTranslation)
return p.parseMetadataTrack(start, parent, LyricKindTranslation)
case "transliteration":
return p.parseMetadataTrack(start, parent, ttmlLyricKindPronunciation)
return p.parseMetadataTrack(start, parent, LyricKindPronunciation)
case "agent":
return p.parseAgentDefinition(start)
}
@ -582,15 +579,15 @@ func (p *ttmlParser) toLyricList() LyricList {
continue
}
res = append(res, p.finalizeLyrics(Lyrics{
Kind: ttmlLyricKindMain,
Kind: LyricKindMain,
Lang: lang,
Line: lines,
Synced: linesAreSynced(lines),
}))
}
res = append(res, p.buildMetadataLyrics(ttmlLyricKindTranslation, p.translationLangOrder, p.translationEntriesByLg)...)
res = append(res, p.buildMetadataLyrics(ttmlLyricKindPronunciation, p.pronunciationLangOrder, p.pronunciationEntriesByLg)...)
res = append(res, p.buildMetadataLyrics(LyricKindTranslation, p.translationLangOrder, p.translationEntriesByLg)...)
res = append(res, p.buildMetadataLyrics(LyricKindPronunciation, p.pronunciationLangOrder, p.pronunciationEntriesByLg)...)
return res
}
@ -854,12 +851,12 @@ func (p *ttmlParser) addMetadataEntry(kind string, lang string, entry ttmlMetada
p.metadataSeq++
switch kind {
case ttmlLyricKindTranslation:
case LyricKindTranslation:
if _, ok := p.translationEntriesByLg[lang]; !ok {
p.translationLangOrder = append(p.translationLangOrder, lang)
}
p.translationEntriesByLg[lang] = append(p.translationEntriesByLg[lang], entry)
case ttmlLyricKindPronunciation:
case LyricKindPronunciation:
if _, ok := p.pronunciationEntriesByLg[lang]; !ok {
p.pronunciationLangOrder = append(p.pronunciationLangOrder, lang)
}

View File

@ -35,7 +35,7 @@ func ParseLyricsfile(text string) (LyricList, error) {
DisplayArtist: str.SanitizeText(doc.Metadata.Artist),
DisplayTitle: str.SanitizeText(doc.Metadata.Title),
Lang: normalizeLyricsfileLang(doc.Metadata.Language),
Kind: lyricsfileKindMain,
Kind: LyricKindMain,
}
if doc.Metadata.OffsetMs != 0 {
off := doc.Metadata.OffsetMs
@ -62,10 +62,7 @@ func ParseLyricsfile(text string) (LyricList, error) {
return LyricList{NormalizeLyrics(lyrics)}, nil
}
const (
lyricsfileVersion = "1.0"
lyricsfileKindMain = "main"
)
const lyricsfileVersion = "1.0"
type lyricsfileDocument struct {
Version string `yaml:"version"`

View File

@ -581,11 +581,7 @@ func buildStructuredLyric(mf *model.MediaFile, lyrics model.Lyrics, enhanced boo
}
if enhanced {
kind := strings.TrimSpace(lyrics.Kind)
if kind == "" {
kind = "main"
}
structured.Kind = kind
structured.Kind = model.LyricKindOrMain(lyrics.Kind)
if len(cueLines) > 0 && len(responseAgents) > 0 {
structured.Agents = responseAgents
}
@ -637,10 +633,9 @@ func buildLyricsList(mf *model.MediaFile, lyricsList model.LyricList, enhanced b
if enhanced {
filtered = lyricsList
} else {
// Without enhanced, only return "main" kind entries
// Without enhanced, only return main-kind entries (a blank kind is main).
for _, l := range lyricsList {
kind := strings.TrimSpace(l.Kind)
if kind == "" || kind == "main" {
if model.LyricKindOrMain(l.Kind) == model.LyricKindMain {
filtered = append(filtered, l)
}
}