]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Make use of magnet source fields and expose Torrent.MergeSpec
authorMatt Joiner <anacrolix@gmail.com>
Tue, 2 Jun 2020 05:06:05 +0000 (15:06 +1000)
committerMatt Joiner <anacrolix@gmail.com>
Tue, 2 Jun 2020 05:06:05 +0000 (15:06 +1000)
client.go
spec.go

index 96a37c025176aca3ee9c442120f965c38f344d83..03bd4541a3f798eb6fa55f41cf96a13c89e70ba7 100644 (file)
--- a/client.go
+++ b/client.go
@@ -10,6 +10,7 @@ import (
        "fmt"
        "io"
        "net"
+       "net/http"
        "strconv"
        "strings"
        "time"
@@ -1138,26 +1139,31 @@ func (cl *Client) AddTorrentInfoHashWithStorage(infoHash metainfo.Hash, specStor
        return
 }
 
-// Add or merge a torrent spec. If the torrent is already present, the
-// trackers will be merged with the existing ones. If the Info isn't yet
-// known, it will be set. The display name is replaced if the new spec
-// provides one. Returns new if the torrent wasn't already in the client.
-// Note that any `Storage` defined on the spec will be ignored if the
-// torrent is already present (i.e. `new` return value is `true`)
+// Add or merge a torrent spec. Returns new if the torrent wasn't already in the client. See also
+// Torrent.MergeSpec.
 func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (t *Torrent, new bool, err error) {
        t, new = cl.AddTorrentInfoHashWithStorage(spec.InfoHash, spec.Storage)
+       err = t.MergeSpec(spec)
+       return
+}
+
+// The trackers will be merged with the existing ones. If the Info isn't yet known, it will be set.
+// The display name is replaced if the new spec provides one. Note that any `Storage` is ignored.
+func (t *Torrent) MergeSpec(spec *TorrentSpec) error {
        if spec.DisplayName != "" {
                t.SetDisplayName(spec.DisplayName)
        }
        if spec.InfoBytes != nil {
-               err = t.SetInfoBytes(spec.InfoBytes)
+               err := t.SetInfoBytes(spec.InfoBytes)
                if err != nil {
-                       return
+                       return err
                }
        }
+       cl := t.cl
        cl.AddDHTNodes(spec.DhtNodes)
        cl.lock()
        defer cl.unlock()
+       useTorrentSources(spec.Sources, t)
        for _, url := range spec.Webseeds {
                t.addWebSeed(url)
        }
@@ -1166,7 +1172,53 @@ func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (t *Torrent, new bool, err e
        }
        t.addTrackers(spec.Trackers)
        t.maybeNewConns()
-       return
+       return nil
+}
+
+func useTorrentSources(sources []string, t *Torrent) {
+       for _, s := range sources {
+               go func(s string) {
+                       err := useTorrentSource(s, t)
+                       if err != nil {
+                               t.logger.WithDefaultLevel(log.Warning).Printf("using torrent source %q: %v", s, err)
+                       } else {
+                               t.logger.Printf("successfully used source %q", s)
+                       }
+               }(s)
+       }
+}
+
+func useTorrentSource(source string, t *Torrent) error {
+       req, err := http.NewRequest(http.MethodGet, source, nil)
+       if err != nil {
+               panic(err)
+       }
+       ctx, cancel := context.WithCancel(context.Background())
+       defer cancel()
+       go func() {
+               select {
+               case <-t.GotInfo():
+               case <-t.Closed():
+               case <-ctx.Done():
+               }
+               cancel()
+       }()
+       req = req.WithContext(ctx)
+       resp, err := http.DefaultClient.Do(req)
+       if err != nil {
+               if ctx.Err() != nil {
+                       return nil
+               }
+               return err
+       }
+       mi, err := metainfo.Load(resp.Body)
+       if err != nil {
+               if ctx.Err() != nil {
+                       return nil
+               }
+               return err
+       }
+       return t.MergeSpec(TorrentSpecFromMetaInfo(mi))
 }
 
 func (cl *Client) dropTorrent(infoHash metainfo.Hash) (err error) {
diff --git a/spec.go b/spec.go
index e0c13bd8ce5db01edf670e204ed49108442dac88..e0c0bc24232bfdc6837379c7d3aa7cb3565b4bc2 100644 (file)
--- a/spec.go
+++ b/spec.go
@@ -16,6 +16,8 @@ type TorrentSpec struct {
        DisplayName string
        Webseeds    []string
        DhtNodes    []string
+       // The combination of the "xs" and "as" fields in magnet links, for now.
+       Sources []string
 
        // The chunk size to use for outbound requests. Defaults to 16KiB if not set.
        ChunkSize int
@@ -32,6 +34,7 @@ func TorrentSpecFromMagnetURI(uri string) (spec *TorrentSpec, err error) {
                DisplayName: m.DisplayName,
                InfoHash:    m.InfoHash,
                Webseeds:    m.Params["ws"],
+               Sources:     append(m.Params["xs"], m.Params["as"]...),
                // TODO: What's the parameter for DHT nodes or bootstrap peers in a magnet link?
        }
        return