diff --git a/cmd/zurg/main.go b/cmd/zurg/main.go index f63d6e5..1305f9a 100644 --- a/cmd/zurg/main.go +++ b/cmd/zurg/main.go @@ -18,6 +18,11 @@ import ( ) func main() { + if len(os.Args) > 1 && os.Args[1] == "networktest" { + realdebrid.RunTest() + return + } + log := logutil.NewLogger().Named("zurg") config, configErr := config.LoadZurgConfig("./config.yml") diff --git a/pkg/realdebrid/network.go b/pkg/realdebrid/network.go new file mode 100644 index 0000000..c33e62d --- /dev/null +++ b/pkg/realdebrid/network.go @@ -0,0 +1,106 @@ +package realdebrid + +import ( + "fmt" + "os/exec" + "sort" + "strconv" + "strings" + "sync" + "time" +) + +type IPInfo struct { + Address string + Hops int + Latency time.Duration +} + +func traceroute(ip string) (int, time.Duration, error) { + cmd := exec.Command("traceroute", "-n", "-q", "1", "-w", "1", ip) + out, err := cmd.CombinedOutput() + if err != nil { + return 0, 0, err + } + + output := string(out) + lines := strings.Split(output, "\n") + + hopCount := len(lines) - 1 + + var latency time.Duration + if hopCount > 0 { + lastLine := lines[hopCount-1] + fmt.Println(lastLine) + parts := strings.Fields(lastLine) + if len(parts) >= 3 { + latencyValue, parseErr := strconv.ParseFloat(parts[2], 64) + if parseErr == nil { + latency = time.Duration(latencyValue * float64(time.Millisecond)) + } + } + } + + return hopCount, latency, nil +} + +func RunTest() { + fmt.Println("Running network test...") + + ips := []string{"20.download.real-debrid.cloud", "20.download.real-debrid.com", "21.download.real-debrid.cloud", "21.download.real-debrid.com", "22.download.real-debrid.cloud", "22.download.real-debrid.com", "23.download.real-debrid.cloud", "23.download.real-debrid.com", "30.download.real-debrid.cloud", "30.download.real-debrid.com", "31.download.real-debrid.cloud", "31.download.real-debrid.com", "32.download.real-debrid.cloud", "32.download.real-debrid.com", "34.download.real-debrid.cloud", "34.download.real-debrid.com", "40.download.real-debrid.cloud", "40.download.real-debrid.com", "41.download.real-debrid.cloud", "41.download.real-debrid.com", "42.download.real-debrid.cloud", "42.download.real-debrid.com", "43.download.real-debrid.cloud", "43.download.real-debrid.com", "44.download.real-debrid.cloud", "44.download.real-debrid.com", "45.download.real-debrid.cloud", "45.download.real-debrid.com", "50.download.real-debrid.cloud", "50.download.real-debrid.com", "51.download.real-debrid.cloud", "51.download.real-debrid.com", "52.download.real-debrid.cloud", "52.download.real-debrid.com", "53.download.real-debrid.cloud", "53.download.real-debrid.com", "54.download.real-debrid.cloud", "54.download.real-debrid.com", "55.download.real-debrid.cloud", "55.download.real-debrid.com", "56.download.real-debrid.cloud", "56.download.real-debrid.com", "57.download.real-debrid.cloud", "57.download.real-debrid.com", "58.download.real-debrid.cloud", "58.download.real-debrid.com", "59.download.real-debrid.cloud", "59.download.real-debrid.com", "60.download.real-debrid.cloud", "60.download.real-debrid.com", "61.download.real-debrid.cloud", "61.download.real-debrid.com", "62.download.real-debrid.cloud", "62.download.real-debrid.com", "63.download.real-debrid.cloud", "63.download.real-debrid.com", "64.download.real-debrid.cloud", "64.download.real-debrid.com", "65.download.real-debrid.cloud", "65.download.real-debrid.com", "66.download.real-debrid.cloud", "66.download.real-debrid.com", "67.download.real-debrid.cloud", "67.download.real-debrid.com", "68.download.real-debrid.cloud", "68.download.real-debrid.com", "69.download.real-debrid.cloud", "69.download.real-debrid.com", "hkg1.download.real-debrid.com", "lax1.download.real-debrid.com", "lon1.download.real-debrid.com", "mum1.download.real-debrid.com", "rbx.download.real-debrid.com", "sgp1.download.real-debrid.com", "tlv1.download.real-debrid.com", "tyo1.download.real-debrid.com"} + + var wg sync.WaitGroup + infoChan := make(chan IPInfo, len(ips)) + semaphore := make(chan struct{}, 10) + + for _, ip := range ips { + wg.Add(1) + semaphore <- struct{}{} + go func(ip string) { + defer wg.Done() + hops, latency, err := traceroute(ip) + if err != nil { + fmt.Println("Error performing traceroute:", err) + } else { + infoChan <- IPInfo{Address: ip, Hops: hops, Latency: latency} + } + <-semaphore + }(ip) + } + + wg.Wait() + close(infoChan) + + var ipInfos []IPInfo + for info := range infoChan { + ipInfos = append(ipInfos, info) + } + + sort.Slice(ipInfos, func(i, j int) bool { + if ipInfos[i].Hops == ipInfos[j].Hops { + return ipInfos[i].Latency < ipInfos[j].Latency + } + return ipInfos[i].Hops < ipInfos[j].Hops + }) + var lowestLatency time.Duration + if len(ipInfos) > 0 { + lowestLatency = ipInfos[0].Latency + for _, info := range ipInfos { + if info.Latency < lowestLatency { + lowestLatency = info.Latency + } + } + } + + latencyThreshold := lowestLatency + lowestLatency/3 + + var okIPs []IPInfo + for _, info := range ipInfos { + if info.Latency <= latencyThreshold { + okIPs = append(okIPs, info) + } + } + for _, info := range okIPs { + fmt.Printf("Host: %s, Hops: %d, Latency: %v\n", info.Address, info.Hops, info.Latency) + } +}