package main import ( "fmt" "log" "net/http" "net/url" "regexp" "strconv" "strings" "github.com/PuerkitoBio/goquery" ) const TORRENTGALAXY_DOMAIN = "torrentgalaxy.to" type TorrentGalaxy struct{} func NewTorrentGalaxy() *TorrentGalaxy { return &TorrentGalaxy{} } func (tg *TorrentGalaxy) Name() string { return "torrentgalaxy" } func (tg *TorrentGalaxy) getCategoryCode(category string) string { switch category { case "all": return "" case "audiobook": return "&c13=1" case "movie": return "&c3=1&c46=1&c45=1&c42=1&c4=1&c1=1" case "tv": return "&c41=1&c5=1&c11=1&c6=1&c7=1" case "games": return "&c43=1&c10=1" case "software": return "&c20=1&c21=1&c18=1" case "anime": return "&c28=1" case "music": return "&c28=1&c22=1&c26=1&c23=1&c25=1&c24=1" case "xxx": safeSearch := true // Replace with actual safe search status if safeSearch { return "ignore" } return "&c48=1&c35=1&c47=1&c34=1" default: return "" } } func (tg *TorrentGalaxy) Search(query string, category string) ([]TorrentResult, error) { categoryCode := tg.getCategoryCode(category) if categoryCode == "ignore" { return []TorrentResult{}, nil } searchURL := fmt.Sprintf("https://%s/torrents.php?search=%s%s#results", TORRENTGALAXY_DOMAIN, url.QueryEscape(query), categoryCode) resp, err := http.Get(searchURL) if err != nil { return nil, fmt.Errorf("error making request to TorrentGalaxy: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } doc, err := goquery.NewDocumentFromReader(resp.Body) if err != nil { return nil, fmt.Errorf("error parsing HTML: %w", err) } var results []TorrentResult doc.Find("div.tgxtablerow").Each(func(i int, s *goquery.Selection) { titleDiv := s.Find("div#click") title := strings.TrimSpace(titleDiv.Text()) magnetLink, exists := s.Find("a[href^='magnet']").Attr("href") if !exists { log.Printf("No magnet link found for title: %s", title) return } byteSize := parseSize(s.Find("span.badge-secondary").Text()) viewCount := parseInt(s.Find("font[color='orange']").Text()) seeder := parseInt(s.Find("font[color='green']").Text()) leecher := parseInt(s.Find("font[color='#ff0000']").Text()) result := TorrentResult{ URL: fmt.Sprintf("https://%s", TORRENTGALAXY_DOMAIN), Seeders: seeder, Leechers: leecher, Magnet: applyTrackers(magnetLink), Views: viewCount, Size: formatSize(byteSize), Title: title, } results = append(results, result) }) return results, nil } func parseInt(s string) int { s = strings.ReplaceAll(s, ",", "") result, err := strconv.Atoi(s) if err != nil { log.Printf("Error parsing integer: %v", err) return 0 } return result } func parseSize(sizeStr string) int64 { sizeStr = strings.TrimSpace(sizeStr) if sizeStr == "" { return 0 } // Use regex to extract numeric value and unit separately re := regexp.MustCompile(`(?i)([\d.]+)\s*([KMGT]?B)`) matches := re.FindStringSubmatch(sizeStr) if len(matches) < 3 { log.Printf("Error parsing size: invalid format %s", sizeStr) return 0 } sizeStr = matches[1] unit := strings.ToUpper(matches[2]) var multiplier int64 = 1 switch unit { case "KB": multiplier = 1024 case "MB": multiplier = 1024 * 1024 case "GB": multiplier = 1024 * 1024 * 1024 case "TB": multiplier = 1024 * 1024 * 1024 * 1024 default: log.Printf("Unknown unit: %s", unit) return 0 } size, err := strconv.ParseFloat(sizeStr, 64) if err != nil { log.Printf("Error parsing size: %v", err) return 0 } return int64(size * float64(multiplier)) } func applyTrackers(magnetLink string) string { if magnetLink == "" { return "" } trackers := []string{ "udp://tracker.openbittorrent.com:80/announce", "udp://tracker.opentrackr.org:1337/announce", "udp://tracker.coppersurfer.tk:6969/announce", "udp://tracker.leechers-paradise.org:6969/announce", } for _, tracker := range trackers { magnetLink += "&tr=" + url.QueryEscape(tracker) } return magnetLink } func formatSize(size int64) string { if size >= 1024*1024*1024*1024 { return fmt.Sprintf("%.2f TB", float64(size)/(1024*1024*1024*1024)) } else if size >= 1024*1024*1024 { return fmt.Sprintf("%.2f GB", float64(size)/(1024*1024*1024)) } else if size >= 1024*1024 { return fmt.Sprintf("%.2f MB", float64(size)/(1024*1024)) } else if size >= 1024 { return fmt.Sprintf("%.2f KB", float64(size)/1024) } return fmt.Sprintf("%d B", size) }