Skip to content
Permalink
Browse files

Add option to repeat last captured frame instead of capturing new fra…

…mes for the end of a timelapse. Defaults to current behavior (capturing new frames for the time after). Implements foosel/OctoPrint#1422
  • Loading branch information...
dattas committed Oct 21, 2016
1 parent f72cdb8 commit 5f073caf01667823a6bf3b9aff798e3409d2a027
@@ -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 @@ def settings(init=False, basedir=None, configfile=None):
"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 @@ def _on_z_change(self, event, payload):


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 @@ def __init__(self, post_roll=0, interval=1, fps=25):
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 @@ def on_print_started(self, event, payload):
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 @@ def _timer_task(self):
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):

0 comments on commit 5f073ca

Please sign in to comment.
You can’t perform that action at this time.