Properly track unrendered timelapses that are being processed

UI removes action buttons for them and displays a spinner
indicating that the timelapse in question is either being
recorded or being rendered at the moment.

Should prevent users from accidentally trying to render an
incomplete timelapse.
This commit is contained in:
Gina Häußge 2016-06-02 10:11:54 +02:00
parent 21a4033f73
commit 8ce1eafd5d
2 changed files with 41 additions and 12 deletions

View file

@ -94,7 +94,10 @@
<td class="timelapse_unrendered_name" data-bind="text: name"></td>
<td class="timelapse_unrendered_count" data-bind="text: count"></td>
<td class="timelapse_unrendered_size" data-bind="text: size"></td>
<td class="timelapse_unrendered_action"><a href="javascript:void(0)" title="{{ _('Delete unrendered timelapse') }}" class="icon-trash" data-bind="click: function() { if ($root.loginState.isUser()) { $parent.removeUnrendered($data.name); } else { return; } }, css: {disabled: !$root.loginState.isUser()}"></a>&nbsp;|&nbsp;<a href="javascript:void(0)" title="{{ _('Render timelapse') }}" class="icon-facetime-video" data-bind="click: function() { if ($root.loginState.isUser() && !$root.isBusy()) { $parent.renderUnrendered($data.name); } else { return; } }, css: {disabled: !$root.loginState.isUser() || $root.isBusy()}"></a></td>
<td class="timelapse_unrendered_action">
<span data-bind="visible: processing"><i class="icon-refresh icon-spin"></i></span>
<span data-bind="visible: !processing"><a href="javascript:void(0)" title="{{ _('Delete unrendered timelapse') }}" class="icon-trash" data-bind="click: function() { if ($root.loginState.isUser()) { $parent.removeUnrendered($data.name); } else { return; } }, css: {disabled: !$root.loginState.isUser()}"></a>&nbsp;|&nbsp;<a href="javascript:void(0)" title="{{ _('Render timelapse') }}" class="icon-facetime-video" data-bind="click: function() { if ($root.loginState.isUser() && !$root.isBusy()) { $parent.renderUnrendered($data.name); } else { return; } }, css: {disabled: !$root.loginState.isUser() || $root.isBusy()}"></a></span>
</td>
</tr>
</tbody>
</table>

View file

@ -47,6 +47,9 @@ _update_callbacks = []
# lock for timelapse cleanup, must be re-entrant
_cleanup_lock = threading.RLock()
# lock for timelapse job
_job_lock = threading.RLock()
def _extract_prefix(filename):
"""
@ -78,10 +81,14 @@ def get_finished_timelapses():
def get_unrendered_timelapses():
global _job_lock
global current
delete_old_unrendered_timelapses()
basedir = settings().getBaseFolder("timelapse_tmp")
jobs = collections.defaultdict(lambda: dict(count=0, size=None, bytes=0, date=None, timestamp=None))
for osFile in os.listdir(basedir):
if not fnmatch.fnmatch(osFile, "*.jpg"):
continue
@ -96,13 +103,23 @@ def get_unrendered_timelapses():
if jobs[prefix]["timestamp"] is None or statResult.st_ctime < jobs[prefix]["timestamp"]:
jobs[prefix]["timestamp"] = statResult.st_ctime
def finalize_fields(job):
job["size"] = util.get_formatted_size(job["bytes"])
job["date"] = util.get_formatted_datetime(datetime.datetime.fromtimestamp(job["timestamp"]))
del job["timestamp"]
return job
with _job_lock:
global current_render_job
return sorted([util.dict_merge(dict(name=key), finalize_fields(value)) for key, value in jobs.items()], key=lambda x: x["name"])
def finalize_fields(prefix, job):
currently_recording = current is not None and current.prefix == prefix
currently_rendering = current_render_job is not None and current_render_job["prefix"] == prefix
job["size"] = util.get_formatted_size(job["bytes"])
job["date"] = util.get_formatted_datetime(datetime.datetime.fromtimestamp(job["timestamp"]))
job["recording"] = currently_recording
job["rendering"] = currently_rendering
job["processing"] = currently_recording or currently_rendering
del job["timestamp"]
return job
return sorted([util.dict_merge(dict(name=key), finalize_fields(key, value)) for key, value in jobs.items()], key=lambda x: x["name"])
def delete_unrendered_timelapse(name):
@ -175,11 +192,15 @@ def delete_old_unrendered_timelapses():
def _create_render_start_handler(name, gcode=None):
def f(movie):
global current_render_job
event_payload = {"gcode": gcode if gcode is not None else "unknown",
"movie": movie,
"movie_basename": os.path.basename(movie)}
current_render_job = event_payload
global _job_lock
with _job_lock:
global current_render_job
event_payload = {"gcode": gcode if gcode is not None else "unknown",
"movie": movie,
"movie_basename": os.path.basename(movie)}
current_render_job = dict(prefix=name)
current_render_job.update(event_payload)
eventManager().fire(Events.MOVIE_RENDERING, event_payload)
return f
@ -277,6 +298,7 @@ class Timelapse(object):
self._image_number = None
self._in_timelapse = False
self._gcode_file = None
self._file_prefix = None
self._post_roll = post_roll
self._on_post_roll_done = None
@ -303,6 +325,10 @@ class Timelapse(object):
for (event, callback) in self.event_subscriptions():
eventManager().subscribe(event, callback)
@property
def prefix(self):
return self._file_prefix
@property
def post_roll(self):
return self._post_roll