parent
b596ccdf0f
commit
b6db90cc32
6 changed files with 143 additions and 1 deletions
|
@ -297,7 +297,9 @@ func Run(Retry bool, showStats bool, cmd *cobra.Command, f func() error) {
|
||||||
if !showStats && ShowStats() {
|
if !showStats && ShowStats() {
|
||||||
showStats = true
|
showStats = true
|
||||||
}
|
}
|
||||||
if showStats {
|
if fs.Config.Progress {
|
||||||
|
stopStats = startProgress()
|
||||||
|
} else if showStats {
|
||||||
stopStats = StartStats()
|
stopStats = StartStats()
|
||||||
}
|
}
|
||||||
SigInfoHandler()
|
SigInfoHandler()
|
||||||
|
|
118
cmd/progress.go
Normal file
118
cmd/progress.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
// Show the dynamic progress bar
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/fs/accounting"
|
||||||
|
"github.com/ncw/rclone/fs/log"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// interval between progress prints
|
||||||
|
defaultProgressInterval = 500 * time.Millisecond
|
||||||
|
// time format for logging
|
||||||
|
logTimeFormat = "2006-01-02 15:04:05"
|
||||||
|
)
|
||||||
|
|
||||||
|
// startProgress starts the progress bar printing
|
||||||
|
//
|
||||||
|
// It returns a channel which should be closed to stop the stats.
|
||||||
|
func startProgress() chan struct{} {
|
||||||
|
stopStats := make(chan struct{})
|
||||||
|
oldLogPrint := fs.LogPrint
|
||||||
|
if !log.Redirected() {
|
||||||
|
// Intercept the log calls if not logging to file or syslog
|
||||||
|
fs.LogPrint = func(level fs.LogLevel, text string) {
|
||||||
|
printProgress(fmt.Sprintf("%s %-6s: %s", time.Now().Format(logTimeFormat), level, text))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
progressInterval := defaultProgressInterval
|
||||||
|
if ShowStats() {
|
||||||
|
progressInterval = *statsInterval
|
||||||
|
}
|
||||||
|
ticker := time.NewTicker(progressInterval)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
printProgress("")
|
||||||
|
case <-stopStats:
|
||||||
|
ticker.Stop()
|
||||||
|
fs.LogPrint = oldLogPrint
|
||||||
|
fmt.Println("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return stopStats
|
||||||
|
}
|
||||||
|
|
||||||
|
// VT100 codes
|
||||||
|
const (
|
||||||
|
eraseLine = "\x1b[2K"
|
||||||
|
moveToStartOfLine = "\x1b[0G"
|
||||||
|
moveUp = "\x1b[A"
|
||||||
|
)
|
||||||
|
|
||||||
|
// state for the progress printing
|
||||||
|
var (
|
||||||
|
nlines = 0 // number of lines in the previous stats block
|
||||||
|
progressMu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// printProgress prings the progress with an optional log
|
||||||
|
func printProgress(logMessage string) {
|
||||||
|
progressMu.Lock()
|
||||||
|
defer progressMu.Unlock()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, h, err := terminal.GetSize(int(os.Stdout.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
w, h = 80, 25
|
||||||
|
}
|
||||||
|
_ = h
|
||||||
|
stats := strings.TrimSpace(accounting.Stats.String())
|
||||||
|
logMessage = strings.TrimSpace(logMessage)
|
||||||
|
|
||||||
|
out := func(s string) {
|
||||||
|
buf.WriteString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if logMessage != "" {
|
||||||
|
out("\n")
|
||||||
|
out(moveUp)
|
||||||
|
}
|
||||||
|
// Move to the start of the block we wrote erasing all the previous lines
|
||||||
|
for i := 0; i < nlines-1; i++ {
|
||||||
|
out(eraseLine)
|
||||||
|
out(moveUp)
|
||||||
|
}
|
||||||
|
out(eraseLine)
|
||||||
|
out(moveToStartOfLine)
|
||||||
|
if logMessage != "" {
|
||||||
|
out(eraseLine)
|
||||||
|
out(logMessage + "\n")
|
||||||
|
}
|
||||||
|
fixedLines := strings.Split(stats, "\n")
|
||||||
|
nlines = len(fixedLines)
|
||||||
|
for i, line := range fixedLines {
|
||||||
|
if len(line) > w {
|
||||||
|
line = line[:w]
|
||||||
|
}
|
||||||
|
out(line)
|
||||||
|
if i != nlines-1 {
|
||||||
|
out("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Stdout.Write(buf.Bytes())
|
||||||
|
}
|
|
@ -619,6 +619,21 @@ files if they are incorrect as it would normally.
|
||||||
This can be used if the remote is being synced with another tool also
|
This can be used if the remote is being synced with another tool also
|
||||||
(eg the Google Drive client).
|
(eg the Google Drive client).
|
||||||
|
|
||||||
|
### --P, --progress ###
|
||||||
|
|
||||||
|
This flag makes rclone update the stats in a static block in the
|
||||||
|
terminal providing a realtime overview of the transfer.
|
||||||
|
|
||||||
|
Any log messages will scroll above the static block. Log messages
|
||||||
|
will push the static block down to the bottom of the terminal where it
|
||||||
|
will stay.
|
||||||
|
|
||||||
|
Normally this is updated every 500mS but this period can be overridden
|
||||||
|
with the `--stats` flag.
|
||||||
|
|
||||||
|
This can be used with the `--stats-one-line` flag for a simpler
|
||||||
|
display.
|
||||||
|
|
||||||
### -q, --quiet ###
|
### -q, --quiet ###
|
||||||
|
|
||||||
Normally rclone outputs stats and a completion message. If you set
|
Normally rclone outputs stats and a completion message. If you set
|
||||||
|
|
|
@ -83,6 +83,7 @@ type ConfigInfo struct {
|
||||||
MaxTransfer SizeSuffix
|
MaxTransfer SizeSuffix
|
||||||
MaxBacklog int
|
MaxBacklog int
|
||||||
StatsOneLine bool
|
StatsOneLine bool
|
||||||
|
Progress bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig creates a new config with everything set to the default
|
// NewConfig creates a new config with everything set to the default
|
||||||
|
|
|
@ -85,6 +85,7 @@ func AddFlags(flagSet *pflag.FlagSet) {
|
||||||
flags.FVarP(flagSet, &fs.Config.MaxTransfer, "max-transfer", "", "Maximum size of data to transfer.")
|
flags.FVarP(flagSet, &fs.Config.MaxTransfer, "max-transfer", "", "Maximum size of data to transfer.")
|
||||||
flags.IntVarP(flagSet, &fs.Config.MaxBacklog, "max-backlog", "", fs.Config.MaxBacklog, "Maximum number of objects in sync or check backlog.")
|
flags.IntVarP(flagSet, &fs.Config.MaxBacklog, "max-backlog", "", fs.Config.MaxBacklog, "Maximum number of objects in sync or check backlog.")
|
||||||
flags.BoolVarP(flagSet, &fs.Config.StatsOneLine, "stats-one-line", "", fs.Config.StatsOneLine, "Make the stats fit on one line.")
|
flags.BoolVarP(flagSet, &fs.Config.StatsOneLine, "stats-one-line", "", fs.Config.StatsOneLine, "Make the stats fit on one line.")
|
||||||
|
flags.BoolVarP(flagSet, &fs.Config.Progress, "progress", "P", fs.Config.Progress, "Show progress during transfer.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFlags converts any flags into config which weren't straight foward
|
// SetFlags converts any flags into config which weren't straight foward
|
||||||
|
|
|
@ -88,3 +88,8 @@ func InitLogging() {
|
||||||
startSysLog()
|
startSysLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Redirected returns true if the log has been redirected from stdout
|
||||||
|
func Redirected() bool {
|
||||||
|
return *useSyslog || *logFile != ""
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue