|
|
|
package mget
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Redo struct {
|
|
|
|
Is206 bool `json:"is_206"`
|
|
|
|
OriginUri string `json:"origin_uri"`
|
|
|
|
Date time.Time `json:"date"`
|
|
|
|
Filename string `json:"filename"`
|
|
|
|
ContentLength uint64 `json:"content_length"`
|
|
|
|
Range []Range `json:"range"`
|
|
|
|
TimeCost uint64 `json:"time_cost"`
|
|
|
|
rangeUpdated bool
|
|
|
|
startDate time.Time
|
|
|
|
startCount uint64
|
|
|
|
lastUpdate time.Time
|
|
|
|
lastTotal uint64
|
|
|
|
speed float64
|
|
|
|
avgSpeed float64
|
|
|
|
total uint64
|
|
|
|
isRedo bool
|
|
|
|
sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) CacheTotal() uint64 {
|
|
|
|
return r.total
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) Total() uint64 {
|
|
|
|
var total uint64
|
|
|
|
for {
|
|
|
|
r.RLock()
|
|
|
|
for _, v := range r.Range {
|
|
|
|
total += v.Max - v.Min + 1
|
|
|
|
}
|
|
|
|
r.total = total
|
|
|
|
r.RUnlock()
|
|
|
|
if r.total > r.ContentLength && r.ContentLength > 0 {
|
|
|
|
r.reform()
|
|
|
|
total = 0
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return total
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) Update(start, end int) error {
|
|
|
|
if start < 0 || end < 0 || start > end {
|
|
|
|
return fmt.Errorf("invalid range: %d-%d", start, end)
|
|
|
|
}
|
|
|
|
r.Lock()
|
|
|
|
defer r.Unlock()
|
|
|
|
r.rangeUpdated = true
|
|
|
|
if r.lastUpdate.IsZero() {
|
|
|
|
r.startDate = time.Now()
|
|
|
|
for _, v := range r.Range {
|
|
|
|
r.startCount += v.Max - v.Min + 1
|
|
|
|
}
|
|
|
|
time.Sleep(time.Millisecond)
|
|
|
|
}
|
|
|
|
r.Range = append(r.Range, Range{uint64(start), uint64(end)})
|
|
|
|
now := time.Now()
|
|
|
|
if now.Sub(r.lastUpdate) >= time.Millisecond*500 {
|
|
|
|
var total uint64
|
|
|
|
for _, v := range r.Range {
|
|
|
|
total += v.Max - v.Min + 1
|
|
|
|
}
|
|
|
|
r.total = total
|
|
|
|
r.speed = float64(total-r.lastTotal) / (float64(now.Sub(r.lastUpdate).Milliseconds()) / 1000.00)
|
|
|
|
if !r.lastUpdate.IsZero() {
|
|
|
|
r.TimeCost += uint64(now.Sub(r.lastUpdate).Milliseconds())
|
|
|
|
}
|
|
|
|
r.avgSpeed = float64(total-r.startCount) / (float64(now.Sub(r.startDate).Milliseconds()) / 1000.00)
|
|
|
|
r.lastTotal = total
|
|
|
|
r.lastUpdate = now
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) Percent() float64 {
|
|
|
|
return float64(r.Total()) / float64(r.ContentLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) FormatPercent() string {
|
|
|
|
return fmt.Sprintf("%.2f%%", r.Percent()*100)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) FormatSpeed(unit string) string {
|
|
|
|
switch strings.ToLower(unit) {
|
|
|
|
case "kb":
|
|
|
|
return fmt.Sprintf("%.2f KB/s", r.speed/1024)
|
|
|
|
case "mb":
|
|
|
|
return fmt.Sprintf("%.2f MB/s", r.speed/1024/1024)
|
|
|
|
case "gb":
|
|
|
|
return fmt.Sprintf("%.2f GB/s", r.speed/1024/1024/1024)
|
|
|
|
default:
|
|
|
|
return fmt.Sprintf("%.2f B/s", r.speed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) FormatAvgSpeed(unit string) string {
|
|
|
|
switch strings.ToLower(unit) {
|
|
|
|
case "kb":
|
|
|
|
return fmt.Sprintf("%.2f KB/s", r.avgSpeed/1024)
|
|
|
|
case "mb":
|
|
|
|
return fmt.Sprintf("%.2f MB/s", r.avgSpeed/1024/1024)
|
|
|
|
case "gb":
|
|
|
|
return fmt.Sprintf("%.2f GB/s", r.avgSpeed/1024/1024/1024)
|
|
|
|
default:
|
|
|
|
return fmt.Sprintf("%.2f B/s", r.avgSpeed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) Speed() float64 {
|
|
|
|
return r.speed
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) AverageSpeed() float64 {
|
|
|
|
return r.avgSpeed
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) Save() error {
|
|
|
|
var err error
|
|
|
|
err = r.reform()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if r.Filename != "" {
|
|
|
|
data, err := json.Marshal(r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.Lock()
|
|
|
|
defer r.Unlock()
|
|
|
|
return os.WriteFile(r.Filename+".bgrd", data, 0644)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) reform() error {
|
|
|
|
r.Lock()
|
|
|
|
defer r.Unlock()
|
|
|
|
if !r.rangeUpdated {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
tmp, err := r.uniformRange(r.Range)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.Range = tmp
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) uniformRange(rg []Range) ([]Range, error) {
|
|
|
|
return uniformRange(rg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redo) ReverseRange() ([]Range, error) {
|
|
|
|
r.reform()
|
|
|
|
r.RLock()
|
|
|
|
defer r.RUnlock()
|
|
|
|
return r.uniformRange(subRange([]Range{{0, r.ContentLength - 1}}, r.Range))
|
|
|
|
}
|