fix: reject low-confidence automatic metadata matches

This commit is contained in:
ShukeBta
2026-06-25 02:58:07 +08:00
parent 9021007027
commit 5eb0d4c8af
5 changed files with 138 additions and 8 deletions
+98
View File
@@ -0,0 +1,98 @@
package service
import (
"regexp"
"strings"
)
var (
metadataTrustTokenRE = regexp.MustCompile(`[\p{L}\p{N}]+`)
metadataTrustDanglingEpisodeRE = regexp.MustCompile(`(?i)^s\d{1,2}e$`)
)
func automaticMetadataTitleTrusted(query string, match *Match) bool {
queryKey := metadataTrustKey(query)
if queryKey == "" || match == nil {
return false
}
for _, title := range []string{match.Title, match.OriginalName} {
titleKey := metadataTrustKey(title)
if titleKey == "" {
continue
}
if queryKey == titleKey || metadataTrustTokenOverlap(queryKey, titleKey) {
return true
}
}
return false
}
func metadataTrustKey(value string) string {
value = strings.ToLower(strings.TrimSpace(value))
if value == "" {
return ""
}
value = bracketedTag.ReplaceAllString(value, " ")
value = yearPattern.ReplaceAllString(value, " ")
for _, re := range []*regexp.Regexp{patSEnE, patNxE, patEP, patCN, patSeasonOnly, patCNSeason} {
value = re.ReplaceAllString(value, " ")
}
tokens := metadataTrustTokenRE.FindAllString(value, -1)
out := make([]string, 0, len(tokens))
for _, token := range tokens {
token = strings.TrimSpace(token)
if token == "" || metadataTrustNoiseToken(token) {
continue
}
out = append(out, token)
}
return strings.Join(out, " ")
}
func metadataTrustNoiseToken(token string) bool {
if token == "x" || token == "×" {
return true
}
if _, ok := noiseTokenSet[token]; ok {
return true
}
if metadataTrustDanglingEpisodeRE.MatchString(token) {
return true
}
return false
}
func metadataTrustTokenOverlap(queryKey, titleKey string) bool {
queryTokens := metadataTrustSignificantTokens(queryKey)
titleTokens := metadataTrustSignificantTokens(titleKey)
if len(queryTokens) <= 1 || len(titleTokens) <= 1 {
return false
}
titleSet := make(map[string]struct{}, len(titleTokens))
for _, token := range titleTokens {
titleSet[token] = struct{}{}
}
overlap := 0
for _, token := range queryTokens {
if _, ok := titleSet[token]; ok {
overlap++
}
}
queryCoverage := float64(overlap) / float64(len(queryTokens))
titleCoverage := float64(overlap) / float64(len(titleTokens))
return queryCoverage >= 0.80 && titleCoverage >= 0.50
}
func metadataTrustSignificantTokens(key string) []string {
fields := strings.Fields(key)
out := make([]string, 0, len(fields))
for _, field := range fields {
switch field {
case "the", "a", "an", "of", "and":
continue
default:
out = append(out, field)
}
}
return out
}