From: Matt Joiner Date: Tue, 2 Jun 2020 05:06:05 +0000 (+1000) Subject: Make use of magnet source fields and expose Torrent.MergeSpec X-Git-Tag: v1.16.0~16^2~4 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=997384a3941546476e719742bbba8714f46e51f6;p=btrtrc.git Make use of magnet source fields and expose Torrent.MergeSpec --- diff --git a/client.go b/client.go index 96a37c02..03bd4541 100644 --- 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 e0c13bd8..e0c0bc24 100644 --- 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