client.go | 74 ++++++++++++++++++++++++++++++----------------------- client_test.go | 18 ++++++++++++++++++ diff --git a/client.go b/client.go index 30e6aa30c6317527d38339a683d9218fd0b7c8f7..8b21c44ce66d5a917888794bb6f5197bffda315f 100644 --- a/client.go +++ b/client.go @@ -2258,59 +2258,69 @@ CopyExact(&spec.InfoHash, &mi.Info.Hash) 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. func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (T Torrent, new bool, err error) { T.cl = cl cl.mu.Lock() defer cl.mu.Unlock() t, ok := cl.torrents[spec.InfoHash] - if ok { - T.torrent = t - return - } + if !ok { + new = true - new = true + if _, ok := cl.bannedTorrents[spec.InfoHash]; ok { + err = errors.New("banned torrent") + return + } - if _, ok := cl.bannedTorrents[spec.InfoHash]; ok { - err = errors.New("banned torrent") - return - } - - t, err = newTorrent(spec.InfoHash) - if err != nil { - return + t, err = newTorrent(spec.InfoHash) + if err != nil { + return + } } if spec.DisplayName != "" { t.DisplayName = spec.DisplayName } - if spec.Info != nil { - err = cl.setMetaData(t, &spec.Info.Info, spec.Info.Bytes) - } else { - var mi *metainfo.MetaInfo - mi, err = cl.torrentCacheMetaInfo(spec.InfoHash) - if err != nil { - log.Printf("error getting cached metainfo: %s", err) - } else if mi != nil { - t.addTrackers(mi.AnnounceList) - err = cl.setMetaData(t, &mi.Info.Info, mi.Info.Bytes) + // Try to merge in info we have on the torrent. Any err left will + // terminate the function. + if t.Info == nil { + if spec.Info != nil { + err = cl.setMetaData(t, &spec.Info.Info, spec.Info.Bytes) + } else { + var mi *metainfo.MetaInfo + mi, err = cl.torrentCacheMetaInfo(spec.InfoHash) + if err != nil { + log.Printf("error getting cached metainfo: %s", err) + err = nil + } else if mi != nil { + t.addTrackers(mi.AnnounceList) + err = cl.setMetaData(t, &mi.Info.Info, mi.Info.Bytes) + } } } if err != nil { return } + t.addTrackers(spec.Trackers) cl.torrents[spec.InfoHash] = t T.torrent = t - T.torrent.pruneTimer = time.AfterFunc(0, func() { - cl.pruneConnectionsUnlocked(T.torrent) - }) - t.addTrackers(spec.Trackers) - if !cl.disableTrackers { - go cl.announceTorrentTrackers(T.torrent) - } - if cl.dHT != nil { - go cl.announceTorrentDHT(T.torrent, true) + // From this point onwards, we can consider the torrent a part of the + // client. + if new { + t.pruneTimer = time.AfterFunc(0, func() { + cl.pruneConnectionsUnlocked(T.torrent) + }) + if !cl.disableTrackers { + go cl.announceTorrentTrackers(T.torrent) + } + if cl.dHT != nil { + go cl.announceTorrentDHT(T.torrent, true) + } } return } diff --git a/client_test.go b/client_test.go index 447d941487449edb28393cf338b3dcdf842e71f7..08dbc80cd8b655647a35be4fbf58ec1e1b481ebc 100644 --- a/client_test.go +++ b/client_test.go @@ -14,6 +14,7 @@ "github.com/anacrolix/libtorgo/bencode" "github.com/anacrolix/utp" "github.com/bradfitz/iter" + "gopkg.in/check.v1" "github.com/anacrolix/torrent/data/blob" "github.com/anacrolix/torrent/internal/testutil" @@ -299,3 +300,20 @@ t.Fatalf("case failed: %v", case_) } } } + +func (suite) TestMergingTrackersByAddingSpecs(c *check.C) { + cl, _ := NewClient(&TestingConfig) + defer cl.Close() + spec := TorrentSpec{} + T, new, _ := cl.AddTorrentSpec(&spec) + if !new { + c.FailNow() + } + spec.Trackers = [][]string{{"http://a"}, {"udp://b"}} + _, new, _ = cl.AddTorrentSpec(&spec) + if new { + c.FailNow() + } + c.Assert(T.Trackers[0][0].URL(), check.Equals, "http://a") + c.Assert(T.Trackers[1][0].URL(), check.Equals, "udp://b") +}