Prevent stale torrents
This commit is contained in:
@@ -31,7 +31,6 @@ type RootResponse struct {
|
|||||||
Logs string `json:"logs"`
|
Logs string `json:"logs"`
|
||||||
UserInfo *realdebrid.User `json:"user_info"`
|
UserInfo *realdebrid.User `json:"user_info"`
|
||||||
APITraffic uint64 `json:"traffic_from_api"`
|
APITraffic uint64 `json:"traffic_from_api"`
|
||||||
RequestedMB uint64 `json:"requested_mb"`
|
|
||||||
ServedMB uint64 `json:"served_mb"`
|
ServedMB uint64 `json:"served_mb"`
|
||||||
LibrarySize int `json:"library_size"` // Number of torrents in the library
|
LibrarySize int `json:"library_size"` // Number of torrents in the library
|
||||||
TorrentsToRepair string `json:"repair_queue"` // List of torrents in the repair queue
|
TorrentsToRepair string `json:"repair_queue"` // List of torrents in the repair queue
|
||||||
@@ -103,7 +102,6 @@ func (zr *Handlers) generateResponse(resp http.ResponseWriter, req *http.Request
|
|||||||
Logs: fmt.Sprintf("//%s/logs/", req.Host),
|
Logs: fmt.Sprintf("//%s/logs/", req.Host),
|
||||||
UserInfo: userInfo,
|
UserInfo: userInfo,
|
||||||
APITraffic: uint64(trafficFromAPI),
|
APITraffic: uint64(trafficFromAPI),
|
||||||
RequestedMB: bToMb(zr.downloader.RequestedBytes.Load()),
|
|
||||||
ServedMB: bToMb(zr.downloader.TotalBytes.Load()),
|
ServedMB: bToMb(zr.downloader.TotalBytes.Load()),
|
||||||
LibrarySize: allTorrents.Count(),
|
LibrarySize: allTorrents.Count(),
|
||||||
TorrentsToRepair: repairQueueStr,
|
TorrentsToRepair: repairQueueStr,
|
||||||
@@ -193,7 +191,7 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
|
|||||||
response.Logs,
|
response.Logs,
|
||||||
)
|
)
|
||||||
|
|
||||||
denominator := response.RequestedMB
|
denominator := bToMb(response.APITraffic - zr.trafficOnStartup.Load())
|
||||||
if denominator == 0 {
|
if denominator == 0 {
|
||||||
denominator = 1
|
denominator = 1
|
||||||
}
|
}
|
||||||
@@ -234,17 +232,13 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
|
|||||||
<td>Traffic Logged</td>
|
<td>Traffic Logged</td>
|
||||||
<td colspan="2">%d MB (%d MB added)</td>
|
<td colspan="2">%d MB (%d MB added)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>Traffic Requested</td>
|
|
||||||
<td colspan="2">%d MB</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Traffic Served</td>
|
<td>Traffic Served</td>
|
||||||
<td colspan="2">%d MB</td>
|
<td colspan="2">%d MB</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Traffic Efficiency</td>
|
<td>Traffic Efficiency</td>
|
||||||
<td colspan="2">%d%% (wasted %d MB)</td>
|
<td colspan="2">%d%% (wasted %d MB) disclaimer: this assumes all RD traffic comes from this zurg instance</td>
|
||||||
</tr>`,
|
</tr>`,
|
||||||
response.LibrarySize,
|
response.LibrarySize,
|
||||||
response.MemAlloc,
|
response.MemAlloc,
|
||||||
@@ -254,10 +248,9 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
|
|||||||
response.PID,
|
response.PID,
|
||||||
bToMb(response.APITraffic),
|
bToMb(response.APITraffic),
|
||||||
bToMb(response.APITraffic-zr.trafficOnStartup.Load()),
|
bToMb(response.APITraffic-zr.trafficOnStartup.Load()),
|
||||||
response.RequestedMB,
|
|
||||||
response.ServedMB,
|
response.ServedMB,
|
||||||
efficiency,
|
efficiency,
|
||||||
response.RequestedMB-response.ServedMB,
|
bToMb(response.APITraffic-zr.trafficOnStartup.Load())-response.ServedMB,
|
||||||
)
|
)
|
||||||
|
|
||||||
out += fmt.Sprintf(`
|
out += fmt.Sprintf(`
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *realdebrid.T
|
|||||||
var err error
|
var err error
|
||||||
info, err = t.rd.GetTorrentInfo(rdTorrent.ID)
|
info, err = t.rd.GetTorrentInfo(rdTorrent.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.log.Warnf("Cannot get info for id=%s: %v", rdTorrent.ID, err)
|
t.log.Warnf("Cannot get info for torrent %s (id=%s): %v", rdTorrent.Name, rdTorrent.ID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.writeInfoToFile(info)
|
t.writeInfoToFile(info)
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ package universal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -22,7 +20,6 @@ import (
|
|||||||
type Downloader struct {
|
type Downloader struct {
|
||||||
rd *realdebrid.RealDebrid
|
rd *realdebrid.RealDebrid
|
||||||
workerPool *ants.Pool
|
workerPool *ants.Pool
|
||||||
RequestedBytes atomic.Uint64
|
|
||||||
TotalBytes atomic.Uint64
|
TotalBytes atomic.Uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +48,6 @@ func (dl *Downloader) StartResetBandwidthCountersJob() {
|
|||||||
ticker := time.NewTicker(24 * time.Hour)
|
ticker := time.NewTicker(24 * time.Hour)
|
||||||
for {
|
for {
|
||||||
dl.rd.TokenManager.ResetAllTokens()
|
dl.rd.TokenManager.ResetAllTokens()
|
||||||
dl.RequestedBytes.Store(0)
|
|
||||||
dl.TotalBytes.Store(0)
|
dl.TotalBytes.Store(0)
|
||||||
<-ticker.C
|
<-ticker.C
|
||||||
}
|
}
|
||||||
@@ -234,14 +230,9 @@ func (dl *Downloader) streamFileToResponse(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Update the download statistics
|
// Update the download statistics
|
||||||
reqBytes, _ := parseRangeHeader(req.Header.Get("Range"))
|
|
||||||
if reqBytes == 0 && unrestrict != nil {
|
|
||||||
reqBytes = uint64(unrestrict.Filesize)
|
|
||||||
}
|
|
||||||
dl.RequestedBytes.Add(reqBytes)
|
|
||||||
dl.TotalBytes.Add(uint64(n))
|
dl.TotalBytes.Add(uint64(n))
|
||||||
if cfg.ShouldLogRequests() {
|
if cfg.ShouldLogRequests() {
|
||||||
log.Debugf("Served %d MB of the requested %d MB of file %s (range=%s)", bToMb(uint64(n)), bToMb(reqBytes), unrestrict.Filename, req.Header.Get("Range"))
|
log.Debugf("Served %d MB of file %s (range=%s)", bToMb(uint64(n)), unrestrict.Filename, req.Header.Get("Range"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -250,50 +241,6 @@ func redirect(resp http.ResponseWriter, req *http.Request, url string) {
|
|||||||
http.Redirect(resp, req, url, http.StatusFound)
|
http.Redirect(resp, req, url, http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseRangeHeader(rangeHeader string) (uint64, error) {
|
|
||||||
if rangeHeader == "" { // Empty header means no range request
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(rangeHeader, "bytes=") {
|
|
||||||
return 0, fmt.Errorf("invalid range header format")
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := strings.SplitN(rangeHeader[6:], "-", 2) // [6:] removes "bytes="
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return 0, fmt.Errorf("invalid range specification")
|
|
||||||
}
|
|
||||||
|
|
||||||
var start, end uint64
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Case 1: "bytes=100-" (from byte 100 to the end)
|
|
||||||
if parts[0] != "" {
|
|
||||||
start, err = strconv.ParseUint(parts[0], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("invalid start value: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case 2: "bytes=-200" (last 200 bytes)
|
|
||||||
if parts[1] != "" {
|
|
||||||
end, err = strconv.ParseUint(parts[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("invalid end value: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle "bytes=500-100" (invalid range)
|
|
||||||
if start > end {
|
|
||||||
return 0, fmt.Errorf("invalid range: start cannot be greater than end")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate bytes to read
|
|
||||||
bytesToRead := end - start + 1 // +1 because ranges are inclusive
|
|
||||||
|
|
||||||
return bytesToRead, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type byteCounter struct {
|
type byteCounter struct {
|
||||||
totalBytes *int64
|
totalBytes *int64
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ func NewRealDebrid(apiClient, unrestrictClient, downloadClient *zurghttp.HTTPCli
|
|||||||
rd.UnrestrictMap.Set(token, cmap.New[*Download]())
|
rd.UnrestrictMap.Set(token, cmap.New[*Download]())
|
||||||
}
|
}
|
||||||
|
|
||||||
rd.loadCachedTorrents()
|
|
||||||
|
|
||||||
return rd
|
return rd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package realdebrid
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -106,7 +104,7 @@ func (rd *RealDebrid) GetTorrents(onlyOne bool) ([]Torrent, int, error) {
|
|||||||
page += maxParallelThreads
|
page += maxParallelThreads
|
||||||
}
|
}
|
||||||
|
|
||||||
rd.cacheTorrents(allTorrents)
|
rd.torrentsCache = allTorrents
|
||||||
return allTorrents, len(allTorrents), nil
|
return allTorrents, len(allTorrents), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,51 +185,3 @@ func (rd *RealDebrid) fetchPageOfTorrents(page, limit int) fetchTorrentsResult {
|
|||||||
err: nil,
|
err: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *RealDebrid) cacheTorrents(torrents []Torrent) {
|
|
||||||
filePath := "data/info/all.json"
|
|
||||||
file, err := os.Create(filePath)
|
|
||||||
if err != nil {
|
|
||||||
rd.log.Warnf("Cannot create info file %s: %v", filePath, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
jsonData, err := json.Marshal(torrents)
|
|
||||||
if err != nil {
|
|
||||||
rd.log.Warnf("Cannot marshal torrent info: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := file.Write(jsonData); err != nil {
|
|
||||||
rd.log.Warnf("Cannot write to info file %s: %v", filePath, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rd.torrentsCache = torrents
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *RealDebrid) loadCachedTorrents() {
|
|
||||||
filePath := "data/info/all.json"
|
|
||||||
file, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
|
||||||
rd.log.Warnf("Cannot open info file %s: %v", filePath, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
jsonData, err := io.ReadAll(file)
|
|
||||||
if err != nil {
|
|
||||||
rd.log.Warnf("Cannot read info file %s: %v", filePath, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var torrents []Torrent
|
|
||||||
err = json.Unmarshal(jsonData, &torrents)
|
|
||||||
if err != nil {
|
|
||||||
rd.log.Warnf("Cannot unmarshal torrent info: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rd.torrentsCache = torrents
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user