Skip to content

Commit

Permalink
Feat: display download speed and remaining time (#32)
Browse files Browse the repository at this point in the history
* chore(util): format size

* feat(dl): add download speed and time

* feat(dl): fix zero division

* chore: fix linting issue
  • Loading branch information
dreamjz authored Mar 12, 2024
1 parent a855c14 commit 64beefb
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 9 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/charmbracelet/log v0.3.1
github.com/grafov/m3u8 v0.12.0
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/magiconair/properties v1.8.7
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.0
golang.org/x/net v0.19.0
Expand All @@ -23,7 +24,6 @@ require (
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
Expand Down
9 changes: 6 additions & 3 deletions internal/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,15 @@ func countProgressBar(p *tea.Program, total int64, fileName string) *progressbar

func progressBar(p *tea.Program, total int64, fileName string) *progressbar.ProgressBar {
pw := &progressbar.ProgressWriter{
FileName: fileName,
Total: total,
OnProgress: func(fileName string, ratio float64) {
FileName: fileName,
Total: total,
StartTime: time.Now(),
OnProgress: func(fileName string, ratio, dltime float64, speed int64) {
p.Send(progressbar.ProgressMsg{
FileName: fileName,
Ratio: ratio,
DLTime: dltime,
Speed: speed,
})
},
}
Expand Down
19 changes: 17 additions & 2 deletions internal/tui/progressbar/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"sync"
"sync/atomic"
"time"

"github.com/charmbracelet/bubbles/progress"
tea "github.com/charmbracelet/bubbletea"
Expand Down Expand Up @@ -40,7 +41,10 @@ type ProgressWriter struct {
File *os.File
Reader io.Reader
FileName string
OnProgress func(string, float64)
StartTime time.Time
Speed int64
DLTime float64
OnProgress func(string, float64, float64, int64)
}

func (pw *ProgressWriter) Start(p *tea.Program) (int64, error) {
Expand All @@ -60,7 +64,18 @@ func (pw *ProgressWriter) Start(p *tea.Program) (int64, error) {
func (pw *ProgressWriter) Write(p []byte) (int, error) {
pw.Downloaded += int64(len(p))
if pw.Total > 0 && pw.OnProgress != nil {
pw.OnProgress(pw.FileName, float64(pw.Downloaded)/float64(pw.Total))
t := int64(time.Since(pw.StartTime).Seconds())
var speed int64
if t > 0 {
speed = pw.Downloaded / t
}

var dltime float64
if speed > 0 {
dltime = float64((pw.Total - pw.Downloaded) / speed)
}

pw.OnProgress(pw.FileName, float64(pw.Downloaded)/float64(pw.Total), dltime, speed)
}
return len(p), nil
}
Expand Down
4 changes: 4 additions & 0 deletions internal/tui/progressbar/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const (
type ProgressMsg struct {
FileName string
Ratio float64
Speed int64
DLTime float64
}

type ProgressCompleteMsg struct{}
Expand Down Expand Up @@ -53,6 +55,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:cyclop
fileName, ratio := msg.FileName, msg.Ratio
if pb, ok := m.Pbs[fileName]; ok {
cmds = append(cmds, pb.Progress.SetPercent(ratio))
pb.Pw.Speed = msg.Speed
pb.Pw.DLTime = msg.DLTime
}
return m, tea.Batch(cmds...)

Expand Down
9 changes: 6 additions & 3 deletions internal/tui/progressbar/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"fmt"
"sort"
"strings"
"time"

"github.com/acgtools/hanime-hunter/pkg/util"
"github.com/charmbracelet/lipgloss"
)

Expand Down Expand Up @@ -59,7 +61,7 @@ func (m *Model) View() string {
if pb.Pc != nil {
stats = fmt.Sprintf("%d/%d", pb.Pc.Downloaded.Load(), pb.Pc.Total)
} else {
stats = getDLStatus(pb.Pw.Downloaded, pb.Pw.Total)
stats = getDLStatus(pb.Pw)
}

status := renderPbStatus(pb.Status)
Expand Down Expand Up @@ -110,6 +112,7 @@ func pbMapToSortedSlice(m map[string]*ProgressBar, w int) []*ProgressBar {
return res
}

func getDLStatus(downloaded, total int64) string {
return fmt.Sprintf("%.2f MiB/%.2f MiB", float64(downloaded)/mib, float64(total)/mib)
func getDLStatus(pw *ProgressWriter) string {
d := time.Duration(pw.DLTime) * time.Second
return fmt.Sprintf("%.2f MiB/%.2f MiB %s %s", float64(pw.Downloaded)/mib, float64(pw.Total)/mib, util.FormatSize(pw.Speed), d.String())
}
25 changes: 25 additions & 0 deletions pkg/util/math.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package util

import "fmt"

// FormatSize formats the file size and ensure the number of size always > 1.
// the unit of parameter `size` is Byte.
func FormatSize(size int64) string {
units := [4]string{"B", "KiB", "MiB", "GiB"}

i := 0
for v := size; ; i++ {
if v>>10 < 1 || i > 3 {
break
}
v >>= 10
}
if i > 3 { //nolint:gomnd
i = 3
}

tmp := 1 << (i * 10) //nolint:gomnd
num := float64(size) / float64(tmp)

return fmt.Sprintf("%.2f %s/s", num, units[i])
}
68 changes: 68 additions & 0 deletions pkg/util/math_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package util_test

import (
"testing"

"github.com/acgtools/hanime-hunter/pkg/util"
"github.com/magiconair/properties/assert"
)

func TestFormatSize(t *testing.T) {
t.Parallel()

for _, tc := range []struct {
name string
size int64
res string
}{
{
name: "0",
size: 0,
res: "0.00 B/s",
},
{
name: "512",
size: 512,
res: "512.00 B/s",
},
{
name: "1024",
size: 1024,
res: "1.00 KiB/s",
},
{
name: "1048576",
size: 1048576,
res: "1.00 MiB/s",
},
{
name: "1073741824",
size: 1073741824,
res: "1.00 GiB/s",
},
{
name: "1023",
size: 1023,
res: "1023.00 B/s",
},
{
name: "1025",
size: 1025,
res: "1.00 KiB/s",
},
{
name: "1,099,511,627,776", // 1TiB
size: 1099511627776,
res: "1024.00 GiB/s",
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

s := util.FormatSize(tc.size)

assert.Equal(t, s, tc.res)
})
}
}

0 comments on commit 64beefb

Please sign in to comment.