privtracker/main.go
2020-12-08 14:36:51 +01:00

110 lines
2.6 KiB
Go

package main
import (
"github.com/gin-gonic/gin"
"github.com/jackpal/bencode-go"
)
func main() {
r := gin.Default()
r.GET("/:room/announce", announce)
r.GET("/:room/scrape", scrape)
go Cleanup()
r.Run()
}
type AnnounceRequest struct {
InfoHash string `form:"info_hash"`
PeerID string `form:"peer_id"`
IP string `form:"ip"`
Port uint16 `form:"port"`
Uploaded uint `form:"uploaded"`
Downloaded uint `form:"downloaded"`
Left uint `form:"left"`
Numwant uint `form:"numwant"`
Key string `form:"key"`
Compact bool `form:"compact"`
SupportCrypto bool `form:"supportcrypto"`
Event string `form:"event"`
}
func (req *AnnounceRequest) IsSeeding() bool {
return req.Left == 0
}
type AnnounceResponse struct {
Interval int `bencode:"interval"`
Complete int `bencode:"complete"`
Incomplete int `bencode:"incomplete"`
Peers []byte `bencode:"peers"`
PeersIPv6 []byte `bencode:"peers_ipv6"`
}
func announce(c *gin.Context) {
req := new(AnnounceRequest)
c.BindQuery(req)
// if req.IP == "" {
req.IP = c.ClientIP() // not sure if ip from request should be honored
// }
if req.Numwant == 0 {
req.Numwant = 30
}
switch req.Event {
case "stopped":
DeletePeer(c.Param("room"), req.InfoHash, req.IP, req.Port)
case "completed":
GraduateLeecher(c.Param("room"), req.InfoHash, req.IP, req.Port)
default:
PutPeer(c.Param("room"), req.InfoHash, req.IP, req.Port, req.IsSeeding())
}
peersIPv4, peersIPv6, numSeeders, numLeechers := GetPeers(c.Param("room"), req.InfoHash, req.IP, req.Port, req.IsSeeding(), req.Numwant)
interval := 120
if numSeeders == 0 {
interval /= 2
} else if numLeechers == 0 {
interval *= 2
}
resp := AnnounceResponse{
Interval: interval,
Complete: numSeeders,
Incomplete: numLeechers,
Peers: peersIPv4,
PeersIPv6: peersIPv6,
}
if err := bencode.Marshal(c.Writer, resp); err != nil {
c.Error(err)
return
}
}
type ScrapeRequest struct {
InfoHash string `form:"info_hash"`
}
type ScrapeResponse struct {
Files map[string]Stat `bencode:"files"`
}
type Stat struct {
Complete int `bencode:"complete"`
Incomplete int `bencode:"incomplete"`
// Downloaded uint `bencode:"downloaded"`
}
func scrape(c *gin.Context) {
req := new(ScrapeRequest)
c.BindQuery(req)
numSeeders, numLeechers := GetStats(c.Param("room"), req.InfoHash)
resp := ScrapeResponse{
Files: map[string]Stat{
req.InfoHash: {
Complete: numSeeders,
Incomplete: numLeechers,
},
},
}
if err := bencode.Marshal(c.Writer, resp); err != nil {
c.Error(err)
return
}
}