github.com/anacrolix/torrent@v1.61.0/metainfo/metainfo.go (about)

     1  package metainfo
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io"
     7  	"net/url"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/anacrolix/torrent/bencode"
    12  	infohash_v2 "github.com/anacrolix/torrent/types/infohash-v2"
    13  )
    14  
    15  // Also known as a torrent file.
    16  type MetaInfo struct {
    17  	InfoBytes    bencode.Bytes `bencode:"info,omitempty"`                                                        // BEP 3
    18  	Announce     string        `bencode:"announce,omitempty" mapstructure:",omitempty"`                          // BEP 3
    19  	AnnounceList AnnounceList  `bencode:"announce-list,omitempty"`                                               // BEP 12
    20  	Nodes        []Node        `bencode:"nodes,omitempty,ignore_unmarshal_type_error" mapstructure:",omitempty"` // BEP 5
    21  	// Where's this specified? Mentioned at
    22  	// https://wiki.theory.org/index.php/BitTorrentSpecification: (optional) the creation time of
    23  	// the torrent, in standard UNIX epoch format (integer, seconds since 1-Jan-1970 00:00:00 UTC)
    24  	CreationDate int64   `bencode:"creation date,omitempty,ignore_unmarshal_type_error"`
    25  	Comment      string  `bencode:"comment,omitempty"`
    26  	CreatedBy    string  `bencode:"created by,omitempty"`
    27  	Encoding     string  `bencode:"encoding,omitempty" mapstructure:",omitempty"` // BEP 54
    28  	UrlList      UrlList `bencode:"url-list,omitempty"`                           // BEP 19 WebSeeds
    29  	// BEP 52 (BitTorrent v2): Keys are file merkle roots ("pieces root"s), and the values are the
    30  	// concatenated hashes of the merkle tree layer that corresponds to the piece length.
    31  	PieceLayers map[string]string `bencode:"piece layers,omitempty" mapstructure:",omitempty"`
    32  }
    33  
    34  // Load a MetaInfo from an io.Reader. Returns a non-nil error in case of failure.
    35  func Load(r io.Reader) (*MetaInfo, error) {
    36  	var mi MetaInfo
    37  	d := bencode.NewDecoder(r)
    38  	err := d.Decode(&mi)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	err = d.ReadEOF()
    43  	if err != nil {
    44  		err = fmt.Errorf("error after decoding metainfo: %w", err)
    45  	}
    46  	return &mi, err
    47  }
    48  
    49  // Convenience function for loading a MetaInfo from a file.
    50  func LoadFromFile(filename string) (*MetaInfo, error) {
    51  	f, err := os.Open(filename)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	defer f.Close()
    56  	var buf bufio.Reader
    57  	buf.Reset(f)
    58  	return Load(&buf)
    59  }
    60  
    61  func (mi *MetaInfo) UnmarshalInfo() (info Info, err error) {
    62  	err = bencode.Unmarshal(mi.InfoBytes, &info)
    63  	return
    64  }
    65  
    66  func (mi *MetaInfo) HashInfoBytes() (infoHash Hash) {
    67  	return HashBytes(mi.InfoBytes)
    68  }
    69  
    70  // Encode to bencoded form.
    71  func (mi *MetaInfo) Write(w io.Writer) error {
    72  	return bencode.NewEncoder(w).Encode(mi)
    73  }
    74  
    75  // Set good default values in preparation for creating a new MetaInfo file.
    76  func (mi *MetaInfo) SetDefaults() {
    77  	mi.CreatedBy = "github.com/anacrolix/torrent"
    78  	mi.CreationDate = time.Now().Unix()
    79  }
    80  
    81  // Deprecated: Use MagnetV2. Creates a Magnet from a MetaInfo. Optional infohash and parsed info can
    82  // be provided.
    83  func (mi MetaInfo) Magnet(infoHash *Hash, info *Info) (m Magnet) {
    84  	m.Trackers = append(m.Trackers, mi.UpvertedAnnounceList().DistinctValues()...)
    85  	if info != nil {
    86  		m.DisplayName = info.BestName()
    87  	}
    88  	if infoHash != nil {
    89  		m.InfoHash = *infoHash
    90  	} else {
    91  		m.InfoHash = mi.HashInfoBytes()
    92  	}
    93  	m.Params = make(url.Values)
    94  	m.Params["ws"] = mi.UrlList
    95  	return
    96  }
    97  
    98  // Creates a MagnetV2 from a MetaInfo. This supports v1, hybrid, and v2 magnet links.
    99  func (mi *MetaInfo) MagnetV2() (m MagnetV2, err error) {
   100  	m.Trackers = append(m.Trackers, mi.UpvertedAnnounceList().DistinctValues()...)
   101  	info, err := mi.UnmarshalInfo()
   102  	if err != nil {
   103  		return
   104  	}
   105  	m.DisplayName = info.BestName()
   106  	if info.HasV1() {
   107  		m.InfoHash.Set(mi.HashInfoBytes())
   108  	}
   109  	if info.HasV2() {
   110  		m.V2InfoHash.Set(infohash_v2.HashBytes(mi.InfoBytes))
   111  	}
   112  	m.Params = make(url.Values)
   113  	m.Params["ws"] = mi.UrlList
   114  	return
   115  }
   116  
   117  // Returns the announce-list converted from the old single announce field if necessary.
   118  func (mi *MetaInfo) UpvertedAnnounceList() AnnounceList {
   119  	if mi.AnnounceList.OverridesAnnounce(mi.Announce) {
   120  		return mi.AnnounceList
   121  	}
   122  	if mi.Announce != "" {
   123  		return [][]string{{mi.Announce}}
   124  	}
   125  	return nil
   126  }