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