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)) }