Add resumable downloads, download paths, queue controls and history
This commit is contained in:
1 parent
434f6441da
commit
6027cfabb9
43 files changed
+2138
-291
No files matched your search
@@ -0,0 +1,97 @@
|
||||
package fetch
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
const (
|
||||
MaxConnections = 16
|
||||
minSegmentBytes = int64(32) << 20
|
||||
)
|
||||
|
||||
type Segment struct {
|
||||
Start int64 `json:"start"`
|
||||
End int64 `json:"end"`
|
||||
Done int64 `json:"done"`
|
||||
}
|
||||
|
||||
type State struct {
|
||||
FileName string `json:"fileName"`
|
||||
Validator string `json:"validator"`
|
||||
TotalBytes int64 `json:"totalBytes"`
|
||||
Segments []Segment `json:"segments"`
|
||||
}
|
||||
|
||||
func (state State) DoneBytes() int64 {
|
||||
var done int64
|
||||
for _, segment := range state.Segments {
|
||||
done += segment.Done
|
||||
}
|
||||
return done
|
||||
}
|
||||
|
||||
func (state State) valid() bool {
|
||||
if state.FileName == "" || len(state.Segments) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, segment := range state.Segments {
|
||||
if segment.Start < 0 || segment.Done < 0 {
|
||||
return false
|
||||
}
|
||||
if segment.End == 0 && state.TotalBytes > 0 {
|
||||
return false
|
||||
}
|
||||
if segment.End > 0 && segment.Start+segment.Done > segment.End {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func planSegments(totalBytes int64, connections int) []Segment {
|
||||
if totalBytes <= 0 {
|
||||
return []Segment{{}}
|
||||
}
|
||||
count := min(int64(max(connections, 1)), max(totalBytes/minSegmentBytes, 1))
|
||||
size := totalBytes / count
|
||||
segments := make([]Segment, count)
|
||||
for index := range segments {
|
||||
start := int64(index) * size
|
||||
segments[index] = Segment{Start: start, End: start + size}
|
||||
}
|
||||
segments[count-1].End = totalBytes
|
||||
return segments
|
||||
}
|
||||
|
||||
type liveSegment struct {
|
||||
start int64
|
||||
end int64
|
||||
done atomic.Int64
|
||||
}
|
||||
|
||||
func newLiveSegment(segment Segment) *liveSegment {
|
||||
live := &liveSegment{start: segment.Start, end: segment.End}
|
||||
live.done.Store(segment.Done)
|
||||
return live
|
||||
}
|
||||
|
||||
func (segment *liveSegment) position() int64 {
|
||||
return segment.start + segment.done.Load()
|
||||
}
|
||||
|
||||
func (segment *liveSegment) bounded() bool {
|
||||
return segment.end > 0
|
||||
}
|
||||
|
||||
func (segment *liveSegment) remaining() int64 {
|
||||
if !segment.bounded() {
|
||||
return -1
|
||||
}
|
||||
return segment.end - segment.position()
|
||||
}
|
||||
|
||||
func (segment *liveSegment) complete() bool {
|
||||
return segment.bounded() && segment.position() >= segment.end
|
||||
}
|
||||
|
||||
func (segment *liveSegment) snapshot() Segment {
|
||||
return Segment{Start: segment.start, End: segment.end, Done: segment.done.Load()}
|
||||
}
|
||||
Reference in new issue
Block a user