Files
got/download/download.go

78 lines
2.7 KiB
Go

// Package download defines the core abstraction of the program: a Download is
// a thing you can Run and ask for a Stat snapshot. Where a single-threaded
// Command/event-poll reactor would express this, we use one
// goroutine per Download blocking on real I/O. Following Rob Pike, the data
// (Stat) and the interface are kept small; the algorithms fall out of them.
package download
import "context"
// Status is the coarse lifecycle state of a download, using the
// active/waiting/complete/error vocabulary.
type Status int
const (
Waiting Status = iota
Active
Seeding
Complete
Errored
)
func (s Status) String() string {
switch s {
case Waiting:
return "waiting"
case Active:
return "active"
case Seeding:
return "seeding"
case Complete:
return "complete"
case Errored:
return "error"
default:
return "unknown"
}
}
// Stat is a snapshot of a download's progress. Every field is a plain value so
// a renderer running in another goroutine can copy it without sharing memory.
// Completed and Uploaded are cumulative byte counters; the renderer derives
// speeds from successive snapshots rather than each download tracking its own.
type Stat struct {
Name string
// ID is a process-unique stable identity for this download. Name can
// collide (two pre-metadata magnets both show their source string), so the
// renderer keys its per-download speed samples on ID, not Name.
ID string
IsBT bool // true for BitTorrent; progress always shows CN and shows SD for torrents
Status Status
Total int64 // total bytes, or -1 if not yet known
Completed int64 // bytes downloaded so far
Uploaded int64 // bytes uploaded (BitTorrent), 0 otherwise
Conns int // active connections / peers
Seeders int // connected seeders (BitTorrent only)
}
// A Download is one logical job: a URL, a torrent, a magnet. Run blocks in its
// own goroutine until the work finishes, fails, or ctx is cancelled. Stat may
// be called concurrently at any time and must not block; implementations back
// it with atomics or a briefly-held lock.
type Download interface {
Name() string
Run(ctx context.Context) error
Stat() Stat
}
// Seeder is an optional capability of a Download whose work continues after its
// content is fully downloaded — a torrent that keeps running to seed.
// SeedingStarted is closed at that download->seed transition. The engine uses it
// to free the concurrency slot the moment a download stops downloading: a
// seed-only torrent is no longer a download, so it must not count against
// --max-concurrent-downloads (-j) and block a queued download. HTTP downloads do
// not implement this, so they hold their slot until they finish.
type Seeder interface {
SeedingStarted() <-chan struct{}
}