| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- package torrent
- import (
- "strconv"
- "strings"
- "github.com/anacrolix/chansync/events"
- "github.com/anacrolix/missinggo/v2/pubsub"
- "github.com/anacrolix/sync"
- "github.com/anacrolix/torrent/metainfo"
- )
- // The Torrent's infohash. This is fixed and cannot change. It uniquely identifies a torrent.
- func (t *Torrent) InfoHash() metainfo.Hash {
- return *t.canonicalShortInfohash()
- }
- // Returns a channel that is closed when the info (.Info()) for the torrent has become available.
- func (t *Torrent) GotInfo() events.Done {
- return t.gotMetainfoC
- }
- // Returns the metainfo info dictionary, or nil if it's not yet available.
- func (t *Torrent) Info() (info *metainfo.Info) {
- t.nameMu.RLock()
- info = t.info
- t.nameMu.RUnlock()
- return
- }
- // Returns a Reader bound to the torrent's data. All read calls block until the data requested is
- // actually available. Note that you probably want to ensure the Torrent Info is available first.
- func (t *Torrent) NewReader() Reader {
- return t.newReader(0, t.length())
- }
- func (t *Torrent) newReader(offset, length int64) Reader {
- r := reader{
- mu: t.cl.locker(),
- t: t,
- offset: offset,
- length: length,
- }
- r.readaheadFunc = defaultReadaheadFunc
- t.addReader(&r)
- return &r
- }
- type PieceStateRuns []PieceStateRun
- func (me PieceStateRuns) String() (s string) {
- if len(me) > 0 {
- var sb strings.Builder
- sb.WriteString(me[0].String())
- for i := 1; i < len(me); i += 1 {
- sb.WriteByte(' ')
- sb.WriteString(me[i].String())
- }
- return sb.String()
- }
- return
- }
- // Returns the state of pieces of the torrent. They are grouped into runs of same state. The sum of
- // the state run-lengths is the number of pieces in the torrent.
- func (t *Torrent) PieceStateRuns() (runs PieceStateRuns) {
- t.cl.rLock()
- runs = t.pieceStateRuns()
- t.cl.rUnlock()
- return
- }
- func (t *Torrent) PieceState(piece pieceIndex) (ps PieceState) {
- t.cl.rLock()
- ps = t.pieceState(piece)
- t.cl.rUnlock()
- return
- }
- // The number of pieces in the torrent. This requires that the info has been
- // obtained first.
- func (t *Torrent) NumPieces() pieceIndex {
- return t.numPieces()
- }
- // Get missing bytes count for specific piece.
- func (t *Torrent) PieceBytesMissing(piece int) int64 {
- t.cl.rLock()
- defer t.cl.rUnlock()
- return int64(t.pieces[piece].bytesLeft())
- }
- // Drop the torrent from the client, and close it. It's always safe to do
- // this. No data corruption can, or should occur to either the torrent's data,
- // or connected peers.
- func (t *Torrent) Drop() {
- var wg sync.WaitGroup
- defer wg.Wait()
- t.cl.lock()
- defer t.cl.unlock()
- err := t.cl.dropTorrent(t, &wg)
- if err != nil {
- panic(err)
- }
- }
- // Number of bytes of the entire torrent we have completed. This is the sum of
- // completed pieces, and dirtied chunks of incomplete pieces. Do not use this
- // for download rate, as it can go down when pieces are lost or fail checks.
- // Sample Torrent.Stats.DataBytesRead for actual file data download rate.
- func (t *Torrent) BytesCompleted() int64 {
- t.cl.rLock()
- defer t.cl.rUnlock()
- return t.bytesCompleted()
- }
- // The subscription emits as (int) the index of pieces as their state changes.
- // A state change is when the PieceState for a piece alters in value.
- func (t *Torrent) SubscribePieceStateChanges() *pubsub.Subscription[PieceStateChange] {
- return t.pieceStateChanges.Subscribe()
- }
- // Returns true if the torrent is currently being seeded. This occurs when the
- // client is willing to upload without wanting anything in return.
- func (t *Torrent) Seeding() (ret bool) {
- t.cl.rLock()
- ret = t.seeding()
- t.cl.rUnlock()
- return
- }
- // Clobbers the torrent display name if metainfo is unavailable.
- // The display name is used as the torrent name while the metainfo is unavailable.
- func (t *Torrent) SetDisplayName(dn string) {
- t.nameMu.Lock()
- if !t.haveInfo() {
- t.displayName = dn
- }
- t.nameMu.Unlock()
- }
- // The current working name for the torrent. Either the name in the info dict,
- // or a display name given such as by the dn value in a magnet link, or "".
- func (t *Torrent) Name() string {
- return t.name()
- }
- // The completed length of all the torrent data, in all its files. This is
- // derived from the torrent info, when it is available.
- func (t *Torrent) Length() int64 {
- return t._length.Value
- }
- // Returns a run-time generated metainfo for the torrent that includes the
- // info bytes and announce-list as currently known to the client.
- func (t *Torrent) Metainfo() metainfo.MetaInfo {
- t.cl.rLock()
- defer t.cl.rUnlock()
- return t.newMetaInfo()
- }
- func (t *Torrent) addReader(r *reader) {
- t.cl.lock()
- defer t.cl.unlock()
- if t.readers == nil {
- t.readers = make(map[*reader]struct{})
- }
- t.readers[r] = struct{}{}
- r.posChanged()
- }
- func (t *Torrent) deleteReader(r *reader) {
- delete(t.readers, r)
- t.readersChanged()
- }
- // Raise the priorities of pieces in the range [begin, end) to at least Normal
- // priority. Piece indexes are not the same as bytes. Requires that the info
- // has been obtained, see Torrent.Info and Torrent.GotInfo.
- func (t *Torrent) DownloadPieces(begin, end pieceIndex) {
- t.cl.lock()
- t.downloadPiecesLocked(begin, end)
- t.cl.unlock()
- }
- func (t *Torrent) downloadPiecesLocked(begin, end pieceIndex) {
- for i := begin; i < end; i++ {
- if t.pieces[i].priority.Raise(PiecePriorityNormal) {
- t.updatePiecePriority(i, "Torrent.DownloadPieces")
- }
- }
- }
- func (t *Torrent) CancelPieces(begin, end pieceIndex) {
- t.cl.lock()
- t.cancelPiecesLocked(begin, end, "Torrent.CancelPieces")
- t.cl.unlock()
- }
- func (t *Torrent) cancelPiecesLocked(begin, end pieceIndex, reason updateRequestReason) {
- for i := begin; i < end; i++ {
- p := &t.pieces[i]
- if p.priority == PiecePriorityNone {
- continue
- }
- p.priority = PiecePriorityNone
- t.updatePiecePriority(i, reason)
- }
- }
- func (t *Torrent) initFiles() {
- info := t.info
- var offset int64
- t.files = new([]*File)
- for _, fi := range t.info.UpvertedFiles() {
- *t.files = append(*t.files, &File{
- t,
- strings.Join(append([]string{info.BestName()}, fi.BestPath()...), "/"),
- offset,
- fi.Length,
- fi,
- fi.DisplayPath(info),
- PiecePriorityNone,
- fi.PiecesRoot,
- })
- offset += fi.Length
- if info.FilesArePieceAligned() {
- offset = (offset + info.PieceLength - 1) / info.PieceLength * info.PieceLength
- }
- }
- }
- // Returns handles to the files in the torrent. This requires that the Info is
- // available first.
- func (t *Torrent) Files() []*File {
- return *t.files
- }
- func (t *Torrent) AddPeers(pp []PeerInfo) (n int) {
- t.cl.lock()
- defer t.cl.unlock()
- n = t.addPeers(pp)
- return
- }
- // Marks the entire torrent for download. Requires the info first, see
- // GotInfo. Sets piece priorities for historical reasons.
- func (t *Torrent) DownloadAll() {
- t.DownloadPieces(0, t.numPieces())
- }
- func (t *Torrent) String() string {
- s := t.name()
- if s == "" {
- return t.canonicalShortInfohash().HexString()
- } else {
- return strconv.Quote(s)
- }
- }
- func (t *Torrent) AddTrackers(announceList [][]string) {
- t.cl.lock()
- defer t.cl.unlock()
- t.addTrackers(announceList)
- }
- func (t *Torrent) ModifyTrackers(announceList [][]string) {
- t.cl.lock()
- defer t.cl.unlock()
- t.modifyTrackers(announceList)
- }
- func (t *Torrent) Piece(i pieceIndex) *Piece {
- return t.piece(i)
- }
- func (t *Torrent) PeerConns() []*PeerConn {
- t.cl.rLock()
- defer t.cl.rUnlock()
- ret := make([]*PeerConn, 0, len(t.conns))
- for c := range t.conns {
- ret = append(ret, c)
- }
- return ret
- }
- func (t *Torrent) WebseedPeerConns() []*Peer {
- t.cl.rLock()
- defer t.cl.rUnlock()
- ret := make([]*Peer, 0, len(t.conns))
- for _, c := range t.webSeeds {
- ret = append(ret, c)
- }
- return ret
- }
|