github.com/anacrolix/torrent@v1.61.0/spec.go (about) 1 package torrent 2 3 import ( 4 "fmt" 5 6 g "github.com/anacrolix/generics" 7 8 "github.com/anacrolix/torrent/metainfo" 9 infohash_v2 "github.com/anacrolix/torrent/types/infohash-v2" 10 ) 11 12 // Specifies a new torrent for adding to a client, or additions to an existing Torrent. There are 13 // constructor functions for magnet URIs and torrent metainfo files. TODO: This type should be 14 // dismantled into a new Torrent option type, and separate Torrent mutate method(s). 15 type TorrentSpec struct { 16 AddTorrentOpts 17 // The tiered tracker URIs. 18 Trackers [][]string 19 // The name to use if the Name field from the Info isn't available. 20 DisplayName string 21 // WebSeed URLs. For additional options add the URLs separately with Torrent.AddWebSeeds 22 // instead. 23 Webseeds []string 24 DhtNodes []string 25 PeerAddrs []string 26 // The combination of the "xs" and "as" fields in magnet links, for now. 27 Sources []string 28 // BEP 52 "piece layers" from metainfo 29 PieceLayers map[string]string 30 } 31 32 func TorrentSpecFromMagnetUri(uri string) (spec *TorrentSpec, err error) { 33 m, err := metainfo.ParseMagnetV2Uri(uri) 34 if err != nil { 35 return 36 } 37 spec = &TorrentSpec{ 38 Trackers: [][]string{m.Trackers}, 39 DisplayName: m.DisplayName, 40 Webseeds: m.Params["ws"], 41 Sources: append(m.Params["xs"], m.Params["as"]...), 42 PeerAddrs: m.Params["x.pe"], // BEP 9 43 // TODO: What's the parameter for DHT nodes? 44 } 45 spec.InfoHash = m.InfoHash.UnwrapOrZeroValue() 46 spec.InfoHashV2 = m.V2InfoHash 47 return 48 } 49 50 // The error will be from unmarshalling the info bytes. The TorrentSpec is still filled out as much 51 // as possible in this case. 52 func TorrentSpecFromMetaInfoErr(mi *metainfo.MetaInfo) (*TorrentSpec, error) { 53 info, err := mi.UnmarshalInfo() 54 if err != nil { 55 err = fmt.Errorf("unmarshalling info: %w", err) 56 } 57 var v1Ih metainfo.Hash 58 if info.HasV1() { 59 v1Ih = mi.HashInfoBytes() 60 } 61 var v2Infohash g.Option[infohash_v2.T] 62 if info.HasV2() { 63 v2Infohash.Set(infohash_v2.HashBytes(mi.InfoBytes)) 64 } 65 66 return &TorrentSpec{ 67 Trackers: mi.UpvertedAnnounceList(), 68 PieceLayers: mi.PieceLayers, 69 DisplayName: info.BestName(), 70 Webseeds: mi.UrlList, 71 DhtNodes: func() (ret []string) { 72 ret = make([]string, 0, len(mi.Nodes)) 73 for _, node := range mi.Nodes { 74 ret = append(ret, string(node)) 75 } 76 return 77 }(), 78 AddTorrentOpts: AddTorrentOpts{ 79 InfoHash: v1Ih, 80 InfoHashV2: v2Infohash, 81 InfoBytes: mi.InfoBytes, 82 }, 83 }, err 84 } 85 86 // Panics if there was anything missing from the metainfo. 87 func TorrentSpecFromMetaInfo(mi *metainfo.MetaInfo) *TorrentSpec { 88 ts, err := TorrentSpecFromMetaInfoErr(mi) 89 if err != nil { 90 panic(err) 91 } 92 return ts 93 }