diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 4970021e..97a7e30c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -10,13 +10,14 @@ This is a bug and feature tracker, please only use it to report bugs or request features within OctoPrint (not OctoPi, not any OctoPrint plugins and not unofficial OctoPrint versions). -Do not seek support here ("I need help with ..."), that belongs on -the mailing list or the G+ community (both linked in the "guidelines -for contributing" linked above, read it!), NOT here. +Do not seek support here ("I need help with ...", "I have a +question ..."), that belongs on the mailing list or the G+ community +(both linked in the "guidelines for contributing" linked above, read +them!), NOT here. -Mark requests with a "[Request]" prefix in the title please. Fully fill -out the bug reporting template for bug reports (if you don't know where -to find some information - it's all described in the Contribution +Mark requests with a "[Request]" prefix in the title please. For bug +reports fully fill out the bug reporting template (if you don't know +where to find some information - it's all described in the Contribution Guidelines linked up there in the big yellow box). When reporting a bug do NOT delete ANY lines from the template but @@ -91,7 +92,7 @@ reporting communication issues. Never truncate. serial.log is usually not written due to performance reasons and must be enabled explicitly. Provide at the very least the FULL contents of your terminal tab at the time of the bug occurrence, even if you do not have -a serial.log.] +a serial.log (which the Contribution Guidelines tell you where to find).] #### Link to contents of Javascript console in the browser diff --git a/.versioneer-lookup b/.versioneer-lookup index 342b9dec..9585f8ad 100644 --- a/.versioneer-lookup +++ b/.versioneer-lookup @@ -16,11 +16,14 @@ prerelease HEAD \(detached.* -# maintenance is currently the branch for preparation of maintenance release 1.3.3 +# maintenance is currently the branch for preparation of maintenance release 1.3.4 # so are any fix/... and improve/... branches -maintenance 1.3.3 0a69dbeddb301d5a32827a3f0d561f875df24234 pep440-dev -fix/.* 1.3.3 0a69dbeddb301d5a32827a3f0d561f875df24234 pep440-dev -improve/.* 1.3.3 0a69dbeddb301d5a32827a3f0d561f875df24234 pep440-dev +maintenance 1.3.4 3fbd477d15b5776ca929ea578c5437720aaf7f31 pep440-dev +fix/.* 1.3.4 3fbd477d15b5776ca929ea578c5437720aaf7f31 pep440-dev +improve/.* 1.3.4 3fbd477d15b5776ca929ea578c5437720aaf7f31 pep440-dev + +# staging/maintenance is currently the branch for preparation of 1.3.3rc3 (if we'll need that) +staging/maintenance 1.3.3rc2 3fbd477d15b5776ca929ea578c5437720aaf7f31 pep440-dev # every other branch is a development branch and thus gets resolved to 1.4.0-dev for now .* 1.4.0 7f5d03d0549bcbd26f40e7e4a3297ea5204fb1cc pep440-dev diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b37bf0..613f1702 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,21 @@ # OctoPrint Changelog +## 1.3.3rc2 (2017-05-17) + +### Bug fixes + + * [#1917](https://github.com/foosel/OctoPrint/issues/1917) (regression) - Fix job data resetting on print job completion. + * [#1918](https://github.com/foosel/OctoPrint/issues/1918) (regression) - Fix "save as default" checkbox not being disabled when other controls are disabled. + * [#1919](https://github.com/foosel/OctoPrint/issues/1919) (regression) - Fix call to no longer existing function in Plugin Manager UI. + +([Commits](https://github.com/foosel/OctoPrint/compare/1.3.3rc1...1.3.3rc2)) + ## 1.3.3rc1 (2017-05-11) ### Improvements - * [#478](https://github.com/foosel/OctoPrint/issues/478) - Made webcam stream contained fixed height (with selectable aspect ratio) to prevent jumps of the controls beneath it on load. - * [#748](https://github.com/foosel/OctoPrint/issues/748) - Added delete confirmation and bulk delete for timelapses. See also the discussion in brainstorming ticket [#1807].(https://github.com/foosel/OctoPrint/issues/1807). + * [#478](https://github.com/foosel/OctoPrint/issues/478) - Made webcam stream container fixed height (with selectable aspect ratio) to prevent jumps of the controls beneath it on load. + * [#748](https://github.com/foosel/OctoPrint/issues/748) - Added delete confirmation and bulk delete for timelapses. See also the discussion in brainstorming ticket [#1807](https://github.com/foosel/OctoPrint/issues/1807). * [#1092](https://github.com/foosel/OctoPrint/issues/1092) - Added new event to the file manager: `FileAdded`, `FileRemoved`, `FolderAdded`, `FolderRemoved`. Contrary to the `Upload` event, `FileAdded` will always fire when a file was added to storage through the file manager, not only when added through the web interface. Extended documentation accordingly. * [#1521](https://github.com/foosel/OctoPrint/issues/1521) - Software update plugin: Display timestamp of last version cache refresh in "Advanced options" area. * [#1734](https://github.com/foosel/OctoPrint/issues/1734) - Treat default/initial printer profile like all other printer profiles, persisting it to disk instead of `config.yaml` and allowing deletion. OctoPrint will migrate the existing default profile to the new location on first start. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3fb3411..b8ba81c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -365,10 +365,16 @@ There are three main branches in OctoPrint: the `maintenance` branch and are now being pushed on the "Maintenance" pre release channel for further testing. Version number follows the scheme `..rc` (e.g. `1.2.9rc1`). + * `staging/maintenance`: Any preparation for potential follow-up RCs takes place here. + Version number follows the scheme `..rc.dev` (e.g. + `1.2.9rc1.dev3`) for a current Maintenance RC of `..rc`. * `rc/devel`: This branch is reserved for future releases that have graduated from the `devel` branch and are now being pushed on the "Devel" pre release channel for further testing. Version number follows the scheme `..0rc` (e.g. `1.3.0rc1`) for a current stable OctoPrint version of `..`. + * `staging/devel`: Any preparation for potential follow-up Devel RCs takes place + here. Version number follows the scheme `..0rc.dev` (e.g. + `1.3.0rc1.dev12`) for a current Devel RC of `..0rc`. Additionally, from time to time you might see other branches pop up in the repository. Those usually have one of the following prefixes: diff --git a/SUPPORTERS.md b/SUPPORTERS.md index 50a82c76..393d83e9 100644 --- a/SUPPORTERS.md +++ b/SUPPORTERS.md @@ -64,4 +64,4 @@ thanks to everyone who contributed! * Timeshell.ca * Trent Shumay -and 1076 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file +and 1084 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file diff --git a/src/octoprint/plugins/announcements/static/js/announcements.js b/src/octoprint/plugins/announcements/static/js/announcements.js index 85d69c60..497828e8 100644 --- a/src/octoprint/plugins/announcements/static/js/announcements.js +++ b/src/octoprint/plugins/announcements/static/js/announcements.js @@ -258,22 +258,22 @@ $(function() { buttons: [{ text: gettext("Later"), click: function(notice) { - self.hiddenChannels.push(key); notice.remove(); + self.hiddenChannels.push(key); } }, { text: gettext("Mark read"), click: function(notice) { - self.markRead(key, value.last); notice.remove(); + self.markRead(key, value.last); } }, { text: gettext("Read..."), addClass: "btn-primary", click: function(notice) { + notice.remove(); self.showAnnouncementDialog(key); self.markRead(key, value.last); - notice.remove(); } }] }, diff --git a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js index 0da68a47..3aef0338 100644 --- a/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js +++ b/src/octoprint/plugins/pluginmanager/static/js/pluginmanager.js @@ -526,13 +526,11 @@ $(function() { if (reinstall) { OctoPrint.plugins.pluginmanager.reinstall(reinstall, url, followDependencyLinks) .done(onSuccess) - .fail(onError) - .always(onAlways); + .fail(onError); } else { OctoPrint.plugins.pluginmanager.install(url, followDependencyLinks) .done(onSuccess) - .fail(onError) - .always(onAlways); + .fail(onError); } }; @@ -687,17 +685,21 @@ $(function() { hide: false }; + var restartClicked = false; if (self.restartCommandSpec) { options.confirm = { confirm: true, buttons: [{ text: gettext("Restart now"), - click: function () { + click: function (notice) { + if (restartClicked) return; + restartClicked = true; showConfirmationDialog({ message: gettext("This will restart your OctoPrint server."), onproceed: function() { OctoPrint.system.executeCommand("core", "restart") .done(function() { + notice.remove(); new PNotify({ title: gettext("Restart in progress"), text: gettext("The server is now being restarted in the background") @@ -709,6 +711,9 @@ $(function() { text: gettext("Trying to restart the server produced an error, please check octoprint.log for details. You'll have to restart manually.") }) }); + }, + onclose: function() { + restartClicked = false; } }); } @@ -718,6 +723,7 @@ $(function() { notification = PNotify.singleButtonNotify(options); } else if (response.needs_refresh) { + var refreshClicked = false; notification = PNotify.singleButtonNotify({ title: titleSuccess, text: textReload, @@ -726,6 +732,8 @@ $(function() { buttons: [{ text: gettext("Reload now"), click: function () { + if (refreshClicked) return; + refreshClicked = true; location.reload(true); } }] diff --git a/src/octoprint/plugins/softwareupdate/__init__.py b/src/octoprint/plugins/softwareupdate/__init__.py index 507c5593..ee0df3da 100644 --- a/src/octoprint/plugins/softwareupdate/__init__.py +++ b/src/octoprint/plugins/softwareupdate/__init__.py @@ -868,7 +868,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, self._logger.info("Restarting...") try: - util.execute(restart_command) + util.execute(restart_command, evaluate_returncode=False, async=True) except exceptions.ScriptError as e: self._logger.exception("Error while restarting via command {}".format(restart_command)) self._logger.warn("Restart stdout:\n{}".format(e.stdout)) diff --git a/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js b/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js index 702edc3f..30c5585a 100644 --- a/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js +++ b/src/octoprint/plugins/softwareupdate/static/js/softwareupdate.js @@ -123,8 +123,9 @@ $(function() { self.config_updateMethod = ko.observable(); self.config_releaseChannel = ko.observable(); - self.configurationDialog = $("#settings_plugin_softwareupdate_configurationdialog"); - self.confirmationDialog = $("#softwareupdate_confirmation_dialog"); + self.configurationDialog = undefined; + self.confirmationDialog = undefined; + self._updateClicked = false; self.config_availableCheckTypes = ko.observableArray([]); self.config_availableReleaseChannels = ko.observableArray([]); @@ -357,7 +358,11 @@ $(function() { }, { text: gettext("Update now"), addClass: "btn-primary", - click: self.update + click: function() { + if (self._updateClicked) return; + self._updateClicked = true; + self.update(); + } }] }; options["buttons"] = { @@ -501,8 +506,14 @@ $(function() { }; self.update = function(force) { - if (self.updateInProgress) return; - if (!self.loginState.isAdmin()) return; + if (self.updateInProgress) { + self._updateClicked = false; + return; + } + if (!self.loginState.isAdmin()) { + self._updateClicked = false; + return; + } if (self.printerState.isPrinting()) { self._showPopup({ @@ -510,6 +521,7 @@ $(function() { text: gettext("A print job is currently in progress. Updating will be prevented until it is done."), type: "error" }); + self._updateClicked = false; } else { self.forceUpdate = (force == true); self.confirmationDialog.modal("show"); @@ -518,9 +530,13 @@ $(function() { }; self.confirmUpdate = function() { - self.confirmationDialog.modal("hide"); self.performUpdate(self.forceUpdate, _.map(self.availableAndPossible(), function(info) { return info.key })); + self.confirmationDialog.modal("hide"); + }; + + self.confirmationHidden = function() { + self._updateClicked = false; }; self._showWorkingDialog = function(title) { @@ -578,6 +594,10 @@ $(function() { self.onStartup = function() { self.workingDialog = $("#settings_plugin_softwareupdate_workingdialog"); self.workingOutput = $("#settings_plugin_softwareupdate_workingdialog_output"); + self.configurationDialog = $("#settings_plugin_softwareupdate_configurationdialog"); + self.confirmationDialog = $("#softwareupdate_confirmation_dialog"); + + self.confirmationDialog.on("hidden", self.confirmationHidden); }; self.onServerDisconnect = function() { diff --git a/src/octoprint/plugins/softwareupdate/util.py b/src/octoprint/plugins/softwareupdate/util.py index 27335182..020fce3d 100644 --- a/src/octoprint/plugins/softwareupdate/util.py +++ b/src/octoprint/plugins/softwareupdate/util.py @@ -9,12 +9,12 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms from .exceptions import ScriptError -def execute(command, cwd=None, evaluate_returncode=True): +def execute(command, cwd=None, evaluate_returncode=True, async=False): import sarge p = None try: - p = sarge.run(command, cwd=cwd, stdout=sarge.Capture(), stderr=sarge.Capture()) + p = sarge.run(command, cwd=cwd, stdout=sarge.Capture(), stderr=sarge.Capture(), async=async) except: returncode = p.returncode if p is not None else None stdout = p.stdout.text if p is not None and p.stdout is not None else "" diff --git a/src/octoprint/printer/__init__.py b/src/octoprint/printer/__init__.py index e26a80e4..8e4b1e3c 100644 --- a/src/octoprint/printer/__init__.py +++ b/src/octoprint/printer/__init__.py @@ -299,6 +299,7 @@ class PrinterInterface(object): after the file is selected. Raises: + InvalidFileType: if the file is not a machinecode file and hence cannot be printed InvalidFileLocation: if an absolute path was provided and not contained within local storage or doesn't exist """ @@ -593,3 +594,6 @@ class UnknownScript(Exception): class InvalidFileLocation(Exception): pass + +class InvalidFileType(Exception): + pass diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 7c3ba32e..45c309c7 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -17,9 +17,9 @@ import time from octoprint import util as util from octoprint.events import eventManager, Events -from octoprint.filemanager import FileDestinations, NoSuchStorage +from octoprint.filemanager import FileDestinations, NoSuchStorage, valid_file_type from octoprint.plugin import plugin_manager, ProgressPlugin -from octoprint.printer import PrinterInterface, PrinterCallback, UnknownScript, InvalidFileLocation +from octoprint.printer import PrinterInterface, PrinterCallback, UnknownScript, InvalidFileLocation, InvalidFileType from octoprint.printer.estimation import TimeEstimationHelper from octoprint.settings import settings from octoprint.util import comm as comm @@ -846,6 +846,9 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self._stateMonitor.add_temperature(data) def _validateJob(self, filename, sd): + if not valid_file_type(filename, type="machinecode"): + raise InvalidFileType("{} is not a machinecode file, cannot print".format(filename)) + if sd: return @@ -1070,13 +1073,17 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): must_be_set=False) def on_comm_print_job_done(self): - self._updateProgressData() - self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) self._fileManager.delete_recovery_data() payload = self._payload_for_print_job_event() if payload: payload["time"] = self._comm.getPrintTime() + self._updateProgressData(completion=1.0, + filepos=payload["size"], + printTime=payload["time"], + printTimeLeft=0) + self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + eventManager().fire(Events.PRINT_DONE, payload) self.script("afterPrintDone", context=dict(event=payload), @@ -1088,6 +1095,10 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): payload["time"], True, self._printerProfileManager.get_current_or_default()["id"]) + else: + self._updateProgressData() + self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + def on_comm_print_job_failed(self): payload = self._payload_for_print_job_event() @@ -1134,7 +1145,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self._sdStreaming = True self._setJobData(filename, filesize, True) - self._updateProgressData() + self._updateProgressData(completion=0.0, filepos=0, printTime=0) self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) def on_comm_file_transfer_done(self, filename): @@ -1162,6 +1173,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self._logger.exception("Error while trying to persist print recovery data") def _payload_for_print_job_event(self, location=None, print_job_file=None, position=None): + print_job_size = None if print_job_file is None: with self._selectedFileMutex: selected_file = self._selectedFile @@ -1169,9 +1181,10 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): return dict() print_job_file = selected_file.get("filename", None) + print_job_size = selected_file.get("filesize", None) location = FileDestinations.SDCARD if selected_file.get("sd", False) else FileDestinations.LOCAL - if not print_job_file or not location: + if not print_job_file or not print_job_size or not location: return dict() if location == FileDestinations.SDCARD: @@ -1190,6 +1203,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): result= dict(name=name, path=path, origin=origin, + size=print_job_size, # TODO deprecated, remove in 1.4.0 file=full_path, diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index e847885a..cbad25af 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -550,7 +550,7 @@ class Server(object): "on_shutdown", sorting_context="ShutdownPlugin.on_shutdown") - # wait for shutdown even to be processed, but maximally for 15s + # wait for shutdown event to be processed, but maximally for 15s event_timeout = 15.0 if eventManager.join(timeout=event_timeout): self._logger.warn("Event loop was still busy processing after {}s, shutting down anyhow".format(event_timeout)) @@ -565,13 +565,16 @@ class Server(object): def sigterm_handler(*args, **kwargs): # will stop tornado on SIGTERM, making the program exit cleanly def shutdown_tornado(): + self._logger.debug("Shutting down tornado's IOLoop...") ioloop.stop() + self._logger.debug("SIGTERM received...") ioloop.add_callback_from_signal(shutdown_tornado) signal.signal(signal.SIGTERM, sigterm_handler) try: # this is the main loop - as long as tornado is running, OctoPrint is running ioloop.start() + self._logger.debug("Tornado's IOLoop stopped") except (KeyboardInterrupt, SystemExit): pass except: diff --git a/src/octoprint/static/js/app/helpers.js b/src/octoprint/static/js/app/helpers.js index 98a24d5b..744ce15b 100644 --- a/src/octoprint/static/js/app/helpers.js +++ b/src/octoprint/static/js/app/helpers.js @@ -646,6 +646,7 @@ function showConfirmationDialog(msg, onacknowledge, options) { var proceed = options.proceed || gettext("Proceed"); var proceedClass = options.proceedClass || "danger"; var onproceed = options.onproceed || undefined; + var onclose = options.onclose || undefined; var dialogClass = options.dialogClass || ""; var modalHeader = $('

' + title + '

'); @@ -663,14 +664,19 @@ function showConfirmationDialog(msg, onacknowledge, options) { .append($('
').addClass('modal-header').append(modalHeader)) .append($('
').addClass('modal-body').append(modalBody)) .append($('
').addClass('modal-footer').append(cancelButton).append(proceedButton)); + modal.on('hidden', function(event) { + if (onclose && _.isFunction(onclose)) { + onclose(event); + } + }); modal.modal("show"); proceedButton.click(function(e) { e.preventDefault(); - modal.modal("hide"); if (onproceed && _.isFunction(onproceed)) { onproceed(e); } + modal.modal("hide"); }); return modal; diff --git a/src/octoprint/static/js/app/viewmodels/control.js b/src/octoprint/static/js/app/viewmodels/control.js index 544eeeed..ecf86eb7 100644 --- a/src/octoprint/static/js/app/viewmodels/control.js +++ b/src/octoprint/static/js/app/viewmodels/control.js @@ -442,6 +442,7 @@ $(function() { self.additionalControls = additionalControls; self.rerenderControls(); } + self._enableWebcam(); }; self.onFocus = function (data, event) { diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 5da2cd3a..6b94f997 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -841,13 +841,14 @@ $(function() { function evaluateDropzones() { var enableLocal = self.loginState.isUser(); - var enableSd = enableLocal && CONFIG_SD_SUPPORT && self.printerState.isSdReady(); + var enableSd = enableLocal && CONFIG_SD_SUPPORT && self.printerState.isSdReady() && !self.isPrinting(); self._setDropzone("local", enableLocal); self._setDropzone("sdcard", enableSd); } self.loginState.isUser.subscribe(evaluateDropzones); self.printerState.isSdReady.subscribe(evaluateDropzones); + self.isPrinting.subscribe(evaluateDropzones); evaluateDropzones(); self.requestData(); @@ -1106,7 +1107,7 @@ $(function() { if (foundLocal) { self.dropZoneLocalBackground.addClass("hover"); self.dropZoneSdBackground.removeClass("hover"); - } else if (foundSd && self.printerState.isSdReady()) { + } else if (foundSd && self.printerState.isSdReady() && !self.isPrinting()) { self.dropZoneSdBackground.addClass("hover"); self.dropZoneLocalBackground.removeClass("hover"); } else if (found) { diff --git a/src/octoprint/static/js/app/viewmodels/timelapse.js b/src/octoprint/static/js/app/viewmodels/timelapse.js index f6a96c68..ed26a414 100644 --- a/src/octoprint/static/js/app/viewmodels/timelapse.js +++ b/src/octoprint/static/js/app/viewmodels/timelapse.js @@ -48,7 +48,7 @@ $(function() { return ("timed" == self.timelapseType()); }); self.saveButtonEnabled = ko.pureComputed(function() { - return self.isDirty() && self.isOperational() && !self.isPrinting() && self.loginState.isUser(); + return self.isDirty() && !self.isPrinting() && self.loginState.isUser(); }); self.isOperational.subscribe(function() { diff --git a/src/octoprint/templates/overlays/dragndrop.jinja2 b/src/octoprint/templates/overlays/dragndrop.jinja2 index 57f8661e..2e0e21de 100644 --- a/src/octoprint/templates/overlays/dragndrop.jinja2 +++ b/src/octoprint/templates/overlays/dragndrop.jinja2 @@ -4,7 +4,7 @@ {% if enableSdSupport %}

{{ _('Upload locally') }}
-

{{ _('Upload to SD') }}
({{ _('SD not initialized') }})
+

{{ _('Upload to SD') }}
({{ _('Cannot upload to printer\'s SD while printing') }})({{ _('SD not initialized') }})
{% else %}

{{ _('Upload') }}
diff --git a/src/octoprint/templates/sidebar/files.jinja2 b/src/octoprint/templates/sidebar/files.jinja2 index 730019c9..e7b9eec4 100644 --- a/src/octoprint/templates/sidebar/files.jinja2 +++ b/src/octoprint/templates/sidebar/files.jinja2 @@ -55,18 +55,18 @@
{% if enableSdSupport %} - + {{ _('Upload') }} - + {{ _('Upload to SD') }} - + {% else %} - + {{ _('Upload') }} diff --git a/src/octoprint/templates/tabs/timelapse.jinja2 b/src/octoprint/templates/tabs/timelapse.jinja2 index 7890d7f0..06724625 100644 --- a/src/octoprint/templates/tabs/timelapse.jinja2 +++ b/src/octoprint/templates/tabs/timelapse.jinja2 @@ -5,7 +5,7 @@

{{ _('Timelapse Configuration') }}

- @@ -16,25 +16,25 @@
- + {{ _('sec') }}
- + {{ _('fps') }}
- + {{ _('sec') }}
@@ -42,7 +42,7 @@
- + {{ _('mm') }}
{{ _('Enter the retraction z-hop used in the firmware or the gcode file to trigger snapshots for the timelapse only if a real layer change happens. For this to work properly your retraction z-hop has to be different from your layerheight!') }} @@ -50,7 +50,7 @@