diff --git a/files-torrentgalaxy.go b/files-torrentgalaxy.go new file mode 100644 index 0000000..20284e3 --- /dev/null +++ b/files-torrentgalaxy.go @@ -0,0 +1,185 @@ +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) +} diff --git a/files.go b/files.go new file mode 100644 index 0000000..dc7baf7 --- /dev/null +++ b/files.go @@ -0,0 +1,123 @@ +package main + +import ( + "fmt" + "html/template" + "log" + "net/http" + "sort" + "time" +) + +type Settings struct { + UxLang string + Safe string +} + +type TorrentSite interface { + Name() string + Search(query string, category string) ([]TorrentResult, error) +} + +type TorrentResult struct { + URL string + Seeders int + Leechers int + Magnet string + Views int + Size string + Title string + Error string +} + +var ( + torrentGalaxy TorrentSite + nyaa TorrentSite + thePirateBay TorrentSite + rutor TorrentSite +) + +func initializeTorrentSites() { + torrentGalaxy = NewTorrentGalaxy() + // nyaa = NewNyaa() + // thePirateBay = NewThePirateBay() + // rutor = NewRutor() +} + +func handleFileSearch(w http.ResponseWriter, query, safe, lang string, page int) { + startTime := time.Now() + + settings := Settings{UxLang: lang, Safe: safe} + sites := []TorrentSite{torrentGalaxy, nyaa, thePirateBay, rutor} + results := []TorrentResult{} + allErrors := true + + for _, site := range sites { + if site == nil { + continue + } + res, err := site.Search(query, "all") + if err != nil { + continue + } + if len(res) > 0 { + allErrors = false + } + results = append(results, res...) + } + + if allErrors { + results = []TorrentResult{ + {Error: "Results are currently unavailable, sorry. Please try again later."}, + } + } + + sort.Slice(results, func(i, j int) bool { return results[i].Seeders > results[j].Seeders }) + + elapsedTime := time.Since(startTime) + funcMap := template.FuncMap{ + "sub": subtract, + "add": add, + } + tmpl, err := template.New("files.html").Funcs(funcMap).ParseFiles("templates/files.html") + if err != nil { + log.Printf("Failed to load template: %v", err) + http.Error(w, "Failed to load template", http.StatusInternalServerError) + return + } + + data := struct { + Results []TorrentResult + Query string + Fetched string + Category string + Sort string + HasPrevPage bool + HasNextPage bool + Page int + Settings Settings + }{ + Results: results, + Query: query, + Fetched: fmt.Sprintf("%.2f", elapsedTime.Seconds()), + Category: "all", + Sort: "seed", + HasPrevPage: page > 1, + HasNextPage: len(results) > 0, + Page: page, + Settings: settings, + } + + if err := tmpl.Execute(w, data); err != nil { + log.Printf("Failed to render template: %v", err) + http.Error(w, "Failed to render template", http.StatusInternalServerError) + } +} + +func subtract(a, b int) int { + return a - b +} + +func add(a, b int) int { + return a + b +} diff --git a/forums.go b/forums.go index 348bc6c..be2d6a0 100644 --- a/forums.go +++ b/forums.go @@ -1,4 +1,3 @@ -// forums.go package main import ( @@ -23,13 +22,13 @@ type ForumSearchResult struct { func PerformRedditSearch(query string, safe string, page int) ([]ForumSearchResult, error) { const ( pageSize = 25 - baseURL = "https://www.reddit.com/" + baseURL = "https://www.reddit.com" maxRetries = 5 initialBackoff = 2 * time.Second ) var results []ForumSearchResult - searchURL := fmt.Sprintf("%ssearch.json?q=%s&limit=%d&start=%d", baseURL, url.QueryEscape(query), pageSize, page*pageSize) + searchURL := fmt.Sprintf("%s/search.json?q=%s&limit=%d&start=%d", baseURL, url.QueryEscape(query), pageSize, page*pageSize) var resp *http.Response var err error @@ -86,7 +85,7 @@ func PerformRedditSearch(query string, safe string, page int) ([]ForumSearchResu } publishedDate := time.Unix(int64(postData["created_utc"].(float64)), 0) permalink := postData["permalink"].(string) - resultURL := baseURL + permalink + resultURL := fmt.Sprintf("%s%s", baseURL, permalink) result := ForumSearchResult{ URL: resultURL, diff --git a/images.go b/images.go index a588348..572f8bc 100644 --- a/images.go +++ b/images.go @@ -51,7 +51,7 @@ func fetchImageResults(query string, safe, lang string, page int) ([]ImageSearch const resultsPerPage = 50 var offset int if page <= 1 { - offset = 0 // Assuming the API expects offset to start from 0 for the first page + offset = 0 } else { offset = (page - 1) * resultsPerPage } diff --git a/main.go b/main.go index 9a5ec3a..a10f225 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,3 @@ -// main.go package main import ( @@ -72,6 +71,7 @@ func main() { http.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "templates/settings.html") }) + initializeTorrentSites() fmt.Println("Server is listening on http://localhost:5000") log.Fatal(http.ListenAndServe(":5000", nil)) } @@ -88,11 +88,13 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { case "image": handleImageSearch(w, query, safe, lang, page) case "video": - videoSearchEndpointHandler(w, r) + handleVideoSearch(w, query, safe, lang, page) case "map": handleMapSearch(w, query, safe) case "forum": handleForumsSearch(w, query, safe, lang, page) + case "file": + handleFileSearch(w, query, safe, lang, page) case "text": fallthrough default: diff --git a/run.sh b/run.sh index f56f9fc..c217373 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,3 @@ #!/bin/bash -go run main.go text-google.go images.go imageproxy.go video.go map.go text.go text-quant.go text-duckduckgo.go cache.go forums.go --debug \ No newline at end of file +go run main.go text-google.go images.go imageproxy.go video.go map.go text.go text-quant.go text-duckduckgo.go cache.go forums.go files.go files-torrentgalaxy.go --debug \ No newline at end of file diff --git a/templates/files.html b/templates/files.html new file mode 100644 index 0000000..ebea712 --- /dev/null +++ b/templates/files.html @@ -0,0 +1,112 @@ + + +
+ + +{{ if .Views }}{{ .Views }} views • {{ end }}{{ .Size }}
+Seeders: {{ .Seeders }} | Leechers: {{ .Leechers }}
+ {{ end }} +