]> Sergey Matveev's repositories - btrtrc.git/blob - t.go
Inlineable `DownloadPieces()` and `CancelPieces()` (#623)
[btrtrc.git] / t.go
1 package torrent
2
3 import (
4         "strconv"
5         "strings"
6
7         "github.com/anacrolix/missinggo/pubsub"
8         "github.com/anacrolix/sync"
9
10         "github.com/anacrolix/torrent/metainfo"
11 )
12
13 // The Torrent's infohash. This is fixed and cannot change. It uniquely identifies a torrent.
14 func (t *Torrent) InfoHash() metainfo.Hash {
15         return t.infoHash
16 }
17
18 // Returns a channel that is closed when the info (.Info()) for the torrent has become available.
19 func (t *Torrent) GotInfo() (ret <-chan struct{}) {
20         // TODO: We shouldn't need to lock to take a channel here, if the event is only ever set.
21         t.nameMu.RLock()
22         ret = t.gotMetainfoC
23         t.nameMu.RUnlock()
24         return
25 }
26
27 // Returns the metainfo info dictionary, or nil if it's not yet available.
28 func (t *Torrent) Info() (info *metainfo.Info) {
29         t.nameMu.RLock()
30         info = t.info
31         t.nameMu.RUnlock()
32         return
33 }
34
35 // Returns a Reader bound to the torrent's data. All read calls block until the data requested is
36 // actually available. Note that you probably want to ensure the Torrent Info is available first.
37 func (t *Torrent) NewReader() Reader {
38         return t.newReader(0, *t.length)
39 }
40
41 func (t *Torrent) newReader(offset, length int64) Reader {
42         r := reader{
43                 mu:     t.cl.locker(),
44                 t:      t,
45                 offset: offset,
46                 length: length,
47         }
48         r.readaheadFunc = r.defaultReadaheadFunc
49         t.addReader(&r)
50         return &r
51 }
52
53 type PieceStateRuns []PieceStateRun
54
55 func (me PieceStateRuns) String() (s string) {
56         if len(me) > 0 {
57                 var sb strings.Builder
58                 sb.WriteString(me[0].String())
59                 for i := 1; i < len(me); i += 1 {
60                         sb.WriteByte(' ')
61                         sb.WriteString(me[i].String())
62                 }
63                 return sb.String()
64         }
65         return
66 }
67
68 // Returns the state of pieces of the torrent. They are grouped into runs of same state. The sum of
69 // the state run-lengths is the number of pieces in the torrent.
70 func (t *Torrent) PieceStateRuns() (runs PieceStateRuns) {
71         t.cl.rLock()
72         runs = t.pieceStateRuns()
73         t.cl.rUnlock()
74         return
75 }
76
77 func (t *Torrent) PieceState(piece pieceIndex) PieceState {
78         t.cl.rLock()
79         defer t.cl.rUnlock()
80         return t.pieceState(piece)
81 }
82
83 // The number of pieces in the torrent. This requires that the info has been
84 // obtained first.
85 func (t *Torrent) NumPieces() pieceIndex {
86         return t.numPieces()
87 }
88
89 // Get missing bytes count for specific piece.
90 func (t *Torrent) PieceBytesMissing(piece int) int64 {
91         t.cl.lock()
92         defer t.cl.unlock()
93
94         return int64(t.pieces[piece].bytesLeft())
95 }
96
97 // Drop the torrent from the client, and close it. It's always safe to do
98 // this. No data corruption can, or should occur to either the torrent's data,
99 // or connected peers.
100 func (t *Torrent) Drop() {
101         var wg sync.WaitGroup
102         defer wg.Wait()
103         t.cl.lock()
104         defer t.cl.unlock()
105         t.cl.dropTorrent(t.infoHash, &wg)
106 }
107
108 // Number of bytes of the entire torrent we have completed. This is the sum of
109 // completed pieces, and dirtied chunks of incomplete pieces. Do not use this
110 // for download rate, as it can go down when pieces are lost or fail checks.
111 // Sample Torrent.Stats.DataBytesRead for actual file data download rate.
112 func (t *Torrent) BytesCompleted() int64 {
113         t.cl.rLock()
114         defer t.cl.rUnlock()
115         return t.bytesCompleted()
116 }
117
118 // The subscription emits as (int) the index of pieces as their state changes.
119 // A state change is when the PieceState for a piece alters in value.
120 func (t *Torrent) SubscribePieceStateChanges() *pubsub.Subscription {
121         return t.pieceStateChanges.Subscribe()
122 }
123
124 // Returns true if the torrent is currently being seeded. This occurs when the
125 // client is willing to upload without wanting anything in return.
126 func (t *Torrent) Seeding() bool {
127         t.cl.lock()
128         defer t.cl.unlock()
129         return t.seeding()
130 }
131
132 // Clobbers the torrent display name. The display name is used as the torrent
133 // name if the metainfo is not available.
134 func (t *Torrent) SetDisplayName(dn string) {
135         t.nameMu.Lock()
136         defer t.nameMu.Unlock()
137         if t.haveInfo() {
138                 return
139         }
140         t.displayName = dn
141 }
142
143 // The current working name for the torrent. Either the name in the info dict,
144 // or a display name given such as by the dn value in a magnet link, or "".
145 func (t *Torrent) Name() string {
146         return t.name()
147 }
148
149 // The completed length of all the torrent data, in all its files. This is
150 // derived from the torrent info, when it is available.
151 func (t *Torrent) Length() int64 {
152         return *t.length
153 }
154
155 // Returns a run-time generated metainfo for the torrent that includes the
156 // info bytes and announce-list as currently known to the client.
157 func (t *Torrent) Metainfo() metainfo.MetaInfo {
158         t.cl.lock()
159         defer t.cl.unlock()
160         return t.newMetaInfo()
161 }
162
163 func (t *Torrent) addReader(r *reader) {
164         t.cl.lock()
165         defer t.cl.unlock()
166         if t.readers == nil {
167                 t.readers = make(map[*reader]struct{})
168         }
169         t.readers[r] = struct{}{}
170         r.posChanged()
171 }
172
173 func (t *Torrent) deleteReader(r *reader) {
174         delete(t.readers, r)
175         t.readersChanged()
176 }
177
178 // Raise the priorities of pieces in the range [begin, end) to at least Normal
179 // priority. Piece indexes are not the same as bytes. Requires that the info
180 // has been obtained, see Torrent.Info and Torrent.GotInfo.
181 func (t *Torrent) DownloadPieces(begin, end pieceIndex) {
182         t.cl.lock()
183         t.downloadPiecesLocked(begin, end)
184         t.cl.unlock()
185 }
186
187 func (t *Torrent) downloadPiecesLocked(begin, end pieceIndex) {
188         for i := begin; i < end; i++ {
189                 if t.pieces[i].priority.Raise(PiecePriorityNormal) {
190                         t.updatePiecePriority(i)
191                 }
192         }
193 }
194
195 func (t *Torrent) CancelPieces(begin, end pieceIndex) {
196         t.cl.lock()
197         t.cancelPiecesLocked(begin, end)
198         t.cl.unlock()
199 }
200
201 func (t *Torrent) cancelPiecesLocked(begin, end pieceIndex) {
202         for i := begin; i < end; i++ {
203                 p := &t.pieces[i]
204                 if p.priority == PiecePriorityNone {
205                         continue
206                 }
207                 p.priority = PiecePriorityNone
208                 t.updatePiecePriority(i)
209         }
210 }
211
212 func (t *Torrent) initFiles() {
213         var offset int64
214         t.files = new([]*File)
215         for _, fi := range t.info.UpvertedFiles() {
216                 var path []string
217                 if len(fi.PathUTF8) != 0 {
218                         path = fi.PathUTF8
219                 } else {
220                         path = fi.Path
221                 }
222                 dp := t.info.Name
223                 if len(fi.Path) != 0 {
224                         dp = strings.Join(fi.Path, "/")
225                 }
226                 *t.files = append(*t.files, &File{
227                         t,
228                         strings.Join(append([]string{t.info.Name}, path...), "/"),
229                         offset,
230                         fi.Length,
231                         fi,
232                         dp,
233                         PiecePriorityNone,
234                 })
235                 offset += fi.Length
236         }
237
238 }
239
240 // Returns handles to the files in the torrent. This requires that the Info is
241 // available first.
242 func (t *Torrent) Files() []*File {
243         return *t.files
244 }
245
246 func (t *Torrent) AddPeers(pp []PeerInfo) int {
247         cl := t.cl
248         cl.lock()
249         defer cl.unlock()
250         return t.addPeers(pp)
251 }
252
253 // Marks the entire torrent for download. Requires the info first, see
254 // GotInfo. Sets piece priorities for historical reasons.
255 func (t *Torrent) DownloadAll() {
256         t.DownloadPieces(0, t.numPieces())
257 }
258
259 func (t *Torrent) String() string {
260         s := t.name()
261         if s == "" {
262                 return t.infoHash.HexString()
263         } else {
264                 return strconv.Quote(s)
265         }
266 }
267
268 func (t *Torrent) AddTrackers(announceList [][]string) {
269         t.cl.lock()
270         defer t.cl.unlock()
271         t.addTrackers(announceList)
272 }
273
274 func (t *Torrent) Piece(i pieceIndex) *Piece {
275         return t.piece(i)
276 }
277
278 func (t *Torrent) PeerConns() []*PeerConn {
279         t.cl.rLock()
280         defer t.cl.rUnlock()
281         ret := make([]*PeerConn, 0, len(t.conns))
282         for c := range t.conns {
283                 ret = append(ret, c)
284         }
285         return ret
286 }