feat: add backup progress estimation with time remaining

- Add estimate_backup_size() using du -sb before rsync starts
- Parse transfer speed from rsync output (KB/s, MB/s, GB/s)
- Calculate remaining time based on speed and remaining bytes
- Show estimated time remaining and size progress in UI
- Add format_time_remaining() for human-readable time display
- New UI elements: backup_time_label_ and backup_size_label_
This commit is contained in:
x3
2026-09-07 19:29:29 +00:00
parent 33044731c8
commit f95a634655
4 changed files with 122 additions and 6 deletions
+42 -2
View File
@@ -46,13 +46,32 @@ MainWindow::MainWindow(Config& config)
/* Update backup progress window */
if (self->backup_win_) {
if (p.bytes_total > 0) {
double frac = (double)p.bytes_transferred / p.bytes_total;
if (p.estimated_total_size > 0) {
double frac = (p.estimated_total_size > 0) ? (double)p.bytes_transferred / p.estimated_total_size : 0;
if (frac > 1.0) frac = 1.0;
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(self->backup_progress_), frac);
char pct[32];
snprintf(pct, sizeof(pct), "%.0f%%", frac * 100);
gtk_label_set_text(GTK_LABEL(self->backup_status_label_), pct);
char size_info[128];
snprintf(size_info, sizeof(size_info), "%s / %s",
format_size(p.bytes_transferred).c_str(),
format_size(p.estimated_total_size).c_str());
gtk_label_set_text(GTK_LABEL(self->backup_size_label_), size_info);
}
if (p.estimated_remaining > 0 && p.estimated_remaining < 86400 * 365) {
std::string time_str = self->rsync_.format_time_remaining(p.estimated_remaining);
gtk_label_set_text(GTK_LABEL(self->backup_time_label_), time_str.c_str());
} else if (p.elapsed_seconds > 0) {
char elapsed_buf[64];
int mins = (int)(p.elapsed_seconds / 60);
int secs = (int)(p.elapsed_seconds) % 60;
snprintf(elapsed_buf, sizeof(elapsed_buf), "Elapsed: %dm %02ds", mins, secs);
gtk_label_set_text(GTK_LABEL(self->backup_time_label_), elapsed_buf);
}
if (!p.log_line.empty()) {
const char* tag = nullptr;
if (p.log_line.find("[error]") != std::string::npos) tag = "error";
@@ -65,6 +84,7 @@ MainWindow::MainWindow(Config& config)
gtk_label_set_text(GTK_LABEL(self->backup_cmd_label_), cmd_display.c_str());
}
else if (p.log_line.find("[restore") != std::string::npos) tag = "info";
else if (p.log_line.find("[info]") != std::string::npos) tag = "info";
self->append_backup_line(p.log_line, tag);
}
if (p.finished) {
@@ -682,6 +702,8 @@ void MainWindow::open_backup_window(const std::string& description) {
gtk_text_buffer_set_text(buf, "", -1);
gtk_label_set_text(GTK_LABEL(backup_cmd_label_), "");
gtk_label_set_text(GTK_LABEL(backup_status_label_), "Running...");
gtk_label_set_text(GTK_LABEL(backup_time_label_), "");
gtk_label_set_text(GTK_LABEL(backup_size_label_), "");
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(backup_progress_), 0.0);
gtk_button_set_label(GTK_BUTTON(backup_cancel_btn_), "Cancel");
gtk_widget_show_all(backup_win_);
@@ -768,6 +790,22 @@ void MainWindow::open_backup_window(const std::string& description) {
gtk_box_pack_start(GTK_BOX(prog_box), backup_status_label_, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), prog_box, FALSE, FALSE, 0);
/* Time and size info */
GtkWidget* info_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16);
backup_time_label_ = gtk_label_new("");
gtk_widget_set_halign(backup_time_label_, GTK_ALIGN_START);
PangoAttrList* info_attrs = pango_attr_list_new();
pango_attr_list_insert(info_attrs, pango_attr_family_new("monospace"));
gtk_label_set_attributes(GTK_LABEL(backup_time_label_), info_attrs);
gtk_box_pack_start(GTK_BOX(info_box), backup_time_label_, TRUE, TRUE, 0);
backup_size_label_ = gtk_label_new("");
gtk_widget_set_halign(backup_size_label_, GTK_ALIGN_END);
gtk_label_set_attributes(GTK_LABEL(backup_size_label_), info_attrs);
pango_attr_list_unref(info_attrs);
gtk_box_pack_start(GTK_BOX(info_box), backup_size_label_, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), info_box, FALSE, FALSE, 0);
/* Buttons */
GtkWidget* btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
gtk_widget_set_halign(btn_box, GTK_ALIGN_END);
@@ -809,6 +847,8 @@ void MainWindow::close_backup_window() {
backup_cmd_label_ = nullptr;
backup_progress_ = nullptr;
backup_status_label_ = nullptr;
backup_time_label_ = nullptr;
backup_size_label_ = nullptr;
backup_cancel_btn_ = nullptr;
}
}