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