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  }