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 }