stremio/node/media-node/internal/scanner/parser.go
Jos Vooges | STH a5c6fd5fe4 Initial commit: Distributed Stremio Media Cluster V1
Master API, Admin UI, Go media-node, Docker/Dokploy deploy configs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 00:17:04 +02:00

98 lines
2.6 KiB
Go

package scanner
import (
"regexp"
"strings"
)
var (
yearRe = regexp.MustCompile(`[\.\s_-]((19|20)\d{2})[\.\s_-]`)
resolutionRe = regexp.MustCompile(`(?i)\b(2160p|1080p|720p|480p|4K)\b`)
sourceRe = regexp.MustCompile(`(?i)\b(UHD\s*BluRay|BluRay|WEB-DL|WEBRip|HDTV|DVDRip)\b`)
releaseRe = regexp.MustCompile(`(?i)\b(REMUX|PROPER|REPACK)\b`)
videoRe = regexp.MustCompile(`(?i)\b(HEVC|H\.?264|H\.?265|x264|x265|AV1|XviD)\b`)
audioRe = regexp.MustCompile(`(?i)\b(TrueHD(\s*Atmos)?|DTS-HD|DTS|AAC|AC3|Atmos|EAC3)\b`)
seriesRe1 = regexp.MustCompile(`(?i)(.+?)[\.\s_-]S(\d{1,2})E(\d{1,2})`)
seriesRe2 = regexp.MustCompile(`(?i)(.+?)[\.\s_-](\d{1,2})x(\d{1,2})`)
)
func parseMovie(base string) map[string]interface{} {
yearMatch := yearRe.FindStringSubmatchIndex(base)
var titlePart string
var year int
if len(yearMatch) >= 4 {
titlePart = base[:yearMatch[0]]
year = atoi(base[yearMatch[2]:yearMatch[3]])
} else {
titlePart = base
}
title := cleanTitle(titlePart)
if title == "" {
return nil
}
result := map[string]interface{}{"title": title}
if year > 0 {
result["year"] = year
}
if m := resolutionRe.FindStringSubmatch(base); len(m) > 1 {
result["resolution"] = m[1]
}
if m := sourceRe.FindStringSubmatch(base); len(m) > 1 {
result["source"] = m[1]
}
if m := releaseRe.FindStringSubmatch(base); len(m) > 1 {
result["releaseType"] = m[1]
}
if m := videoRe.FindStringSubmatch(base); len(m) > 1 {
result["videoCodec"] = m[1]
}
if m := audioRe.FindStringSubmatch(base); len(m) > 0 {
result["audio"] = m[0]
}
return result
}
func parseSeries(base string) map[string]interface{} {
for _, re := range []*regexp.Regexp{seriesRe1, seriesRe2} {
m := re.FindStringSubmatch(base)
if len(m) >= 4 {
title := cleanTitle(m[1])
season := atoi(m[2])
episode := atoi(m[3])
if title == "" || season <= 0 || episode <= 0 {
continue
}
result := map[string]interface{}{
"title": title,
"season": season,
"episode": episode,
}
if rm := resolutionRe.FindStringSubmatch(base); len(rm) > 1 {
result["resolution"] = rm[1]
}
if vm := videoRe.FindStringSubmatch(base); len(vm) > 1 {
result["videoCodec"] = vm[1]
}
if am := audioRe.FindStringSubmatch(base); len(am) > 0 {
result["audio"] = am[0]
}
return result
}
}
return nil
}
func cleanTitle(s string) string {
return strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(s, ".", " "), "_", " "))
}
func atoi(s string) int {
n := 0
for _, c := range s {
if c < '0' || c > '9' {
return 0
}
n = n*10 + int(c-'0')
}
return n
}