dataWaiter chan struct{}
}
+func (cl *Client) WriteStatus(w io.Writer) {
+ cl.mu.Lock()
+ defer cl.mu.Unlock()
+ for _, t := range cl.torrents {
+ fmt.Fprintf(w, "%s: %f%%\n", t.MetaInfo.Name, 100*(1-float32(t.BytesLeft())/float32(t.Length())))
+ t.WriteStatus(w)
+ }
+}
+
// Read torrent data at the given offset. Returns ErrDataNotReady if the data
// isn't available.
func (cl *Client) TorrentReadAt(ih InfoHash, off int64, p []byte) (n int, err error) {
mountDir string
disableTrackers = flag.Bool("disableTrackers", false, "disables trackers")
testPeer = flag.String("testPeer", "", "the address for a test peer")
- pprofAddr = flag.String("pprofAddr", "", "pprof HTTP server bind address")
+ httpAddr = flag.String("httpAddr", "", "HTTP server bind address")
readaheadBytes = flag.Int("readaheadBytes", 10*1024*1024, "bytes to readahead in each torrent from the last read piece")
testPeerAddr *net.TCPAddr
)
os.Exit(2)
}
log.SetFlags(log.LstdFlags | log.Lshortfile)
- if *pprofAddr != "" {
- go http.ListenAndServe(*pprofAddr, nil)
+ if *httpAddr != "" {
+ go http.ListenAndServe(*httpAddr, nil)
}
conn, err := fuse.Mount(mountDir)
if err != nil {
DisableTrackers: *disableTrackers,
DownloadStrategy: &torrent.ResponsiveDownloadStrategy{*readaheadBytes},
}
+ http.DefaultServeMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ client.WriteStatus(w)
+ })
client.Start()
addTorrentDir(client, torrentPath)
resolveTestPeerAddr()
import (
"container/list"
"encoding"
+ "fmt"
+ "io"
"log"
"net"
"sync"
PeerMaxRequests int // Maximum pending requests the peer allows.
}
+func (cn *connection) completedString() string {
+ if cn.PeerPieces == nil {
+ return "?"
+ }
+ f := float32(cn.piecesPeerHasCount()) / float32(cn.totalPiecesCount())
+ return fmt.Sprintf("%d%%", int(f*100))
+}
+
+func (cn *connection) totalPiecesCount() int {
+ return len(cn.PeerPieces)
+}
+
+func (cn *connection) piecesPeerHasCount() (count int) {
+ for _, has := range cn.PeerPieces {
+ if has {
+ count++
+ }
+ }
+ return
+}
+
+func (cn *connection) WriteStatus(w io.Writer) {
+ fmt.Fprintf(w, "%q: %s-%s: %s completed: ", cn.PeerId, cn.Socket.LocalAddr(), cn.Socket.RemoteAddr(), cn.completedString())
+ c := func(b byte) {
+ fmt.Fprintf(w, "%c", b)
+ }
+ // https://trac.transmissionbt.com/wiki/PeerStatusText
+ if len(cn.Requests) != 0 {
+ c('D')
+ } else if cn.Interested {
+ c('d')
+ }
+ if !cn.PeerChoked && !cn.Interested {
+ c('K')
+ }
+ if !cn.Choked && !cn.PeerInterested {
+ c('?')
+ }
+ fmt.Fprintln(w)
+}
+
func (c *connection) Close() {
c.mu.Lock()
if c.closed {
import (
"container/list"
"fmt"
+ "io"
"net"
"sort"
lastReadPiece int
}
+func (t *torrent) pieceStatusChar(index int) byte {
+ p := t.Pieces[index]
+ switch {
+ case p.Complete():
+ return 'C'
+ case p.QueuedForHash:
+ return 'Q'
+ case p.Hashing:
+ return 'H'
+ case t.PiecePartiallyDownloaded(index):
+ return 'P'
+ default:
+ return '.'
+ }
+}
+
+func (t *torrent) WriteStatus(w io.Writer) {
+ fmt.Fprint(w, "Pieces: ")
+ for index := range t.Pieces {
+ fmt.Fprintf(w, "%c", t.pieceStatusChar(index))
+ }
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, "Priorities: ")
+ for e := t.Priorities.Front(); e != nil; e = e.Next() {
+ fmt.Fprintf(w, "\t%v\n", e.Value)
+ }
+ for _, c := range t.Conns {
+ c.WriteStatus(w)
+ }
+}
+
func (t *torrent) String() string {
return t.MetaInfo.Name
}
return
}
+func (t *torrent) PiecePartiallyDownloaded(index int) bool {
+ return t.PieceNumPendingBytes(pp.Integer(index)) != t.PieceLength(pp.Integer(index))
+}
+
func NumChunksForPiece(chunkSize int, pieceSize int) int {
return (pieceSize + chunkSize - 1) / chunkSize
}