Skip to content

Commit

Permalink
Add option to repeat last captured frame instead of capturing new fra…
Browse files Browse the repository at this point in the history
…mes for the end of a timelapse. Defaults to current behavior (capturing new frames for the time after). Implements #1422
  • Loading branch information
dattas committed Oct 21, 2016
1 parent b38d181 commit 7e2382f
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 9 deletions.
14 changes: 13 additions & 1 deletion src/octoprint/server/api/timelapse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion src/octoprint/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down
12 changes: 12 additions & 0 deletions src/octoprint/static/js/app/viewmodels/timelapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
};
Expand Down Expand Up @@ -214,6 +225,7 @@ $(function() {

if (self.timelapseType() == "timed") {
payload["interval"] = self.timelapseTimedInterval();
payload["capturePostRoll"] = self.timelapseCapturePostRoll();
}

if (self.timelapseType() == "zchange") {
Expand Down
6 changes: 6 additions & 0 deletions src/octoprint/templates/tabs/timelapse.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
41 changes: 34 additions & 7 deletions src/octoprint/timelapse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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")
Expand All @@ -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
}
}

Expand All @@ -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)
Expand All @@ -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):
Expand Down

0 comments on commit 7e2382f

Please sign in to comment.