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