copier: прогресс по байтам, скорость и ETA

- Прогрессбар по скопированным байтам (doneBytes / totalBytes)
- SpeedBPS и ETASec добавлены в Task
- Dashboard показывает скорость (МБ/с) и ETA справа от прогрессбара

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-23 22:00:14 +03:00
parent 7c5736b935
commit 5b3cb9e393
3 changed files with 61 additions and 6 deletions

View File

@@ -9,6 +9,7 @@ import (
"os/exec"
"path/filepath"
"sync"
"time"
"jukebox_maker/internal/config"
"jukebox_maker/internal/db"
@@ -150,36 +151,63 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
return
}
// суммарный объём для прогресса (всех файлов в списке)
var totalBytes int64
for _, f := range files {
totalBytes += f.size
}
total := len(files)
copied := 0
var doneBytes int64
startTime := time.Now()
for i, f := range files {
select {
case <-ctx.Done():
c.tasks.Update(taskID, func(t *task.Task) {
t.Status = task.StatusCanceled
t.Message = "Отменено"
t.SpeedBPS = 0
t.ETASec = 0
})
return
default:
}
if f.size > available {
// файл не влезает — пробуем следующий
continue
}
prog := int(float64(i+1) / float64(total) * 100)
elapsed := time.Since(startTime).Seconds()
var speedBPS, etaSec int64
if elapsed > 0 && doneBytes > 0 {
speedBPS = int64(float64(doneBytes) / elapsed)
remaining := totalBytes - doneBytes
if speedBPS > 0 {
etaSec = remaining / speedBPS
}
}
prog := int(float64(doneBytes) / float64(totalBytes) * 100)
msg := fmt.Sprintf("Копирование %s (%d/%d)", filepath.Base(f.srcAbs), i+1, total)
setStatus(task.StatusRunning, msg, prog)
// destination mirrors source structure under destRoot
c.tasks.Update(taskID, func(t *task.Task) {
t.Status = task.StatusRunning
t.Message = msg
t.Progress = prog
t.SpeedBPS = speedBPS
t.ETASec = int(etaSec)
})
dstAbs := filepath.Join(destRoot, f.relPath)
if err := rsyncFile(ctx, f.srcAbs, dstAbs); err != nil {
if errors.Is(err, context.Canceled) {
c.tasks.Update(taskID, func(t *task.Task) {
t.Status = task.StatusCanceled
t.Message = "Отменено"
t.SpeedBPS = 0
t.ETASec = 0
})
return
}
@@ -187,6 +215,7 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
}
available -= f.size
doneBytes += f.size
copied++
_ = database.RecordCopy(db.CopyRecord{
DiskID: opts.DiskID,