Files
resource-scraper/internal/releaseparser/groups/moonblood/parser.go
T
2026-09-01 00:08:03 +03:00

168 lines
3.9 KiB
Go

package moonblood
import (
"regexp"
"strconv"
"strings"
"git.kplus.net.ua/yevhen/resource-scraper/internal/releaseparser"
)
var yearRegexp = regexp.MustCompile(`^(19|20)\d{2}$`)
func Parse(raw string) releaseparser.ParsedRelease {
result := releaseparser.ParsedRelease{
Raw: strings.TrimSpace(raw),
Warnings: []string{},
UnknownTokens: []string{},
}
tokens := split(raw)
if len(tokens) < 5 {
result.Warnings = append(result.Warnings, "too few tokens")
result.Confidence = 0.2
return result
}
// Останній токен — група. Вона ж — ключ до профілю групи (довідників).
result.Group = tokens[len(tokens)-1]
// Передостанній — рік
last := len(tokens) - 2
if yearRegexp.MatchString(tokens[last]) {
result.Year, _ = strconv.Atoi(tokens[last])
last--
}
// Рухаємось вліво по хвосту: format, source, bit_depth, resolution, language, edition.
// Кожен слот бере токен лише зі свого словника (регістронезалежно).
// Невідомий токен: репортимо лише якщо ліворуч ще є відомий хвостовий маркер
// (токен між двома маркерами = сміття в хвості). Інакше це початок core.
tailLoop:
for last >= 0 {
token := strings.ToUpper(tokens[last])
switch {
case Profile.FormatTokens[token]:
if result.Format == "" {
result.Format = token
}
last--
case Profile.SourceTokens[token]:
if result.Source == "" {
result.Source = token
}
last--
case Profile.BitDepthTokens[token]:
if result.BitDepth == "" {
result.BitDepth = token
}
last--
case Profile.ResolutionTokens[token]:
if result.Resolution == "" {
result.Resolution = token
}
last--
case Profile.LanguageMarkers[token]:
if result.Language == "" {
result.Language = token
}
last--
case Profile.EditionTokens[token]:
if result.Edition == "" {
result.Edition = token
}
last--
default:
if hasKnownTailToken(tokens[:last]) {
result.UnknownTokens = append(result.UnknownTokens, tokens[last])
last--
continue
}
break tailLoop
}
}
// Core = artist-title (все, що залишилось ліворуч)
coreTokens := tokens[:last+1]
result.Core = strings.Join(coreTokens, "-")
if len(coreTokens) >= 2 {
result.Artist = coreTokens[0]
result.Title = strings.Join(coreTokens[1:], "-")
} else if len(coreTokens) == 1 {
result.Artist = coreTokens[0]
result.Title = ""
result.Warnings = append(result.Warnings, "title not found")
}
score(&result)
return result
}
func split(raw string) []string {
parts := strings.Split(strings.TrimSpace(raw), "-")
out := make([]string, 0, len(parts))
for _, part := range parts {
if part = strings.TrimSpace(part); part != "" {
out = append(out, part)
}
}
return out
}
// hasKnownTailToken reports whether any token is a known tail marker
// (format, source, bit_depth, resolution, edition, language or a year).
func hasKnownTailToken(tokens []string) bool {
for _, tok := range tokens {
up := strings.ToUpper(tok)
if Profile.FormatTokens[up] || Profile.SourceTokens[up] ||
Profile.BitDepthTokens[up] || Profile.ResolutionTokens[up] ||
Profile.EditionTokens[up] || Profile.LanguageMarkers[up] {
return true
}
if yearRegexp.MatchString(up) {
return true
}
}
return false
}
func score(result *releaseparser.ParsedRelease) {
score := 0.0
if result.Group != "" {
score += 0.25
}
if result.Year != 0 {
score += 0.20
}
if result.Format != "" {
score += 0.15
}
if result.Source != "" {
score += 0.15
}
if result.Artist != "" {
score += 0.15
}
if len(result.Warnings) == 0 {
score += 0.10
}
if len(result.UnknownTokens) > 0 {
score -= 0.10
}
if score > 1 {
score = 1
}
if score < 0 {
score = 0
}
result.Confidence = score
}