refactor(metadata): drop per-file rawTags duplication for lyrics

mapLyrics() used a dedicated rawPairs()/rawTags path solely to bypass the
32KB lyrics truncation cap (the comment claiming sanitization "strips XML"
was inaccurate -- pair-type tags are only truncated, never stripped). That
duplicated every file s tags into a second lowercased map on the scan hot
path.

Instead, raise the lyrics maxLength so the normal Pairs() path keeps large
word-timed TTML/Enhanced-LRC intact, and remove rawTags/rawPairs. The
existing ">32KB embedded TTML" test continues to pass through Pairs().
This commit is contained in:
Deluan 2026-06-18 21:00:53 -04:00
parent 2e2dc17dd6
commit a42b27cfce
4 changed files with 19 additions and 15 deletions

View File

@ -135,7 +135,7 @@ func (md Metadata) mapGain(rg, r128 model.TagName) *float64 {
}
func (md Metadata) mapLyrics() string {
rawLyrics := md.rawPairs(model.TagLyrics)
rawLyrics := md.Pairs(model.TagLyrics)
lyricList := make(model.LyricList, 0, len(rawLyrics))

View File

@ -70,7 +70,6 @@ func New(filePath string, info Info) Metadata {
return Metadata{
filePath: filePath,
fileInfo: info.FileInfo,
rawTags: lowerTags(info.Tags),
tags: clean(filePath, info.Tags),
audioProps: info.AudioProperties,
hasPicture: info.HasPicture,
@ -80,7 +79,6 @@ func New(filePath string, info Info) Metadata {
type Metadata struct {
filePath string
fileInfo FileInfo
rawTags model.Tags
tags model.Tags
audioProps AudioProperties
hasPicture bool
@ -116,14 +114,6 @@ func (md Metadata) Pairs(key model.TagName) []Pair {
values := md.tags[key]
return slice.Map(values, func(v string) Pair { return Pair(v) })
}
func (md Metadata) rawPairs(key model.TagName) []Pair {
mapping, ok := model.TagMappings()[key]
if !ok {
return nil
}
values := filterDuplicatedOrEmptyValues(processPairMapping(key, mapping, md.rawTags))
return slice.Map(values, func(v string) Pair { return Pair(v) })
}
func (md Metadata) first(key model.TagName) string {
if v, ok := md.tags[key]; ok && len(v) > 0 {
return v[0]

View File

@ -105,7 +105,7 @@ var _ = Describe("Metadata", func() {
props.Tags = model.RawTags{
"Title": {strings.Repeat("a", 2048)},
"Comment": {strings.Repeat("a", 8192)},
"lyrics:xxx": {strings.Repeat("a", 60000)},
"lyrics:xxx": {strings.Repeat("a", 2_000_000)},
}
md = metadata.New(filePath, props)
@ -116,9 +116,21 @@ var _ = Describe("Metadata", func() {
Expect(pair).To(HaveLen(1))
Expect(pair[0].Key()).To(Equal("xxx"))
// Lyrics keep a much larger cap so word-timed karaoke survives.
// Note: a total of 6 characters are lost from maxLength from
// the key portion and separator
Expect(pair[0].Value()).To(HaveLen(32762))
// the key portion and separator.
Expect(pair[0].Value()).To(HaveLen(1048570))
})
It("keeps embedded lyrics that exceed the old 32KB cap", func() {
props.Tags = model.RawTags{
"lyrics:xxx": {strings.Repeat("a", 60000)},
}
md = metadata.New(filePath, props)
pair := md.Pairs(model.TagLyrics)
Expect(pair).To(HaveLen(1))
Expect(pair[0].Value()).To(HaveLen(60000))
})
It("should split multiple values", func() {

View File

@ -110,7 +110,9 @@ main:
lyrics:
# Note, @lyr and wm/lyrics have been removed. Taglib somehow appears to always populate `lyrics:xxx`
aliases: [ uslt:description, lyrics, unsyncedlyrics ]
maxLength: 32768
# Generous cap: word-timed TTML/Enhanced-LRC karaoke for a full song can run
# well past the previous 32KB limit. Still bounded to guard against pathological tags.
maxLength: 1048576
type: pair # ex: lyrics:eng, lyrics:xxx
comment:
aliases: [ comm:description, comment, ©cmt, description, icmt ]