diff --git a/src/octoprint/static/js/app/helpers.js b/src/octoprint/static/js/app/helpers.js
index 40924fe0..bbbc9bf4 100644
--- a/src/octoprint/static/js/app/helpers.js
+++ b/src/octoprint/static/js/app/helpers.js
@@ -387,6 +387,90 @@ function formatFuzzyEstimation(seconds, base) {
return m.fromNow(true);
}
+function formatFuzzyPrintTime(totalSeconds) {
+ if (!totalSeconds || totalSeconds < 0) return "-";
+
+ var d = moment.duration(totalSeconds, "seconds");
+
+ var seconds = d.seconds();
+ var minutes = d.minutes();
+ var hours = d.hours();
+ var days = d.days();
+
+ var replacements = {
+ days: days,
+ hours: hours,
+ minutes: minutes,
+ seconds: seconds,
+ totalSeconds: totalSeconds
+ };
+
+ var text = "-";
+
+ if (days >= 1) {
+ // days
+ if (hours >= 14) {
+ replacements.days += 1;
+ text = gettext("%(days)d days");
+ } else if (hours > 10 && hours < 14) {
+ text = gettext("%(days)d.5 days");
+ } else {
+ if (days == 1) {
+ text = gettext("%(days)d day");
+ } else {
+ text = gettext("%(days)d days");
+ }
+ }
+ } else if (days == 0 && hours >= 1) {
+ // only hours
+ if (hours == 1) {
+ // between 1 and 2 hours, slightly different rules than for other hours
+ if (minutes <= 10) {
+ text = gettext("1 hour");
+ } else if (minutes > 10 && minutes <= 30) {
+ text = gettext("1.5 hours");
+ } else {
+ text = gettext("2 hours");
+ }
+ } else {
+ // over two hours
+ if (minutes < 15) {
+ text = gettext("%(hours)d hours");
+ } else if (minutes >= 15 && minutes < 45) {
+ text = gettext("%(hours)d.5 hours");
+ } else {
+ replacements.hours += 1;
+ text = gettext("%(hours)d hours");
+ }
+ }
+ } else if (days == 0 && hours == 0 && minutes >= 1) {
+ // only minutes
+ if (minutes < 2) {
+ text = gettext("a minute");
+ } else if (minutes < 30) {
+ if (seconds > 30) {
+ replacements.minutes += 1;
+ }
+ text = gettext("%(minutes)d minutes");
+ } else if (minutes <= 40) {
+ text = gettext("40 minutes");
+ } else if (minutes <= 50) {
+ text = gettext("50 minutes");
+ } else {
+ text = gettext("1 hour");
+ }
+ } else {
+ // only seconds
+ if (seconds < 30) {
+ text = gettext("a couple of seconds");
+ } else {
+ text = gettext("less than a minute");
+ }
+ }
+
+ return _.sprintf(text, replacements);
+}
+
function formatDate(unixTimestamp) {
if (!unixTimestamp) return "-";
return moment.unix(unixTimestamp).format(gettext(/* L10N: Date format */ "YYYY-MM-DD HH:mm"));
diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js
index ef4ffe4b..829a4fd1 100644
--- a/src/octoprint/static/js/app/viewmodels/files.js
+++ b/src/octoprint/static/js/app/viewmodels/files.js
@@ -349,7 +349,7 @@ $(function() {
}
}
}
- output += gettext("Estimated Print Time") + ": " + formatDuration(data["gcodeAnalysis"]["estimatedPrintTime"]) + "
";
+ output += gettext("Estimated Print Time") + ": " + formatFuzzyPrintTime(data["gcodeAnalysis"]["estimatedPrintTime"]) + "
";
}
if (data["prints"] && data["prints"]["last"]) {
output += gettext("Last Printed") + ": " + formatTimeAgo(data["prints"]["last"]["date"]) + "
";
diff --git a/src/octoprint/static/js/app/viewmodels/printerstate.js b/src/octoprint/static/js/app/viewmodels/printerstate.js
index 8076c7ae..e4ff49fa 100644
--- a/src/octoprint/static/js/app/viewmodels/printerstate.js
+++ b/src/octoprint/static/js/app/viewmodels/printerstate.js
@@ -52,9 +52,9 @@ $(function() {
self.estimatedPrintTimeString = ko.pureComputed(function() {
if (self.lastPrintTime())
- return formatFuzzyEstimation(self.lastPrintTime());
+ return formatFuzzyPrintTime(self.lastPrintTime());
if (self.estimatedPrintTime())
- return formatFuzzyEstimation(self.estimatedPrintTime());
+ return formatFuzzyPrintTime(self.estimatedPrintTime());
return "-";
});
self.byteString = ko.pureComputed(function() {
@@ -78,17 +78,17 @@ $(function() {
if (!self.printTime() || !(self.isPrinting() || self.isPaused())) {
return "-";
} else {
- return gettext("Calculating...");
+ return gettext("Still stabilizing...");
}
} else {
- return formatFuzzyEstimation(self.printTimeLeft());
+ return formatFuzzyPrintTime(self.printTimeLeft());
}
});
self.printTimeLeftOriginString = ko.pureComputed(function() {
var value = self.printTimeLeftOrigin();
switch (value) {
case "linear": {
- return gettext("Based on a linear approximation (accuracy highly dependent on the model)");
+ return gettext("Based on a linear approximation (very low accuracy, especially at the beginning of the print)");
}
case "analysis": {
return gettext("Based on the estimate from analysis of file (medium accuracy)");