Add option to repeat last captured frame instead of capturing new frames for the end of a timelapse. Defaults to current behavior (capturing new frames for the time after). Implements foosel/OctoPrint#1422
This commit is contained in:
parent
b38d1810dc
commit
7e2382fed4
5 changed files with 67 additions and 9 deletions
|
|
@ -66,7 +66,8 @@ def getTimelapseData():
|
|||
config = dict(type="timed",
|
||||
postRoll=timelapse.post_roll,
|
||||
fps=timelapse.fps,
|
||||
interval=timelapse.interval)
|
||||
interval=timelapse.interval,
|
||||
capturePostRoll=timelapse.capture_post_roll)
|
||||
else:
|
||||
config = dict(type="off")
|
||||
|
||||
|
|
@ -204,6 +205,16 @@ def setTimelapseConfig():
|
|||
else:
|
||||
return make_response("Invalid value for interval: %d" % interval)
|
||||
|
||||
if "capturePostRoll" in data:
|
||||
config["options"]["capturePostRoll"] = True
|
||||
try:
|
||||
capturePostRoll = bool(data["capturePostRoll"])
|
||||
except ValueError:
|
||||
return make_response("Invalid value for capturePostRoll: %r" % data["capturePostRoll"])
|
||||
else:
|
||||
config["options"]["capturePostRoll"] = capturePostRoll
|
||||
|
||||
|
||||
if "retractionZHop" in request.values:
|
||||
config["options"] = {
|
||||
"retractionZHop": 0
|
||||
|
|
@ -219,6 +230,7 @@ def setTimelapseConfig():
|
|||
else:
|
||||
return make_response("Invalid value for retraction Z-Hop: %d" % retractionZHop)
|
||||
|
||||
|
||||
if admin_permission.can() and "save" in data and data["save"] in valid_boolean_trues:
|
||||
octoprint.timelapse.configure_timelapse(config, True)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -166,7 +166,8 @@ default_settings = {
|
|||
"type": "off",
|
||||
"options": {},
|
||||
"postRoll": 0,
|
||||
"fps": 25
|
||||
"fps": 25,
|
||||
"capturePostRoll": True
|
||||
},
|
||||
"cleanTmpAfterDays": 7
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,12 +10,14 @@ $(function() {
|
|||
self.defaultPostRoll = 0;
|
||||
self.defaultInterval = 10;
|
||||
self.defaultRetractionZHop = 0;
|
||||
self.defaultCapturePostroll = true;
|
||||
|
||||
self.timelapseType = ko.observable(undefined);
|
||||
self.timelapseTimedInterval = ko.observable(self.defaultInterval);
|
||||
self.timelapsePostRoll = ko.observable(self.defaultPostRoll);
|
||||
self.timelapseFps = ko.observable(self.defaultFps);
|
||||
self.timelapseRetractionZHop = ko.observable(self.defaultRetractionZHop);
|
||||
self.timelapseCapturePostRoll = ko.observable(self.defaultCapturePostRoll);
|
||||
|
||||
self.persist = ko.observable(false);
|
||||
self.isDirty = ko.observable(false);
|
||||
|
|
@ -61,6 +63,9 @@ $(function() {
|
|||
self.timelapseRetractionZHop.subscribe(function(newValue) {
|
||||
self.isDirty(true);
|
||||
});
|
||||
self.timelapseCapturePostRoll.subscribe(function() {
|
||||
self.isDirty(true);
|
||||
});
|
||||
|
||||
// initialize list helper
|
||||
self.listHelper = new ItemListHelper(
|
||||
|
|
@ -167,6 +172,12 @@ $(function() {
|
|||
self.timelapseFps(self.defaultFps);
|
||||
}
|
||||
|
||||
if (config.capturePostRoll != undefined){
|
||||
self.timelapseCapturePostRoll(config.capturePostRoll);
|
||||
} else {
|
||||
self.timelapseCapturePostRoll(self.defaultCapturePostRoll);
|
||||
}
|
||||
|
||||
self.persist(false);
|
||||
self.isDirty(false);
|
||||
};
|
||||
|
|
@ -214,6 +225,7 @@ $(function() {
|
|||
|
||||
if (self.timelapseType() == "timed") {
|
||||
payload["interval"] = self.timelapseTimedInterval();
|
||||
payload["capturePostRoll"] = self.timelapseCapturePostRoll();
|
||||
}
|
||||
|
||||
if (self.timelapseType() == "zchange") {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@
|
|||
<input type="text" class="input-mini" id="webcam_timelapse_postRoll" data-bind="value: timelapsePostRoll, valueUpdate: 'afterkeydown', enable: isOperational() && !isPrinting() && loginState.isUser() && timelapseTypeSelected()">
|
||||
<span class="add-on">{{ _('sec') }}</span>
|
||||
</div>
|
||||
<div id="webcam_timelapse_timed_postRoll" data-bind="visible: timelapseType() == 'timed'">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: timelapseCapturePostRoll, valueUpdate: 'afterkeydown', enable: isOperational() && !isPrinting() && loginState.isUser()"> {{ _('Capture postroll images. If disabled, we will simply repeat the last frame for the post roll') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="webcam_timelapse_timedsettings" data-bind="visible: intervalInputEnabled">
|
||||
<label for="webcam_timelapse_interval">{{ _('Interval') }}</label>
|
||||
|
|
|
|||
|
|
@ -303,7 +303,10 @@ def configure_timelapse(config=None, persist=False):
|
|||
interval = 10
|
||||
if "options" in config and "interval" in config["options"] and config["options"]["interval"] > 0:
|
||||
interval = config["options"]["interval"]
|
||||
current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps)
|
||||
capture_post_roll = True
|
||||
if "options" in config and "capturePostRoll" in config["options"] and isinstance(config["options"]["capturePostRoll"], bool):
|
||||
capture_post_roll = config["options"]["capturePostRoll"]
|
||||
current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps, capture_post_roll=capture_post_roll)
|
||||
|
||||
notify_callbacks(current)
|
||||
|
||||
|
|
@ -628,11 +631,12 @@ class ZTimelapse(Timelapse):
|
|||
|
||||
|
||||
class TimedTimelapse(Timelapse):
|
||||
def __init__(self, post_roll=0, interval=1, fps=25):
|
||||
def __init__(self, post_roll=0, interval=1, fps=25, capture_post_roll=True):
|
||||
Timelapse.__init__(self, post_roll=post_roll, fps=fps)
|
||||
self._interval = interval
|
||||
if self._interval < 1:
|
||||
self._interval = 1 # force minimum interval of 1s
|
||||
self._capture_post_roll = capture_post_roll
|
||||
self._postroll_captures = 0
|
||||
self._timer = None
|
||||
self._logger.debug("TimedTimelapse initialized")
|
||||
|
|
@ -641,11 +645,16 @@ class TimedTimelapse(Timelapse):
|
|||
def interval(self):
|
||||
return self._interval
|
||||
|
||||
@property
|
||||
def capture_post_roll(self):
|
||||
return self._capture_post_roll
|
||||
|
||||
def config_data(self):
|
||||
return {
|
||||
"type": "timed",
|
||||
"options": {
|
||||
"interval": self._interval
|
||||
"interval": self._interval,
|
||||
"capture_post_roll": self._capture_post_roll
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -662,14 +671,31 @@ class TimedTimelapse(Timelapse):
|
|||
self._timer.start()
|
||||
|
||||
def on_print_done(self, event, payload):
|
||||
self._postroll_captures = self._post_roll * self._fps
|
||||
if self._capture_post_roll:
|
||||
self._postroll_captures = self._post_roll * self._fps
|
||||
Timelapse.on_print_done(self, event, payload)
|
||||
|
||||
def calculate_post_roll(self):
|
||||
return self._post_roll * self._fps * self._interval
|
||||
if self._capture_post_roll:
|
||||
return self._post_roll * self._fps * self._interval
|
||||
else:
|
||||
return Timelapse.calculate_post_roll(self)
|
||||
|
||||
def process_post_roll(self):
|
||||
pass
|
||||
if self._capture_post_roll:
|
||||
return
|
||||
|
||||
with self._capture_mutex:
|
||||
filename = os.path.join(self._capture_dir, _capture_format.format(prefix=self._file_prefix) % self._image_number)
|
||||
self._image_number += 1
|
||||
|
||||
if self._perform_capture(filename):
|
||||
for _ in range(self._post_roll * self._fps):
|
||||
newFile = os.path.join(self._capture_dir, _capture_format.format(prefix=self._file_prefix) % self._image_number)
|
||||
self._image_number += 1
|
||||
shutil.copyfile(filename, newFile)
|
||||
|
||||
self.post_roll_finished()
|
||||
|
||||
def post_roll_finished(self):
|
||||
Timelapse.post_roll_finished(self)
|
||||
|
|
@ -684,7 +710,8 @@ class TimedTimelapse(Timelapse):
|
|||
self._postroll_captures -= 1
|
||||
|
||||
def _on_timer_finished(self):
|
||||
self.post_roll_finished()
|
||||
if self._capture_post_roll:
|
||||
self.post_roll_finished()
|
||||
|
||||
|
||||
class TimelapseRenderJob(object):
|
||||
|
|
|
|||
Loading…
Reference in a new issue