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 }