package config import ( "regexp" "sort" "strings" "gopkg.in/yaml.v3" ) func loadV1Config(content []byte) (*ZurgConfigV1, error) { var configV1 ZurgConfigV1 if err := yaml.Unmarshal(content, &configV1); err != nil { return nil, err } return &configV1, nil } func (z *ZurgConfigV1) GetDirectories() []string { rootDirectories := make([]string, len(z.Directories)) i := 0 for directory := range z.Directories { rootDirectories[i] = directory i++ } return rootDirectories } func (z *ZurgConfigV1) GetGroupMap() map[string][]string { var groupMap = make(map[string][]string) var groupOrderMap = make(map[string]int) // To store GroupOrder for each directory // Populate the groupMap and groupOrderMap for directory, val := range z.Directories { groupMap[val.Group] = append(groupMap[val.Group], directory) groupOrderMap[directory] = val.GroupOrder } // Sort the slice based on GroupOrder and then directory name for deterministic order for group, dirs := range groupMap { sort.Slice(dirs, func(i, j int) bool { if groupOrderMap[dirs[i]] == groupOrderMap[dirs[j]] { return dirs[i] < dirs[j] // Use directory name as secondary sort criterion } return groupOrderMap[dirs[i]] < groupOrderMap[dirs[j]] }) groupMap[group] = dirs } // Return a deep copy of the map result := make(map[string][]string) for k, v := range groupMap { temp := make([]string, len(v)) copy(temp, v) result[k] = temp } return result } func (z *ZurgConfigV1) MeetsConditions(directory, torrentID, torrentName string, fileNames []string) bool { if _, ok := z.Directories[directory]; !ok { return false } for _, filter := range z.Directories[directory].Filters { if z.matchFilter(torrentID, torrentName, fileNames, filter) { return true } } return false } func (z *ZurgConfigV1) matchFilter(fileID, torrentName string, fileNames []string, filter *FilterConditionsV1) bool { if filter.ID != "" && fileID == filter.ID { return true } if filter.RegexStr != "" { regex := compilePattern(filter.RegexStr) if regex.MatchString(torrentName) { return true } } if filter.ContainsStrict != "" && strings.Contains(torrentName, filter.ContainsStrict) { return true } if filter.Contains != "" && strings.Contains(strings.ToLower(torrentName), strings.ToLower(filter.Contains)) { return true } if filter.NotContainsStrict != "" && !strings.Contains(torrentName, filter.NotContainsStrict) { return true } if filter.NotContains != "" && !strings.Contains(strings.ToLower(torrentName), strings.ToLower(filter.NotContains)) { return true } if len(filter.And) > 0 { andResult := true for _, andFilter := range filter.And { andResult = andResult && z.matchFilter(fileID, torrentName, fileNames, andFilter) if !andResult { return false } } return true } if len(filter.Or) > 0 { for _, orFilter := range filter.Or { if z.matchFilter(fileID, torrentName, fileNames, orFilter) { return true } } } if filter.FileInsideRegexStr != "" { regex := compilePattern(filter.FileInsideRegexStr) for _, filename := range fileNames { if regex.MatchString(filename) { return true } } } if filter.FileInsideContains != "" { for _, filename := range fileNames { if strings.Contains(strings.ToLower(filename), strings.ToLower(filter.FileInsideContains)) { return true } } } if filter.FileInsideContainsStrict != "" { for _, filename := range fileNames { if strings.Contains(filename, filter.FileInsideContainsStrict) { return true } } } return false } func compilePattern(pattern string) *regexp.Regexp { flags := map[rune]string{ 'i': "(?i)", 'm': "(?m)", 's': "(?s)", 'x': "(?x)", } lastSlash := strings.LastIndex(pattern, "/") secondLastSlash := strings.LastIndex(pattern[:lastSlash], "/") // Extract the core pattern corePattern := pattern[secondLastSlash+1 : lastSlash] // Extract and process flags flagSection := pattern[lastSlash+1:] flagString := "" processedFlags := make(map[rune]bool) for _, flag := range flagSection { if replacement, ok := flags[flag]; ok && !processedFlags[flag] { flagString += replacement processedFlags[flag] = true } } // Combine the processed flags with the core pattern finalPattern := flagString + corePattern // Validate pattern if finalPattern == "" || finalPattern == flagString { return nil } return regexp.MustCompile(finalPattern) }