forked from anacrolix/torrent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
metadata.go
202 lines (168 loc) · 4.54 KB
/
metadata.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package torrent
import (
"time"
"github.com/anacrolix/missinggo"
"github.com/james-lawrence/torrent/bencode"
"github.com/james-lawrence/torrent/metainfo"
"github.com/james-lawrence/torrent/storage"
"github.com/pkg/errors"
)
// Option for the torrent.
type Option func(*Metadata)
// OptionTrackers set the trackers for the torrent.
func OptionTrackers(trackers [][]string) Option {
return func(t *Metadata) {
t.Trackers = trackers
}
}
// OptionNodes supplimental nodes to add to the dht of the client.
func OptionNodes(nodes ...string) Option {
return func(t *Metadata) {
t.DHTNodes = nodes
}
}
// OptionDisplayName set the display name for the torrent.
func OptionDisplayName(dn string) Option {
return func(t *Metadata) {
t.DisplayName = dn
}
}
// OptionInfo set the info bytes for the torrent.
func OptionInfo(i []byte) Option {
return func(t *Metadata) {
t.InfoBytes = i
}
}
// OptionChunk sets the size of the chunks to use for outbound requests
func OptionChunk(s int) Option {
return func(t *Metadata) {
t.ChunkSize = s
}
}
// OptionStorage set the storage implementation for the torrent.
func OptionStorage(s storage.ClientImpl) Option {
return func(t *Metadata) {
t.Storage = s
}
}
// OptionWebseeds set the webseed hosts for the torrent.
func OptionWebseeds(seeds []string) Option {
return func(t *Metadata) {
t.Webseeds = seeds
}
}
// Metadata specifies the metadata of a torrent for adding to a client.
// There are helpers for magnet URIs and torrent metainfo files.
type Metadata struct {
// The tiered tracker URIs.
Trackers [][]string
InfoHash metainfo.Hash
InfoBytes []byte
// The name to use if the Name field from the Info isn't available.
DisplayName string
Webseeds []string
DHTNodes []string
// The chunk size to use for outbound requests. Defaults to 16KiB if not
// set.
ChunkSize int
Storage storage.ClientImpl
}
// Merge Metadata options into the current metadata.
func (t Metadata) Merge(options ...Option) Metadata {
for _, opt := range options {
opt(&t)
}
return t
}
// New create a torrent from the metainfo.MetaInfo and any additional options.
func New(info metainfo.Hash, options ...Option) (t Metadata, err error) {
t = Metadata{
InfoHash: info,
}.Merge(options...)
return t, nil
}
// NewFromMetaInfoFile loads torrent metadata stored in a file.
func NewFromMetaInfoFile(path string, options ...Option) (t Metadata, err error) {
var (
mi *metainfo.MetaInfo
)
if mi, err = metainfo.LoadFromFile(path); err != nil {
return t, err
}
return NewFromMetaInfo(mi, options...)
}
// NewFromFile convience method to create a torrent directly from a file.
func NewFromFile(path string, options ...Option) (t Metadata, err error) {
var (
encoded []byte
info = metainfo.Info{PieceLength: missinggo.MiB}
)
if err = info.BuildFromFilePath(path); err != nil {
return t, errors.WithStack(err)
}
if encoded, err = bencode.Marshal(info); err != nil {
return t, errors.WithStack(err)
}
if t, err = New(metainfo.HashBytes(encoded), OptionInfo(encoded), OptionDisplayName(info.Name)); err != nil {
return t, errors.WithStack(err)
}
return t.Merge(options...), nil
}
// NewFromMagnet creates a torrent from a magnet uri.
func NewFromMagnet(uri string) (t Metadata, err error) {
var (
m metainfo.Magnet
)
if m, err = metainfo.ParseMagnetURI(uri); err != nil {
return t, errors.WithStack(err)
}
return New(
m.InfoHash,
OptionDisplayName(m.DisplayName),
OptionTrackers([][]string{m.Trackers}),
OptionWebseeds(m.Params["ws"]),
)
}
// NewFromInfo creates a torrent from metainfo.Info
func NewFromInfo(i metainfo.Info, options ...Option) (t Metadata, err error) {
var (
encoded []byte
)
if encoded, err = bencode.Marshal(i); err != nil {
return t, err
}
return New(
metainfo.HashBytes(encoded),
append(options, OptionInfo(encoded))...,
)
}
// NewFromMetaInfo create a torrent from metainfo
func NewFromMetaInfo(mi *metainfo.MetaInfo, options ...Option) (t Metadata, err error) {
var (
info metainfo.Info
)
if info, err = mi.UnmarshalInfo(); err != nil {
return t, err
}
options = append([]Option{
OptionInfo(mi.InfoBytes),
OptionDisplayName(info.Name),
OptionTrackers(mi.UpvertedAnnounceList()),
OptionWebseeds(mi.UrlList),
OptionNodes(mi.NodeList()...),
},
options...,
)
return New(
mi.HashInfoBytes(),
options...,
)
}
// Metainfo generate metainfo from the metadata.
func (t Metadata) Metainfo() metainfo.MetaInfo {
return metainfo.MetaInfo{
AnnounceList: make([][]string, 0),
InfoBytes: t.InfoBytes,
CreationDate: time.Now().Unix(),
}
}