cmd/streamdata: simple progress indicator

For downloading stream VODs, since they are pretty large.

Signed-off-by: Yonah <contrib@gensokyo.uk>
This commit is contained in:
2026-03-18 01:39:27 +09:00
parent 26afe7f386
commit 68cec9f18f
3 changed files with 95 additions and 1 deletions

View File

@@ -0,0 +1,90 @@
package main
import (
"io"
"os"
"strconv"
"sync/atomic"
"syscall"
"time"
"hakurei.app/ext"
)
// pendingMessage is written to the current line every tick if non-nil.
var pendingMessage atomic.Pointer[string]
func init() {
ticker := time.NewTicker(time.Second / 100)
go func() {
for {
<-ticker.C
p := pendingMessage.Swap(nil)
if p == nil {
continue
}
os.Stderr.WriteString("\x1b[2K" + *p + "\r")
}
}()
}
// progressWriter writes a progress bar via writeCurrent.
type progressWriter struct {
// For progress indication. Has no effect unless greater than zero.
contentLength int64
// Accumulated bytes.
n int64
// Increments with n, but resets periodically.
segment int64
// Reference time since segment was reset.
ref time.Time
}
// measureInterval is the interval at which progressWriter.segment is reset.
const measureInterval = 5 * time.Second
func (pw *progressWriter) Write(p []byte) (n int, err error) {
n = len(p)
pw.n += int64(n)
pw.segment += int64(n)
d := time.Now().Sub(pw.ref)
var s string
if pw.contentLength > 0 {
s += " " + strconv.FormatInt(pw.n/(1<<20), 10) + " / " +
strconv.FormatInt(pw.contentLength/(1<<20), 10) + " MiB |"
}
s += " " + strconv.FormatFloat(
float64(pw.segment)/d.Seconds()/float64(1<<20),
'f', 2, 64,
) + " MiB/s"
pendingMessage.Store(&s)
if d > measureInterval {
pw.segment = 0
pw.ref = time.Now()
}
return
}
// copyProgress is like [io.Copy], but prints progress to [os.Stderr] if it is
// attached to a terminal.
//
// Percentage is emitted if contentLength is greater than zero.
func copyProgress(dst io.Writer, src io.Reader, contentLength int64) (
written int64,
err error,
) {
if !ext.Isatty(syscall.Stderr) {
return io.Copy(dst, src)
}
defer os.Stderr.WriteString("\n")
return io.Copy(io.MultiWriter(dst, &progressWriter{
contentLength: contentLength,
ref: time.Now(),
}), src)
}

4
go.mod
View File

@@ -1,3 +1,5 @@
module git.gensokyo.uk/yonah/streamdata
go 1.25.7
go 1.26
require hakurei.app v0.3.8-0.20260317115839-6cdb6a652b63

2
go.sum Normal file
View File

@@ -0,0 +1,2 @@
hakurei.app v0.3.8-0.20260317115839-6cdb6a652b63 h1:Nx5eaHEbWUx6pwA4A+EdmHXvCvO3Xbw7PPcT1QbsVcA=
hakurei.app v0.3.8-0.20260317115839-6cdb6a652b63/go.mod h1:rIWpoHYiZtIPue49bQLkROpQHYh/j2VjJGFrzNrvixU=