Merge branch 'maintenance' into devel
Conflicts: src/octoprint/static/js/app/dataupdater.js src/octoprint/static/js/app/viewmodels/timelapse.js
This commit is contained in:
commit
7d52aa4be2
5 changed files with 160 additions and 104 deletions
|
|
@ -112,7 +112,7 @@ class EventManager(object):
|
|||
self._registeredListeners = collections.defaultdict(list)
|
||||
self._logger = logging.getLogger(__name__)
|
||||
|
||||
self._queue = Queue.PriorityQueue()
|
||||
self._queue = Queue.Queue()
|
||||
self._worker = threading.Thread(target=self._work)
|
||||
self._worker.daemon = True
|
||||
self._worker.start()
|
||||
|
|
@ -120,7 +120,7 @@ class EventManager(object):
|
|||
def _work(self):
|
||||
try:
|
||||
while True:
|
||||
(event, payload) = self._queue.get(True)
|
||||
event, payload = self._queue.get(True)
|
||||
|
||||
eventListeners = self._registeredListeners[event]
|
||||
self._logger.debug("Firing event: %s (Payload: %r)" % (event, payload))
|
||||
|
|
@ -149,7 +149,7 @@ class EventManager(object):
|
|||
payload being a payload object specific to the event.
|
||||
"""
|
||||
|
||||
self._queue.put((event, payload), 0)
|
||||
self._queue.put((event, payload))
|
||||
|
||||
if event == Events.UPDATED_FILES and "type" in payload and payload["type"] == "printables":
|
||||
# when sending UpdatedFiles with type "printables", also send another event with deprecated type "gcode"
|
||||
|
|
@ -157,8 +157,7 @@ class EventManager(object):
|
|||
import copy
|
||||
legacy_payload = copy.deepcopy(payload)
|
||||
legacy_payload["type"] = "gcode"
|
||||
self._queue.put((event, legacy_payload), 0)
|
||||
|
||||
self._queue.put((event, legacy_payload))
|
||||
|
||||
def subscribe(self, event, callback):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -533,7 +533,10 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback):
|
|||
self.refresh_sd_files(blocking=True)
|
||||
existingSdFiles = map(lambda x: x[0], self._comm.getSdFiles())
|
||||
|
||||
remoteName = util.get_dos_filename(filename, existing_filenames=existingSdFiles, extension="gco")
|
||||
remoteName = util.get_dos_filename(filename,
|
||||
existing_filenames=existingSdFiles,
|
||||
extension="gco",
|
||||
whitelisted_extensions=["gco", "g"])
|
||||
self._timeEstimationData = TimeEstimationHelper()
|
||||
self._comm.startFileTransfer(absolutePath, filename, "/" + remoteName)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ function DataUpdater(allViewModels) {
|
|||
self._lastProcessingTimes = [];
|
||||
self._lastProcessingTimesSize = 20;
|
||||
|
||||
self._timelapse_popup = undefined;
|
||||
|
||||
self.increaseThrottle = function() {
|
||||
self.setThrottle(self._throttleFactor + 1);
|
||||
};
|
||||
|
|
@ -156,7 +154,6 @@ function DataUpdater(allViewModels) {
|
|||
var type = event.data["type"];
|
||||
var payload = event.data["payload"];
|
||||
var html = "";
|
||||
var format = {};
|
||||
|
||||
log.debug("Got event " + type + " with payload: " + JSON.stringify(payload));
|
||||
|
||||
|
|
@ -164,91 +161,6 @@ function DataUpdater(allViewModels) {
|
|||
if (payload && payload.hasOwnProperty("config_hash")) {
|
||||
self._configHash = payload.config_hash;
|
||||
}
|
||||
} else if (type == "MovieRendering") {
|
||||
if (self._timelapse_popup !== undefined) {
|
||||
self._timelapse_popup.remove();
|
||||
}
|
||||
self._timelapse_popup = new PNotify({
|
||||
title: gettext("Rendering timelapse"),
|
||||
text: _.sprintf(gettext("Now rendering timelapse %(movie_basename)s. Due to performance reasons it is not recommended to start a print job while a movie is still rendering."), payload),
|
||||
hide: false,
|
||||
callbacks: {
|
||||
before_close: function() {
|
||||
self._timelapse_popup = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type == "MovieDone") {
|
||||
if (self._timelapse_popup !== undefined) {
|
||||
self._timelapse_popup.remove();
|
||||
}
|
||||
self._timelapse_popup = new PNotify({
|
||||
title: gettext("Timelapse ready"),
|
||||
text: _.sprintf(gettext("New timelapse %(movie_basename)s is done rendering."), payload),
|
||||
type: "success",
|
||||
callbacks: {
|
||||
before_close: function(notice) {
|
||||
if (self._timelapse_popup == notice) {
|
||||
self._timelapse_popup = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type == "MovieFailed") {
|
||||
html = "<p>" + _.sprintf(gettext("Rendering of timelapse %(movie_basename)s failed with return code %(returncode)s"), payload) + "</p>";
|
||||
html += pnotifyAdditionalInfo('<pre style="overflow: auto">' + payload.error + '</pre>');
|
||||
|
||||
if (self._timelapse_popup !== undefined) {
|
||||
self._timelapse_popup.remove();
|
||||
}
|
||||
self._timelapse_popup = new PNotify({
|
||||
title: gettext("Rendering failed"),
|
||||
text: html,
|
||||
type: "error",
|
||||
hide: false,
|
||||
callbacks: {
|
||||
before_close: function(notice) {
|
||||
if (self._timelapse_popup == notice) {
|
||||
self._timelapse_popup = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type == "PostRollStart") {
|
||||
var title = gettext("Capturing timelapse postroll");
|
||||
|
||||
var text;
|
||||
if (!payload.postroll_duration) {
|
||||
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take only a moment..."), format);
|
||||
} else {
|
||||
format = {
|
||||
time: moment().add(payload.postroll_duration, "s").format("LT")
|
||||
};
|
||||
|
||||
if (payload.postroll_duration > 60) {
|
||||
format.duration = _.sprintf(gettext("%(minutes)d min"), {minutes: payload.postroll_duration / 60});
|
||||
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s (so until %(time)s)..."), format);
|
||||
} else {
|
||||
format.duration = _.sprintf(gettext("%(seconds)d sec"), {seconds: payload.postroll_duration});
|
||||
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s..."), format);
|
||||
}
|
||||
}
|
||||
|
||||
if (self._timelapse_popup !== undefined) {
|
||||
self._timelapse_popup.remove();
|
||||
}
|
||||
self._timelapse_popup = new PNotify({
|
||||
title: title,
|
||||
text: text,
|
||||
hide: false,
|
||||
callbacks: {
|
||||
before_close: function(notice) {
|
||||
if (self._timelapse_popup == notice) {
|
||||
self._timelapse_popup = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type == "SlicingStarted") {
|
||||
gcodeUploadProgress.addClass("progress-striped").addClass("active");
|
||||
gcodeUploadProgressBar.css("width", "100%");
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ $(function() {
|
|||
|
||||
self.loginState = parameters[0];
|
||||
|
||||
self.timelapsePopup = undefined;
|
||||
|
||||
self.defaultFps = 25;
|
||||
self.defaultPostRoll = 0;
|
||||
self.defaultInterval = 10;
|
||||
|
|
@ -222,11 +224,88 @@ $(function() {
|
|||
.done(self.fromResponse);
|
||||
};
|
||||
|
||||
self.displayTimelapsePopup = function(options) {
|
||||
if (self.timelapsePopup !== undefined) {
|
||||
self.timelapsePopup.remove();
|
||||
}
|
||||
|
||||
_.extend(options, {
|
||||
callbacks: {
|
||||
before_close: function(notice) {
|
||||
if (self.timelapsePopup == notice) {
|
||||
self.timelapsePopup = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.timelapsePopup = new PNotify(options);
|
||||
};
|
||||
|
||||
self.onDataUpdaterReconnect = function() {
|
||||
self.requestData();
|
||||
};
|
||||
|
||||
self.onEventMovieDone = function() {
|
||||
self.onEventPostRollStart = function(payload) {
|
||||
var title = gettext("Capturing timelapse postroll");
|
||||
|
||||
var text;
|
||||
if (!payload.postroll_duration) {
|
||||
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take only a moment..."), format);
|
||||
} else {
|
||||
var format = {
|
||||
time: moment().add(payload.postroll_duration, "s").format("LT")
|
||||
};
|
||||
|
||||
if (payload.postroll_duration > 60) {
|
||||
format.duration = _.sprintf(gettext("%(minutes)d min"), {minutes: payload.postroll_duration / 60});
|
||||
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s (so until %(time)s)..."), format);
|
||||
} else {
|
||||
format.duration = _.sprintf(gettext("%(seconds)d sec"), {seconds: payload.postroll_duration});
|
||||
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s..."), format);
|
||||
}
|
||||
}
|
||||
|
||||
self.displayTimelapsePopup({
|
||||
title: title,
|
||||
text: text,
|
||||
hide: false
|
||||
});
|
||||
};
|
||||
|
||||
self.onEventMovieRendering = function(payload) {
|
||||
self.displayTimelapsePopup({
|
||||
title: gettext("Rendering timelapse"),
|
||||
text: _.sprintf(gettext("Now rendering timelapse %(movie_basename)s. Due to performance reasons it is not recommended to start a print job while a movie is still rendering."), payload),
|
||||
hide: false
|
||||
});
|
||||
};
|
||||
|
||||
self.onEventMovieFailed = function(payload) {
|
||||
var html = "<p>" + _.sprintf(gettext("Rendering of timelapse %(movie_basename)s failed with return code %(returncode)s"), payload) + "</p>";
|
||||
html += pnotifyAdditionalInfo('<pre style="overflow: auto">' + payload.error + '</pre>');
|
||||
|
||||
self.displayTimelapsePopup({
|
||||
title: gettext("Rendering failed"),
|
||||
text: html,
|
||||
type: "error",
|
||||
hide: false
|
||||
});
|
||||
};
|
||||
|
||||
self.onEventMovieDone = function(payload) {
|
||||
self.displayTimelapsePopup({
|
||||
title: gettext("Timelapse ready"),
|
||||
text: _.sprintf(gettext("New timelapse %(movie_basename)s is done rendering."), payload),
|
||||
type: "success",
|
||||
callbacks: {
|
||||
before_close: function(notice) {
|
||||
if (self.timelapsePopup == notice) {
|
||||
self.timelapsePopup = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
self.requestData();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ def get_free_bytes(path):
|
|||
return psutil.disk_usage(path).free
|
||||
|
||||
|
||||
def get_dos_filename(origin, existing_filenames=None, extension=None, **kwargs):
|
||||
def get_dos_filename(input, existing_filenames=None, extension=None, whitelisted_extensions=None, **kwargs):
|
||||
"""
|
||||
Converts the provided input filename to a 8.3 DOS compatible filename. If ``existing_filenames`` is provided, the
|
||||
conversion result will be guaranteed not to collide with any of the filenames provided thus.
|
||||
|
|
@ -219,6 +219,8 @@ def get_dos_filename(origin, existing_filenames=None, extension=None, **kwargs):
|
|||
Optional.
|
||||
extension (string): The .3 file extension to use for the generated filename. If not provided, the extension of
|
||||
the provided ``filename`` will simply be truncated to 3 characters.
|
||||
whitelisted_extensions (list): A list of extensions on ``input`` that will be left as-is instead of
|
||||
exchanging for ``extension``.
|
||||
kwargs (dict): Additional keyword arguments to provide to :func:`find_collision_free_name`.
|
||||
|
||||
Returns:
|
||||
|
|
@ -228,16 +230,43 @@ def get_dos_filename(origin, existing_filenames=None, extension=None, **kwargs):
|
|||
|
||||
Raises:
|
||||
ValueError: No 8.3 compatible name could be found that doesn't collide with the provided ``existing_filenames``.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> get_dos_filename("test1234.gco")
|
||||
u'test1234.gco'
|
||||
>>> get_dos_filename("test1234.gcode")
|
||||
u'test1234.gco'
|
||||
>>> get_dos_filename("test12345.gco")
|
||||
u'test12~1.gco'
|
||||
>>> get_dos_filename("test1234.fnord", extension="gco")
|
||||
u'test1234.gco'
|
||||
>>> get_dos_filename("auto0.g", extension="gco")
|
||||
u'auto0.gco'
|
||||
>>> get_dos_filename("auto0.g", extension="gco", whitelisted_extensions=["g"])
|
||||
u'auto0.g'
|
||||
>>> get_dos_filename(None)
|
||||
>>> get_dos_filename("foo")
|
||||
u'foo'
|
||||
"""
|
||||
|
||||
if origin is None:
|
||||
if input is None:
|
||||
return None
|
||||
|
||||
if existing_filenames is None:
|
||||
existing_filenames = []
|
||||
|
||||
filename, ext = os.path.splitext(origin)
|
||||
if extension is None:
|
||||
if extension is not None:
|
||||
extension = extension.lower()
|
||||
|
||||
if whitelisted_extensions is None:
|
||||
whitelisted_extensions = []
|
||||
|
||||
filename, ext = os.path.splitext(input)
|
||||
|
||||
ext = ext.lower()
|
||||
ext = ext[1:] if ext.startswith(".") else ext
|
||||
if ext in whitelisted_extensions or extension is None:
|
||||
extension = ext
|
||||
|
||||
return find_collision_free_name(filename, extension, existing_filenames, **kwargs)
|
||||
|
|
@ -286,9 +315,36 @@ def find_collision_free_name(filename, extension, existing_filenames, max_power=
|
|||
|
||||
Raises:
|
||||
ValueError: No collision free name could be found.
|
||||
"""
|
||||
|
||||
# TODO unit test!
|
||||
Examples:
|
||||
|
||||
>>> find_collision_free_name("test1234", "gco", [])
|
||||
u'test1234.gco'
|
||||
>>> find_collision_free_name("test1234", "gcode", [])
|
||||
u'test1234.gco'
|
||||
>>> find_collision_free_name("test12345", "gco", [])
|
||||
u'test12~1.gco'
|
||||
>>> find_collision_free_name("test 123", "gco", [])
|
||||
u'test_123.gco'
|
||||
>>> find_collision_free_name("test1234", "g o", [])
|
||||
u'test1234.g_o'
|
||||
>>> find_collision_free_name("test12345", "gco", ["test12~1.gco"])
|
||||
u'test12~2.gco'
|
||||
>>> many_files = ["test12~{}.gco".format(x) for x in range(10)[1:]]
|
||||
>>> find_collision_free_name("test12345", "gco", many_files)
|
||||
u'test1~10.gco'
|
||||
>>> many_more_files = many_files + ["test1~{}.gco".format(x) for x in range(10, 99)]
|
||||
>>> find_collision_free_name("test12345", "gco", many_more_files)
|
||||
u'test1~99.gco'
|
||||
>>> many_more_files_plus_one = many_more_files + ["test1~99.gco"]
|
||||
>>> find_collision_free_name("test12345", "gco", many_more_files_plus_one)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Can't create a collision free filename
|
||||
>>> find_collision_free_name("test12345", "gco", many_more_files_plus_one, max_power=3)
|
||||
u'test~100.gco'
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(filename, unicode):
|
||||
filename = unicode(filename)
|
||||
|
|
@ -302,14 +358,21 @@ def find_collision_free_name(filename, extension, existing_filenames, max_power=
|
|||
extension = make_valid(extension)
|
||||
extension = extension[:3] if len(extension) > 3 else extension
|
||||
|
||||
if len(filename) <= 8 and not filename + "." + extension in existing_filenames:
|
||||
full_name_format = u"{filename}.{extension}" if extension else u"{filename}"
|
||||
|
||||
result = full_name_format.format(filename=filename,
|
||||
extension=extension)
|
||||
if len(filename) <= 8 and not result in existing_filenames:
|
||||
# early exit
|
||||
return filename + "." + extension
|
||||
return result
|
||||
|
||||
counter = 1
|
||||
power = 1
|
||||
prefix_format = u"{segment}~{counter}"
|
||||
while counter < (10 ** max_power):
|
||||
result = filename[:(6 - power + 1)] + "~" + str(counter) + "." + extension
|
||||
prefix = prefix_format.format(segment=filename[:(6 - power + 1)], counter=str(counter))
|
||||
result = full_name_format.format(filename=prefix,
|
||||
extension=extension)
|
||||
if result not in existing_filenames:
|
||||
return result
|
||||
counter += 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue