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

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