Base the progress bar on file counts
With --link-dest unchanged files are linked, not copied, so rsync's byte percentage stayed near zero and the ETA was hours. Use the checked/total file counts instead, and show no percentage or ETA while still scanning.
This commit is contained in:
@@ -75,7 +75,7 @@ R = TypeVar('R')
|
||||
SSH_TIMEOUT = 15
|
||||
|
||||
# An rsync --info=progress2 update: size, percent, speed, time, then optionally (xfr#N, ir-chk/to-chk=N/N)
|
||||
PROGRESS_RE = re.compile(r'\s*(\S+)\s+(\d+)%\s+(\S+)\s+\d+:\d\d:\d\d(?:\s+\(xfr#\d+, (ir|to)-chk=\d+/\d+\))?\s*')
|
||||
PROGRESS_RE = re.compile(r'\s*(\S+)\s+\d+%\s+\S+\s+\d+:\d\d:\d\d(?:\s+\(xfr#\d+, (ir|to)-chk=(\d+)/(\d+)\))?\s*')
|
||||
PROGRESS_BAR_WIDTH = 30
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ class _ProgressBar:
|
||||
self._start = default_timer()
|
||||
# rsync reports ir-chk while incremental recursion is still finding files
|
||||
self._scanning = True
|
||||
self._checked = 0
|
||||
self._total = 0
|
||||
self._shown = False
|
||||
|
||||
def update(self, record: str) -> bool:
|
||||
@@ -121,25 +123,30 @@ class _ProgressBar:
|
||||
if m is None:
|
||||
return False
|
||||
|
||||
size, percent, speed, chk = m.group(1), int(m.group(2)), m.group(3), m.group(4)
|
||||
size, chk = m.group(1), m.group(2)
|
||||
|
||||
# Records in the middle of a file have no counts, so keep the last ones
|
||||
if chk is not None:
|
||||
self._scanning = chk == 'ir'
|
||||
self._total = int(m.group(4))
|
||||
self._checked = self._total - int(m.group(3))
|
||||
|
||||
# rsync's own time field is the elapsed time on records ending a file, so estimate the ETA here
|
||||
if 0 < percent < 100:
|
||||
eta = int((default_timer() - self._start) * (100 - percent) / percent)
|
||||
text = f'{percent:3d}% {size} {speed} ETA {eta // 3600}:{eta // 60 % 60:02d}:{eta % 60:02d}'
|
||||
# Count files, not bytes: with --link-dest unchanged files are linked, and rsync's percentage leaves them out
|
||||
if self._scanning or self._total == 0:
|
||||
text = f'{self._checked:,} files checked {size} copied (scanning)'
|
||||
percent = None
|
||||
else:
|
||||
text = f'{percent:3d}% {size} {speed}'
|
||||
percent = self._checked * 100 // self._total
|
||||
text = f'{percent:3d}% {self._checked:,}/{self._total:,} files {size} copied'
|
||||
|
||||
if self._scanning:
|
||||
text += ' (scanning)'
|
||||
if 0 < percent < 100:
|
||||
eta = int((default_timer() - self._start) * (self._total - self._checked) / self._checked)
|
||||
text += f' ETA {eta // 3600}:{eta // 60 % 60:02d}:{eta % 60:02d}'
|
||||
|
||||
width = get_terminal_size().columns - 1
|
||||
|
||||
# Fixed width, so the bar doesn't jump when the text changes length
|
||||
if len(text) + PROGRESS_BAR_WIDTH + 4 <= width:
|
||||
# Fixed width, so the bar doesn't jump when the text changes length; no bar until the total is known
|
||||
if percent is not None and len(text) + PROGRESS_BAR_WIDTH + 4 <= width:
|
||||
filled = PROGRESS_BAR_WIDTH * percent // 100
|
||||
text = f'[{"#" * filled}{"-" * (PROGRESS_BAR_WIDTH - filled)}] {text}'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user