Master API, Admin UI, Go media-node, Docker/Dokploy deploy configs. Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
2.6 KiB
Go
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
|
|
}
|