252 lines
6.3 KiB
Go
252 lines
6.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Settings struct {
|
|
UxLang string
|
|
Safe string
|
|
}
|
|
|
|
type TorrentSite interface {
|
|
Name() string
|
|
Search(query string, category string) ([]TorrentResult, error)
|
|
}
|
|
|
|
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()
|
|
|
|
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "file"}
|
|
combinedResults := getFileResultsFromCacheOrFetch(cacheKey, query, safe, lang, page)
|
|
|
|
sort.Slice(combinedResults, func(i, j int) bool { return combinedResults[i].Seeders > combinedResults[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: combinedResults,
|
|
Query: query,
|
|
Fetched: fmt.Sprintf("%.2f", elapsedTime.Seconds()),
|
|
Category: "all",
|
|
Sort: "seed",
|
|
HasPrevPage: page > 1,
|
|
HasNextPage: len(combinedResults) > 0,
|
|
Page: page,
|
|
Settings: Settings{UxLang: lang, Safe: safe},
|
|
}
|
|
|
|
// Debugging: Print results before rendering template
|
|
for _, result := range combinedResults {
|
|
fmt.Printf("Title: %s, Magnet: %s\n", result.Title, result.Magnet)
|
|
}
|
|
|
|
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 getFileResultsFromCacheOrFetch(cacheKey CacheKey, query, safe, lang string, page int) []TorrentResult {
|
|
cacheChan := make(chan []SearchResult)
|
|
var combinedResults []TorrentResult
|
|
|
|
go func() {
|
|
results, exists := resultsCache.Get(cacheKey)
|
|
if exists {
|
|
log.Println("Cache hit")
|
|
cacheChan <- results
|
|
} else {
|
|
log.Println("Cache miss")
|
|
cacheChan <- nil
|
|
}
|
|
}()
|
|
|
|
select {
|
|
case results := <-cacheChan:
|
|
if results == nil {
|
|
combinedResults = fetchAndCacheFileResults(query, safe, lang, page)
|
|
} else {
|
|
_, torrentResults, _ := convertToSpecificResults(results)
|
|
combinedResults = torrentResults
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
log.Println("Cache check timeout")
|
|
combinedResults = fetchAndCacheFileResults(query, safe, lang, page)
|
|
}
|
|
|
|
return combinedResults
|
|
}
|
|
|
|
func fetchAndCacheFileResults(query, safe, lang string, page int) []TorrentResult {
|
|
sites := []TorrentSite{torrentGalaxy, nyaa, thePirateBay, rutor}
|
|
results := []TorrentResult{}
|
|
|
|
for _, site := range sites {
|
|
if site == nil {
|
|
continue
|
|
}
|
|
res, err := site.Search(query, "all")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, r := range res {
|
|
r.Magnet = removeMagnetLink(r.Magnet) // Remove "magnet:", prehaps usless now?
|
|
results = append(results, r)
|
|
}
|
|
}
|
|
|
|
// Cache the valid results
|
|
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "file"}
|
|
resultsCache.Set(cacheKey, convertToSearchResults(results))
|
|
|
|
return results
|
|
}
|
|
|
|
func fetchFileResults(query, safe, lang string, page int) []TorrentResult {
|
|
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "file"}
|
|
results := getFileResultsFromCacheOrFetch(cacheKey, query, safe, lang, page)
|
|
return results
|
|
}
|
|
|
|
func removeMagnetLink(magnet string) string {
|
|
// Remove the magnet: prefix unconditionally
|
|
return strings.TrimPrefix(magnet, "magnet:")
|
|
}
|
|
|
|
func subtract(a, b int) int {
|
|
return a - b
|
|
}
|
|
|
|
func add(a, b int) int {
|
|
return a + b
|
|
}
|
|
|
|
func parseInt(s string) int {
|
|
i, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return i
|
|
}
|
|
|
|
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))
|
|
}
|
|
|
|
// apparently this is needed so it can announce that magnet link is being used and people start seeding it, but I dont like the fact that I add trackers purposefully
|
|
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)
|
|
}
|
|
|
|
func sanitizeFileName(name string) string {
|
|
// Replace spaces with dashes
|
|
sanitized := regexp.MustCompile(`\s+`).ReplaceAllString(name, "-")
|
|
// Remove any characters that are not alphanumeric, dashes, or parentheses
|
|
sanitized = regexp.MustCompile(`[^a-zA-Z0-9\-\(\)]`).ReplaceAllString(sanitized, "")
|
|
return sanitized
|
|
}
|