Add traffic monitor

This commit is contained in:
Ben Adrian Sarmiento
2024-06-24 13:05:11 +02:00
parent 921908c890
commit 4049fa7d83
5 changed files with 113 additions and 14 deletions

View File

@@ -150,6 +150,7 @@ func MainApp(configPath string) {
config, config,
api, api,
workerPool, workerPool,
hosts,
log.Named("router"), log.Named("router"),
) )

View File

@@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"os" "os"
"runtime" "runtime"
"sort"
"strings" "strings"
"github.com/debridmediamanager/zurg/internal/config" "github.com/debridmediamanager/zurg/internal/config"
@@ -25,7 +26,8 @@ type RootResponse struct {
Dav string `json:"dav"` Dav string `json:"dav"`
Infuse string `json:"infuse"` Infuse string `json:"infuse"`
Logs string `json:"logs"` Logs string `json:"logs"`
UserInfo *realdebrid.User `json:"user_info"` // Replace UserInfoType with the actual type UserInfo *realdebrid.User `json:"user_info"`
RDTrafficUsed uint64 `json:"rd_traffic_used"`
LibrarySize int `json:"library_size"` // Number of torrents in the library LibrarySize int `json:"library_size"` // Number of torrents in the library
RepairQueueStr string `json:"repair_queue"` // List of torrents in the repair queue RepairQueueStr string `json:"repair_queue"` // List of torrents in the repair queue
MemAlloc uint64 `json:"mem_alloc"` // Memory allocation in MB MemAlloc uint64 `json:"mem_alloc"` // Memory allocation in MB
@@ -35,7 +37,8 @@ type RootResponse struct {
PID int `json:"pid"` // Process ID PID int `json:"pid"` // Process ID
Sponsor SponsorResponse `json:"sponsor_zurg"` // Sponsorship links Sponsor SponsorResponse `json:"sponsor_zurg"` // Sponsorship links
Config config.ZurgConfig `json:"config"` Config config.ZurgConfig `json:"config"`
OnceDoneBin []string `json:"once_done_bin"` IDsToDelete []string `json:"ids_to_delete"`
Hosts []string `json:"hosts"`
} }
func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) { func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
@@ -45,6 +48,12 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
return return
} }
trafficDetails, err := zr.api.GetTrafficDetails()
if err != nil {
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
var mem runtime.MemStats var mem runtime.MemStats
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
@@ -65,6 +74,16 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
} }
} }
sortedIDs := zr.torMgr.OnceDoneBin.ToSlice()
sort.Strings(sortedIDs)
// check if real-debrid.com is in the traffic details
var rdTrafficUsed int64
rdTrafficUsed = 0
if _, ok := trafficDetails["real-debrid.com"]; ok {
rdTrafficUsed = trafficDetails["real-debrid.com"]
}
response := RootResponse{ response := RootResponse{
Version: version.GetVersion(), Version: version.GetVersion(),
BuiltAt: version.GetBuiltAt(), BuiltAt: version.GetBuiltAt(),
@@ -74,6 +93,7 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
Infuse: fmt.Sprintf("//%s/infuse/", req.Host), Infuse: fmt.Sprintf("//%s/infuse/", req.Host),
Logs: fmt.Sprintf("//%s/logs/", req.Host), Logs: fmt.Sprintf("//%s/logs/", req.Host),
UserInfo: userInfo, UserInfo: userInfo,
RDTrafficUsed: bToGb(uint64(rdTrafficUsed)),
LibrarySize: allTorrents.Count(), LibrarySize: allTorrents.Count(),
RepairQueueStr: repairQueueStr, RepairQueueStr: repairQueueStr,
MemAlloc: bToMb(mem.Alloc), MemAlloc: bToMb(mem.Alloc),
@@ -87,7 +107,8 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
Paypal: "https://paypal.me/yowmamasita", Paypal: "https://paypal.me/yowmamasita",
}, },
Config: zr.cfg.GetConfig(), Config: zr.cfg.GetConfig(),
OnceDoneBin: zr.torMgr.OnceDoneBin.ToSlice(), IDsToDelete: sortedIDs,
Hosts: zr.hosts,
} }
out := `<table border="1px"> out := `<table border="1px">
@@ -126,10 +147,6 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
<td>Library Size</td> <td>Library Size</td>
<td colspan="2">%d items</td> <td colspan="2">%d items</td>
</tr> </tr>
<tr>
<td>Repair Queue</td>
<td colspan="2">%s</td>
</tr>
<tr> <tr>
<td>Memory Allocation</td> <td>Memory Allocation</td>
<td colspan="2">%d MB</td> <td colspan="2">%d MB</td>
@@ -150,6 +167,10 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
<td>Process ID</td> <td>Process ID</td>
<td colspan="2">%d</td> <td colspan="2">%d</td>
</tr> </tr>
<tr>
<td>RD Traffic Used</td>
<td colspan="2">%d GB</td>
</tr>
<tr> <tr>
<td rowspan="3">Sponsor Zurg</td> <td rowspan="3">Sponsor Zurg</td>
<td>Patreon</td> <td>Patreon</td>
@@ -189,7 +210,7 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
<td>%s</td> <td>%s</td>
</tr> </tr>
<tr> <tr>
<td rowspan="20">Config</td> <td rowspan="23">Config</td>
<td>Version</td> <td>Version</td>
<td>%s</td> <td>%s</td>
</tr> </tr>
@@ -253,6 +274,18 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
<td>Retries Until Failed</td> <td>Retries Until Failed</td>
<td>%d</td> <td>%d</td>
</tr> </tr>
<tr>
<td>Auto-Analyze New Torrents</td>
<td>%t</td>
</tr>
<tr>
<td>Cache Network Test Results</td>
<td>%t</td>
</tr>
<tr>
<td>Additional Playable Extensions</td>
<td>%s</td>
</tr>
<tr> <tr>
<td>Network Buffer Size</td> <td>Network Buffer Size</td>
<td>%d bytes</td> <td>%d bytes</td>
@@ -273,6 +306,14 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
<td>IDs to be deleted</td> <td>IDs to be deleted</td>
<td colspan="2">%v</td> <td colspan="2">%v</td>
</tr> </tr>
<tr>
<td>Repair Queue</td>
<td colspan="2">%s</td>
</tr>
<tr>
<td>Hosts</td>
<td colspan="2">%v</td>
</tr>
<tr> <tr>
<td>Utilities</td> <td>Utilities</td>
<td colspan="2"> <td colspan="2">
@@ -324,12 +365,12 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
response.Logs, response.Logs,
response.Logs, response.Logs,
response.LibrarySize, response.LibrarySize,
response.RepairQueueStr,
response.MemAlloc, response.MemAlloc,
response.TotalAlloc, response.TotalAlloc,
response.Sys, response.Sys,
response.NumGC, response.NumGC,
response.PID, response.PID,
response.RDTrafficUsed,
response.Sponsor.Patreon, response.Sponsor.Patreon,
response.Sponsor.Patreon, response.Sponsor.Patreon,
response.Sponsor.Github, response.Sponsor.Github,
@@ -360,11 +401,16 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
response.Config.GetApiTimeoutSecs(), response.Config.GetApiTimeoutSecs(),
response.Config.GetDownloadTimeoutSecs(), response.Config.GetDownloadTimeoutSecs(),
response.Config.GetRetriesUntilFailed(), response.Config.GetRetriesUntilFailed(),
response.Config.ShouldAutoAnalyzeNewTorrents(),
response.Config.ShouldCacheNetworkTestResults(),
response.Config.GetPlayableExtensions(),
response.Config.GetNetworkBufferSize(), response.Config.GetNetworkBufferSize(),
response.Config.ShouldServeFromRclone(), response.Config.ShouldServeFromRclone(),
response.Config.ShouldForceIPv6(), response.Config.ShouldForceIPv6(),
response.Config.GetOnLibraryUpdate(), response.Config.GetOnLibraryUpdate(),
response.OnceDoneBin, response.IDsToDelete,
response.RepairQueueStr,
response.Hosts,
) )
fmt.Fprint(resp, out) fmt.Fprint(resp, out)
@@ -373,3 +419,7 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
func bToMb(b uint64) uint64 { func bToMb(b uint64) uint64 {
return b / 1024 / 1024 return b / 1024 / 1024
} }
func bToGb(b uint64) uint64 {
return b / 1024 / 1024 / 1024
}

View File

@@ -25,6 +25,7 @@ type Handlers struct {
cfg config.ConfigInterface cfg config.ConfigInterface
api *realdebrid.RealDebrid api *realdebrid.RealDebrid
workerPool *ants.Pool workerPool *ants.Pool
hosts []string
log *logutil.Logger log *logutil.Logger
} }
@@ -34,13 +35,14 @@ func init() {
chi.RegisterMethod("MOVE") chi.RegisterMethod("MOVE")
} }
func AttachHandlers(router *chi.Mux, downloader *universal.Downloader, torMgr *torrent.TorrentManager, cfg config.ConfigInterface, api *realdebrid.RealDebrid, workerPool *ants.Pool, log *logutil.Logger) { func AttachHandlers(router *chi.Mux, downloader *universal.Downloader, torMgr *torrent.TorrentManager, cfg config.ConfigInterface, api *realdebrid.RealDebrid, workerPool *ants.Pool, hosts []string, log *logutil.Logger) {
hs := &Handlers{ hs := &Handlers{
downloader: downloader, downloader: downloader,
torMgr: torMgr, torMgr: torMgr,
cfg: cfg, cfg: cfg,
api: api, api: api,
workerPool: workerPool, workerPool: workerPool,
hosts: hosts,
log: log, log: log,
} }

View File

@@ -93,15 +93,15 @@ func (r *IPRepository) GetHosts(numberOfHosts int, ipv6 bool) []string {
return kvList[i].Value < kvList[j].Value return kvList[i].Value < kvList[j].Value
}) })
var optimalHosts []string var hosts []string
if numberOfHosts == 0 { if numberOfHosts == 0 {
numberOfHosts = len(kvList) numberOfHosts = len(kvList)
} }
for i := 0; i < numberOfHosts && i < len(kvList); i++ { for i := 0; i < numberOfHosts && i < len(kvList); i++ {
optimalHosts = append(optimalHosts, kvList[i].Key) hosts = append(hosts, kvList[i].Key)
} }
return optimalHosts return hosts
} }
func (r *IPRepository) runLatencyTest() { func (r *IPRepository) runLatencyTest() {

View File

@@ -297,6 +297,52 @@ func (rd *RealDebrid) GetUserInformation() (*User, error) {
return &user, nil return &user, nil
} }
// TrafficDetails represents the structure of the traffic details response
type TrafficDetails map[string]struct {
Host map[string]int64 `json:"host"`
Bytes int64 `json:"bytes"`
}
// GetTrafficDetails gets the traffic details from the Real-Debrid API
func (rd *RealDebrid) GetTrafficDetails() (map[string]int64, error) {
// Construct request URL
reqURL := "https://api.real-debrid.com/rest/1.0/traffic/details"
req, err := http.NewRequest(http.MethodGet, reqURL, nil)
if err != nil {
rd.log.Errorf("Error when creating a traffic details request: %v", err)
return nil, err
}
// Send the request
resp, err := rd.apiClient.Do(req)
if err != nil {
rd.log.Errorf("Error when executing the traffic details request: %v", err)
return nil, err
}
defer resp.Body.Close()
// Decode the JSON response into the TrafficDetails struct
var trafficDetails TrafficDetails
err = json.NewDecoder(resp.Body).Decode(&trafficDetails)
if err != nil {
rd.log.Errorf("Error when decoding traffic details JSON: %v", err)
return nil, err
}
// Find the latest date in the traffic details
var latestDate string
for date := range trafficDetails {
if latestDate == "" || date > latestDate {
latestDate = date
}
}
// Get the traffic details for the latest date
latestTraffic := trafficDetails[latestDate].Host
return latestTraffic, nil
}
// AvailabilityCheck checks the instant availability of torrents // AvailabilityCheck checks the instant availability of torrents
func (rd *RealDebrid) AvailabilityCheck(hashes []string) (AvailabilityResponse, error) { func (rd *RealDebrid) AvailabilityCheck(hashes []string) (AvailabilityResponse, error) {
if len(hashes) == 0 { if len(hashes) == 0 {