FedP2P/cmd/torrent-pick/main.go

191 lines
4.1 KiB
Go
Raw Normal View History

2015-05-11 22:50:59 +08:00
// Downloads torrents from the command-line.
package main
import (
"bufio"
2015-05-11 22:50:59 +08:00
"fmt"
"io"
"io/ioutil"
2015-05-11 22:50:59 +08:00
"log"
"net"
"net/http"
_ "net/http/pprof"
"os"
"strings"
"time"
_ "github.com/anacrolix/envpprof"
"github.com/dustin/go-humanize"
"github.com/jessevdk/go-flags"
2019-08-21 18:58:40 +08:00
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
2015-05-11 22:50:59 +08:00
)
// fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0])
func resolvedPeerAddrs(ss []string) (ret []torrent.PeerInfo, err error) {
2015-05-11 22:50:59 +08:00
for _, s := range ss {
var addr *net.TCPAddr
addr, err = net.ResolveTCPAddr("tcp", s)
if err != nil {
return
}
ret = append(ret, torrent.PeerInfo{
Addr: addr,
2015-05-11 22:50:59 +08:00
})
}
return
}
func bytesCompleted(tc *torrent.Client) (ret int64) {
for _, t := range tc.Torrents() {
if t.Info() != nil {
2015-05-11 22:50:59 +08:00
ret += t.BytesCompleted()
}
}
return
}
// Returns an estimate of the total bytes for all torrents.
func totalBytesEstimate(tc *torrent.Client) (ret int64) {
var noInfo, hadInfo int64
for _, t := range tc.Torrents() {
info := t.Info()
if info == nil {
noInfo++
continue
}
ret += info.TotalLength()
hadInfo++
}
if hadInfo != 0 {
// Treat each torrent without info as the average of those with,
// rounded up.
ret += (noInfo*ret + hadInfo - 1) / hadInfo
}
return
}
func progressLine(tc *torrent.Client) string {
return fmt.Sprintf("\033[K%s / %s\r", humanize.Bytes(uint64(bytesCompleted(tc))), humanize.Bytes(uint64(totalBytesEstimate(tc))))
}
2015-06-02 03:56:10 +08:00
func dstFileName(picked string) string {
parts := strings.Split(picked, "/")
return parts[len(parts)-1]
}
2015-05-11 22:50:59 +08:00
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
2018-06-16 14:33:40 +08:00
var rootGroup = struct {
Client *torrent.ClientConfig `group:"Client Options"`
TestPeers []string `long:"test-peer" description:"address of peer to inject to every torrent"`
Pick string `long:"pick" description:"filename to pick"`
}{
Client: torrent.NewDefaultClientConfig(),
2015-05-11 22:50:59 +08:00
}
// Don't pass flags.PrintError because it's inconsistent with printing.
// https://github.com/jessevdk/go-flags/issues/132
parser := flags.NewParser(&rootGroup, flags.HelpFlag|flags.PassDoubleDash)
parser.Usage = "[OPTIONS] (magnet URI or .torrent file path)..."
posArgs, err := parser.Parse()
if err != nil {
2016-02-01 21:44:29 +08:00
fmt.Fprintf(os.Stderr, "%s", "Download from the BitTorrent network.\n\n")
2015-05-11 22:50:59 +08:00
fmt.Println(err)
os.Exit(2)
}
log.Printf("File to pick: %s", rootGroup.Pick)
testPeers, err := resolvedPeerAddrs(rootGroup.TestPeers)
if err != nil {
log.Fatal(err)
}
if len(posArgs) == 0 {
fmt.Fprintln(os.Stderr, "no torrents specified")
return
}
2015-06-02 03:56:10 +08:00
tmpdir, err := ioutil.TempDir("", "torrent-pick-")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpdir)
rootGroup.Client.DataDir = tmpdir
2018-06-16 14:33:40 +08:00
client, err := torrent.NewClient(rootGroup.Client)
2015-05-11 22:50:59 +08:00
if err != nil {
log.Fatalf("error creating client: %s", err)
}
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
client.WriteStatus(w)
})
defer client.Close()
2015-06-02 03:56:10 +08:00
dstName := dstFileName(rootGroup.Pick)
f, err := os.Create(dstName)
if err != nil {
log.Fatal(err)
}
dstWriter := bufio.NewWriter(f)
done := make(chan struct{})
2015-05-11 22:50:59 +08:00
for _, arg := range posArgs {
2016-04-03 16:40:43 +08:00
t := func() *torrent.Torrent {
2015-05-11 22:50:59 +08:00
if strings.HasPrefix(arg, "magnet:") {
t, err := client.AddMagnet(arg)
if err != nil {
log.Fatalf("error adding magnet: %s", err)
}
return t
} else {
metaInfo, err := metainfo.LoadFromFile(arg)
if err != nil {
log.Fatal(err)
}
t, err := client.AddTorrent(metaInfo)
if err != nil {
log.Fatal(err)
}
return t
}
}()
t.AddPeers(testPeers)
2015-06-02 03:56:10 +08:00
2015-05-11 22:50:59 +08:00
go func() {
2015-10-23 09:42:57 +08:00
defer close(done)
2015-05-11 22:50:59 +08:00
<-t.GotInfo()
2015-10-23 09:42:57 +08:00
for _, file := range t.Files() {
if file.DisplayPath() != rootGroup.Pick {
continue
2015-06-02 03:56:10 +08:00
}
2018-01-21 19:49:12 +08:00
file.Download()
2018-01-06 13:37:13 +08:00
srcReader := file.NewReader()
2018-01-06 13:41:05 +08:00
defer srcReader.Close()
2015-10-23 09:42:57 +08:00
io.Copy(dstWriter, srcReader)
return
2015-06-02 03:56:10 +08:00
}
2015-10-23 09:42:57 +08:00
log.Print("file not found")
2015-05-11 22:50:59 +08:00
}()
}
2015-06-02 03:56:10 +08:00
2015-05-11 22:50:59 +08:00
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
2015-05-11 22:50:59 +08:00
waitDone:
for {
select {
case <-done:
break waitDone
case <-ticker.C:
os.Stdout.WriteString(progressLine(client))
}
}
if rootGroup.Client.Seed {
2015-05-11 22:50:59 +08:00
select {}
}
}