package sorter import ( "fmt" "os" "path/filepath" "regexp" "github.com/veggiedefender/torrent-client/internal/logger" ) var ( tvShowRegex = regexp.MustCompile(`(?i)(s\d{2}e\d{2}|season\s*\d+|сезон\s*\d+)`) movieRegex = regexp.MustCompile(`(?i)(1080p|720p|2160p|4k|bdrip|web-dl|bluray|camrip)`) gameRegex = regexp.MustCompile(`(?i)(repack|fitgirl|dodi|skidrow|codex|crack|iso)`) musicRegex = regexp.MustCompile(`(?i)(discography|flac|mp3\s*320)`) ) // Categorize analyzes the torrent name and determines its category. func Categorize(name string) string { if tvShowRegex.MatchString(name) { return "TV Shows" } if movieRegex.MatchString(name) { return "Movies" } if gameRegex.MatchString(name) { return "Games" } if musicRegex.MatchString(name) { return "Music" } return "Other" } // SortAndMove moves the downloaded files (or folder) to a sub-folder based on its category. func SortAndMove(outputRoot, torrentName string) (string, error) { category := Categorize(torrentName) if category == "Other" || category == "" { return "", nil // Do not move if category is unknown } sourcePath := filepath.Join(outputRoot, torrentName) if _, err := os.Stat(sourcePath); os.IsNotExist(err) { return "", fmt.Errorf("source path does not exist: %s", sourcePath) } destDir := filepath.Join(outputRoot, category) if err := os.MkdirAll(destDir, 0o755); err != nil { return "", fmt.Errorf("failed to create category directory: %w", err) } destPath := filepath.Join(destDir, torrentName) // If it already exists in the destination, maybe we resume/overwrite if _, err := os.Stat(destPath); err == nil { logger.Warn("SYS", "Destination path already exists, skipping move: %s", destPath) return "", nil } if err := os.Rename(sourcePath, destPath); err != nil { return "", fmt.Errorf("failed to move files to category folder: %w", err) } logger.Info("SYS", "Smart Sorter moved '%s' to '%s'", torrentName, category) return destDir, nil }