]> Sergey Matveev's repositories - btrtrc.git/blob - cmd/btrtrc/list.go
Initial draft client
[btrtrc.git] / cmd / btrtrc / list.go
1 package main
2
3 import (
4         "encoding/hex"
5         "fmt"
6         "log"
7         "os"
8         "path"
9         "sort"
10         "strings"
11         "sync"
12         "syscall"
13         "time"
14
15         "github.com/anacrolix/dht/v2"
16         "github.com/anacrolix/torrent"
17         "github.com/anacrolix/torrent/metainfo"
18         "github.com/dustin/go-humanize"
19 )
20
21 const (
22         MaxListNameWidth = 40
23         FIFOsDir         = "fifos"
24         PeersDir         = "peers"
25         FilesDir         = "files"
26 )
27
28 var (
29         TorrentStats  = map[metainfo.Hash]torrent.TorrentStats{}
30         TorrentStatsM sync.RWMutex
31 )
32
33 func recreateFIFO(pth string) {
34         os.Remove(pth)
35         if err := syscall.Mkfifo(pth, 0666); err != nil {
36                 log.Fatalln(err)
37         }
38 }
39
40 func shortenName(name string) string {
41         s := []rune(name)
42         if len(s) > MaxListNameWidth {
43                 s = s[:MaxListNameWidth]
44         }
45         return string(s)
46 }
47
48 func fifoList(c *torrent.Client) {
49         pth := path.Join(FIFOsDir, "list")
50         recreateFIFO(pth)
51         for {
52                 fd, err := os.OpenFile(pth, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
53                 if err != nil {
54                         log.Println("OpenFile:", pth, err)
55                         time.Sleep(time.Second)
56                         continue
57                 }
58                 ts := c.Torrents()
59                 sort.Sort(ByInfoHash(ts))
60                 for _, t := range ts {
61                         if t.Info() == nil {
62                                 fmt.Fprintf(fd, "%s not ready\n", t.Name())
63                                 continue
64                         }
65                         stats := t.Stats()
66                         done := t.BytesCompleted() * 100 / t.Length()
67                         percColour := Red
68                         if done == 100 {
69                                 percColour = Green
70                         }
71                         tx := stats.BytesWrittenData.Int64()
72                         tx += TxStats[t.InfoHash()]
73                         ratio := float64(tx) / float64(t.Length())
74                         TorrentStatsM.RLock()
75                         prev := TorrentStats[t.InfoHash()]
76                         TorrentStatsM.RUnlock()
77                         rxSpeed := stats.BytesReadData.Int64() - prev.BytesReadData.Int64()
78                         txSpeed := stats.BytesWrittenData.Int64() - prev.BytesWrittenData.Int64()
79                         var eta string
80                         if done < 100 && rxSpeed > 0 {
81                                 etaRaw := time.Duration((t.Length() - t.BytesCompleted()) / rxSpeed)
82                                 etaRaw *= time.Second
83                                 eta = etaRaw.String()
84                         }
85                         fmt.Fprintf(fd,
86                                 "%s%s%s %s%40s%s %8s %s%3d%%%s %4.1f %s%d%s/%s%d%s %d/%d/%d/%d %s\n",
87                                 Blue, t.InfoHash().HexString(), Reset,
88                                 Green, shortenName(t.Name()), Reset,
89                                 humanize.IBytes(uint64(t.Length())),
90                                 percColour, done, Reset,
91                                 ratio,
92                                 Green, rxSpeed/1024, Reset,
93                                 Magenta, txSpeed/1024, Reset,
94                                 stats.TotalPeers,
95                                 stats.PendingPeers,
96                                 stats.ActivePeers,
97                                 stats.ConnectedSeeders,
98                                 eta,
99                         )
100                 }
101                 fd.Close()
102                 time.Sleep(time.Second)
103         }
104 }
105
106 func fifoPeerList(t *torrent.Torrent) {
107         pth := path.Join(FIFOsDir, PeersDir, t.InfoHash().HexString())
108         recreateFIFO(pth)
109         for {
110                 fd, err := os.OpenFile(pth, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
111                 if err != nil {
112                         if os.IsNotExist(err) {
113                                 break
114                         }
115                         log.Println("OpenFile:", pth, err)
116                         time.Sleep(time.Second)
117                         continue
118                 }
119                 pcs := t.PeerConns()
120                 sort.Sort(ByPeerID(pcs))
121                 for _, pc := range pcs {
122                         completed := pc.CompletedString()
123                         completedColour := Red
124                         cols := strings.Split(completed, "/")
125                         if cols[0] == cols[1] {
126                                 completedColour = Green
127                         }
128                         stats := pc.Peer.Stats()
129                         fmt.Fprintf(fd,
130                                 "%s%s%s %10s %s%11s%s %s%d%s/%s%d%s %s / %s | %s%s%s %q\n",
131                                 Blue, hex.EncodeToString(pc.PeerID[:]), Reset,
132                                 pc.StatusFlags(),
133                                 completedColour, pc.CompletedString(), Reset,
134                                 Green, int(pc.DownloadRate()/1024), Reset,
135                                 Magenta, int(pc.UploadRate()/1024), Reset,
136                                 humanize.IBytes(uint64(stats.BytesReadData.Int64())),
137                                 humanize.IBytes(uint64(stats.BytesWrittenData.Int64())),
138                                 Green, pc.RemoteAddr, Reset,
139                                 pc.PeerClientName,
140                         )
141                 }
142                 fd.Close()
143                 time.Sleep(time.Second)
144         }
145 }
146
147 func fifoFileList(t *torrent.Torrent) {
148         pth := path.Join(FIFOsDir, FilesDir, t.InfoHash().HexString())
149         recreateFIFO(pth)
150         for {
151                 fd, err := os.OpenFile(pth, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
152                 if err != nil {
153                         if os.IsNotExist(err) {
154                                 break
155                         }
156                         log.Println("OpenFile:", pth, err)
157                         time.Sleep(time.Second)
158                         continue
159                 }
160                 for n, f := range t.Files() {
161                         var done int64
162                         if f.Length() > 0 {
163                                 done = (f.BytesCompleted() * 100) / f.Length()
164                         }
165                         percColour := Green
166                         if done < 100 {
167                                 percColour = Red
168                         }
169                         fmt.Fprintf(fd,
170                                 "%5d %8s %3d%% | %s%s%s\n",
171                                 n, humanize.IBytes(uint64(f.Length())), done,
172                                 percColour, f.Path(), Reset,
173                         )
174                 }
175                 fd.Close()
176                 time.Sleep(time.Second)
177         }
178 }
179
180 func fifoDHTList(c *torrent.Client) {
181         pth := path.Join(FIFOsDir, "dht")
182         recreateFIFO(pth)
183         for {
184                 fd, err := os.OpenFile(pth, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
185                 if err != nil {
186                         if os.IsNotExist(err) {
187                                 break
188                         }
189                         log.Println("OpenFile:", pth, err)
190                         time.Sleep(time.Second)
191                         continue
192                 }
193                 for _, s := range c.DhtServers() {
194                         stats := s.Stats().(dht.ServerStats)
195                         fmt.Fprintf(
196                                 fd, "%s%s%s all:%d good:%d await:%d succ:%d bad:%d\n",
197                                 Green, s.Addr().String(), Reset,
198                                 stats.Nodes,
199                                 stats.GoodNodes,
200                                 stats.OutstandingTransactions,
201                                 stats.SuccessfulOutboundAnnouncePeerQueries,
202                                 stats.BadNodes,
203                         )
204                 }
205                 fd.Close()
206                 time.Sleep(time.Second)
207         }
208 }