mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-13 21:07:10 +02:00
refactor(lyrics): single registry drives both suffix dispatch and sniff order
Collapse the bySuffix map and sniffOrder slice into one ordered registry: slice order is the content-sniff probe order, each row's suffixes drive sidecar dispatch, and per-row bySuffix/byContent parsers preserve the gated-TTML-when-sniffing distinction. One source of truth, no duplicated parser references.
This commit is contained in:
parent
72a69306be
commit
8c10124b65
1 changed files with 25 additions and 18 deletions
|
|
@ -3,6 +3,7 @@ package model
|
|||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
|
|
@ -28,23 +29,23 @@ func parseTTMLIfDocument(lang string, contents []byte) (LyricList, error) {
|
|||
return parseTTMLWithDefaultLang(lang, contents)
|
||||
}
|
||||
|
||||
// bySuffix maps a sidecar file extension to its parser. LRC and unknown
|
||||
// extensions are absent: they fall straight to the plain-text floor. The suffix
|
||||
// already declares the format, so TTML parses unconditionally here (a malformed
|
||||
// .ttml surfaces its error and then the plain-text fallback).
|
||||
var bySuffix = map[string]lyricParser{
|
||||
".ttml": parseTTMLWithDefaultLang,
|
||||
".srt": parseSRTWithLanguage,
|
||||
".yaml": parseLyricsfileBytes,
|
||||
".yml": parseLyricsfileBytes,
|
||||
// registry is the single source of truth for the structured formats. Slice
|
||||
// order is the content-sniff probe order (TTML → SRT → YAML), and each row's
|
||||
// suffixes drive sidecar dispatch. byContent is used when sniffing — it gates a
|
||||
// format so, e.g., TTML only runs the XML decoder on something that looks like a
|
||||
// document; bySuffix parses unconditionally because the file extension already
|
||||
// declares the format. LRC/plain is not listed: it is the fallback floor for
|
||||
// both paths.
|
||||
var registry = []struct {
|
||||
suffixes []string
|
||||
bySuffix lyricParser
|
||||
byContent lyricParser
|
||||
}{
|
||||
{[]string{".ttml"}, parseTTMLWithDefaultLang, parseTTMLIfDocument},
|
||||
{[]string{".srt"}, parseSRTWithLanguage, parseSRTWithLanguage},
|
||||
{[]string{".yaml", ".yml"}, parseLyricsfileBytes, parseLyricsfileBytes},
|
||||
}
|
||||
|
||||
// sniffOrder is the content-sniff candidate order for tag-embedded lyrics and
|
||||
// plugin responses (no suffix to dispatch on): TTML → SRT → YAML → LRC/plain.
|
||||
// TTML is gated on looking like a document so plain/LRC text is not run through
|
||||
// the XML decoder.
|
||||
var sniffOrder = []lyricParser{parseTTMLIfDocument, parseSRTWithLanguage, parseLyricsfileBytes}
|
||||
|
||||
// ParseLyrics is the single entry point for parsing lyrics. A known suffix
|
||||
// (.ttml/.srt/.yaml/.yml/.lrc) routes to that format's parser; an empty or
|
||||
// "auto" suffix content-sniffs. In both modes a structured parser that does not
|
||||
|
|
@ -52,10 +53,16 @@ var sniffOrder = []lyricParser{parseTTMLIfDocument, parseSRTWithLanguage, parseL
|
|||
// format.
|
||||
func ParseLyrics(suffix, lang string, contents []byte) (LyricList, error) {
|
||||
if suffix == "" || strings.EqualFold(suffix, "auto") {
|
||||
return parseFirstMatch(lang, stripBOM(contents), sniffOrder...)
|
||||
candidates := make([]lyricParser, len(registry))
|
||||
for i, r := range registry {
|
||||
candidates[i] = r.byContent
|
||||
}
|
||||
return parseFirstMatch(lang, stripBOM(contents), candidates...)
|
||||
}
|
||||
if parse, ok := bySuffix[strings.ToLower(suffix)]; ok {
|
||||
return parseFirstMatch(lang, contents, parse)
|
||||
for _, r := range registry {
|
||||
if slices.ContainsFunc(r.suffixes, func(s string) bool { return strings.EqualFold(s, suffix) }) {
|
||||
return parseFirstMatch(lang, contents, r.bySuffix)
|
||||
}
|
||||
}
|
||||
return plainLRC(lang, contents) // .lrc and any unknown suffix
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue