From 43209613ce045bf873171fd58fdff4c8e3b779c4 Mon Sep 17 00:00:00 2001 From: FormerLurker Date: Wed, 7 Aug 2019 09:00:39 -0500 Subject: [PATCH 001/485] Fix issue parsing floats as ints in cura speed settings. Replace parse_int with parse_float in these cases. Update cura so that the 'retraction_speed' setting is used if retraction_retract_speed or retraction_prime_speed is not present. --- octoprint_octolapse/gcode_preprocessing.py | 11 +++++------ octoprint_octolapse/settings.py | 9 +++++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/octoprint_octolapse/gcode_preprocessing.py b/octoprint_octolapse/gcode_preprocessing.py index 712f4909..7b2a24a5 100644 --- a/octoprint_octolapse/gcode_preprocessing.py +++ b/octoprint_octolapse/gcode_preprocessing.py @@ -668,7 +668,6 @@ def parse_filament_used(parse_string): u'cm3': cm3_used } - @staticmethod def parse_version(parse_string): # get version @@ -1242,13 +1241,13 @@ def get_settings_dictionary(): u'retraction_amount': SettingsDefinition(u'retraction_amount', CuraParsingFunctions.parse_float, [u'octolapse_setting']), u'retraction_hop': SettingsDefinition(u'retraction_hop', CuraParsingFunctions.parse_float,[u'octolapse_setting']), u'retraction_hop_enabled': SettingsDefinition(u'retraction_hop_enabled', CuraParsingFunctions.parse_bool,[u'octolapse_setting']), - u'retraction_prime_speed': SettingsDefinition(u'retraction_prime_speed', CuraParsingFunctions.parse_int,[u'octolapse_setting']), - u'retraction_retract_speed': SettingsDefinition(u'retraction_retract_speed', CuraParsingFunctions.parse_int,[u'octolapse_setting']), - u'retraction_speed': SettingsDefinition(u'retraction_speed', CuraParsingFunctions.parse_int,[u'octolapse_setting']), + u'retraction_prime_speed': SettingsDefinition(u'retraction_prime_speed', CuraParsingFunctions.parse_float,[u'octolapse_setting']), + u'retraction_retract_speed': SettingsDefinition(u'retraction_retract_speed', CuraParsingFunctions.parse_float,[u'octolapse_setting']), + u'retraction_speed': SettingsDefinition(u'retraction_speed', CuraParsingFunctions.parse_float, [u'octolapse_setting']), # Note that the below speed doesn't represent the initial layer or travel speed. See speed_print_layer_0 # however, a test will need to be performed. - u'speed_travel': SettingsDefinition(u'speed_travel', CuraParsingFunctions.parse_int, [u'octolapse_setting']), + u'speed_travel': SettingsDefinition(u'speed_travel', CuraParsingFunctions.parse_float, [u'octolapse_setting']), u'retraction_enable': SettingsDefinition(u'retraction_enable', CuraParsingFunctions.parse_bool, [u'octolapse_setting']), u'version': SettingsDefinition(u'version', CuraParsingFunctions.strip_string, [u'octolapse_setting']), @@ -1259,7 +1258,7 @@ def get_settings_dictionary(): CuraParsingFunctions.strip_string, [u'octolapse_setting']), # End Octolapse Settings - The rest is included in case it is ever helpful for Octolapse or for other projects! - u'speed_infill': SettingsDefinition(u'speed_infill', CuraParsingFunctions.parse_int, + u'speed_infill': SettingsDefinition(u'speed_infill', CuraParsingFunctions.parse_float, [u'misc']), u'skirt_brim_speed': SettingsDefinition(u'skirt_brim_speed', CuraParsingFunctions.parse_float, [u'misc']), diff --git a/octoprint_octolapse/settings.py b/octoprint_octolapse/settings.py index 5cca5bbc..fd20c018 100644 --- a/octoprint_octolapse/settings.py +++ b/octoprint_octolapse/settings.py @@ -2265,6 +2265,7 @@ class CuraSettings(SlicerSettings): def __init__(self, version="unknown"): super(CuraSettings, self).__init__(SlicerSettings.SlicerTypeCura, version) self.retraction_amount = None + self.retraction_speed = None self.retraction_retract_speed = None self.retraction_prime_speed = None self.retraction_hop_enabled = None # new setting @@ -2313,10 +2314,14 @@ def get_retraction_hop(self): return float(self.retraction_hop) def get_retraction_retract_speed(self): - return self.get_speed_mm_min(self.retraction_retract_speed) + if self.retraction_retract_speed: + return self.get_speed_mm_min(self.retraction_retract_speed) + return self.get_speed_mm_min(self.retraction_speed) def get_retraction_prime_speed(self): - return self.get_speed_mm_min(self.retraction_prime_speed) + if self.retraction_prime_speed: + return self.get_speed_mm_min(self.retraction_prime_speed) + return self.get_speed_mm_min(self.retraction_speed) def get_speed_travel(self): return self.get_speed_mm_min(self.speed_travel) From 27bcdb916ef0d14e40b16873bdbbe3853d4208e4 Mon Sep 17 00:00:00 2001 From: FormerLurker Date: Mon, 12 Aug 2019 09:33:55 -0500 Subject: [PATCH 002/485] Change layer trigger and layer detection so that it can trigger on prime. Fix distance saved when using snap-to-print. Fix issue retriving g90influencesextruder from octoprint settings. --- octoprint_octolapse/__init__.py | 2 +- .../data/lib/c/gcode_position.cpp | 2 +- .../data/lib/c/stabilization_smart_layer.cpp | 12 +++- octoprint_octolapse/gcode.py | 32 ++++++++--- octoprint_octolapse/position.py | 47 ++++++++------- octoprint_octolapse/static/js/octolapse.js | 3 +- octoprint_octolapse/timelapse.py | 4 +- octoprint_octolapse/trigger.py | 57 +++++++++++-------- 8 files changed, 103 insertions(+), 56 deletions(-) diff --git a/octoprint_octolapse/__init__.py b/octoprint_octolapse/__init__.py index d0522b49..80dd6dd8 100644 --- a/octoprint_octolapse/__init__.py +++ b/octoprint_octolapse/__init__.py @@ -1871,7 +1871,7 @@ def test_timelapse_config(self): return {"success": True} def get_octoprint_g90_influences_extruder(self): - return self._settings.global_get(["feature", "g90_influences_extruder"]) + return self._settings.global_get(["feature", "g90InfluencesExtruder"]) def get_octoprint_printer_profile(self): return self._printer_profile_manager.get_current() diff --git a/octoprint_octolapse/data/lib/c/gcode_position.cpp b/octoprint_octolapse/data/lib/c/gcode_position.cpp index b39067f7..7bb121e9 100644 --- a/octoprint_octolapse/data/lib/c/gcode_position.cpp +++ b/octoprint_octolapse/data/lib/c/gcode_position.cpp @@ -264,7 +264,7 @@ void gcode_position::update(parsed_command *command, const int file_line_number, { if (!p_current_pos_->z_null_) { - if (p_current_pos_->is_extruding_) + if (p_current_pos_->is_extruding_ || p_current_pos_->is_primed_) { p_current_pos_->last_extrusion_height_ = p_current_pos_->z_; p_current_pos_->last_extrusion_height_null_ = false; diff --git a/octoprint_octolapse/data/lib/c/stabilization_smart_layer.cpp b/octoprint_octolapse/data/lib/c/stabilization_smart_layer.cpp index 1811402c..fcf1c303 100644 --- a/octoprint_octolapse/data/lib/c/stabilization_smart_layer.cpp +++ b/octoprint_octolapse/data/lib/c/stabilization_smart_layer.cpp @@ -110,6 +110,7 @@ void stabilization_smart_layer::process_pos(position* p_current_pos, position* p { is_layer_change_wait_ = true; // get distance from current point to the stabilization point + standard_layer_trigger_distance_ = utilities::get_cartesian_distance( p_current_pos->x_, p_current_pos->y_, stabilization_x_, stabilization_y_ @@ -289,7 +290,16 @@ void stabilization_smart_layer::add_plan() { //std::cout << "Adding saved plan to plans... F Speed" << p_saved_position_->f_ << " \r\n"; snapshot_plan* p_plan = new snapshot_plan(); - double total_travel_distance = p_closest->distance * 2; + double total_travel_distance; + if (p_smart_layer_args_->snap_to_print) + { + total_travel_distance = 0; + } + else + { + total_travel_distance = p_closest->distance * 2; + } + p_plan->total_travel_distance = total_travel_distance; p_plan->saved_travel_distance = (standard_layer_trigger_distance_ * 2) - total_travel_distance; p_plan->triggering_command_type = p_closest->type; diff --git a/octoprint_octolapse/gcode.py b/octoprint_octolapse/gcode.py index 66c777dc..25de0c02 100644 --- a/octoprint_octolapse/gcode.py +++ b/octoprint_octolapse/gcode.py @@ -71,6 +71,21 @@ def end_index(self): def snapshot_index(self): return len(self.InitializationGcode) + len(self.StartGcode) + len(self.snapshot_commands) - 1 + def __str__(self): + gcode_strings = ["\r\n\tSnapshot Gcode"] + + for gcode in self.InitializationGcode: + gcode_strings.append("Init - {0}".format(gcode)) + for gcode in self.StartGcode: + gcode_strings.append("Start - {0}".format(gcode)) + for gcode in self.snapshot_commands: + gcode_strings.append("Snapshot - {0}".format(gcode)) + for gcode in self.ReturnCommands: + gcode_strings.append("Return - {0}".format(gcode)) + for gcode in self.EndGcode: + gcode_strings.append("End - {0}".format(gcode)) + + return '\r\n\t\t'.join(gcode_strings) class SnapshotPlanStep(object): def __init__(self, action, x=None, y=None, z=None, e=None, f=None): @@ -834,10 +849,13 @@ def get_triggered_type(trigger): def create_snapshot_plan(self, position, trigger): snapshot_plan = SnapshotPlan() + # we have to undo the current position update since we will be operating on the previous position! + final_position = position.undo_update() # the parsed command has not been sent, but record it - snapshot_plan.triggering_command = position.current_pos.parsed_command - parsed_command_position = position.current_pos - current_position = position.previous_pos + snapshot_plan.triggering_command = final_position.parsed_command + parsed_command_position = final_position + current_position = position.current_pos + # create the start and end gcode, which would include any split gcode (position restriction intersection) # or any saved command that needs to be appended @@ -881,8 +899,6 @@ def create_snapshot_plan(self, position, trigger): snapshot_plan.start_command = gcode_command_1 - # undo the previous update to the position processor - position.undo_update() # calculate the initial position position.update(gcode_command_1.gcode) snapshot_plan.initial_position = position.current_pos @@ -1030,8 +1046,10 @@ def create_gcode_for_snapshot_plan(self, snapshot_plan, g90_influences_extruder, self.snapshot_gcode.snapshot_index(), self.snapshot_gcode.end_index() ) - for gcode in self.snapshot_gcode.snapshot_gcode(): - logger.info(" %s", gcode) + # enhanced snapshot gcode logger + logger.info("%s", self.snapshot_gcode) + #for gcode in self.snapshot_gcode.snapshot_gcode(): + # logger.info(" %s", gcode) return self.snapshot_gcode diff --git a/octoprint_octolapse/position.py b/octoprint_octolapse/position.py index dc87abfa..dc54bba8 100644 --- a/octoprint_octolapse/position.py +++ b/octoprint_octolapse/position.py @@ -985,6 +985,13 @@ def _extruder_state_triggered(option, state): return None def is_extruder_triggered(self, options): + return self._is_extruder_triggered(self.current_pos, options) + + def is_previous_extruder_triggered(self, options): + return self._is_extruder_triggered(self.previous_pos, options) + + @staticmethod + def _is_extruder_triggered(pos, options): # if there are no extruder trigger options, return true. if options is None: return True @@ -992,35 +999,35 @@ def is_extruder_triggered(self, options): # Matches the supplied extruder trigger options to the current # extruder state. Returns true if triggering, false if not. - extruding_start_triggered = self._extruder_state_triggered( - options.on_extruding_start, self.current_pos.is_extruding_start + extruding_start_triggered = Position._extruder_state_triggered( + options.on_extruding_start, pos.is_extruding_start ) - extruding_triggered = self._extruder_state_triggered( - options.on_extruding, self.current_pos.is_extruding + extruding_triggered = Position._extruder_state_triggered( + options.on_extruding, pos.is_extruding ) - primed_triggered = self._extruder_state_triggered( - options.on_primed, self.current_pos.is_primed + primed_triggered = Position._extruder_state_triggered( + options.on_primed, pos.is_primed ) - retracting_start_triggered = self._extruder_state_triggered( - options.on_retracting_start, self.current_pos.is_retracting_start + retracting_start_triggered = Position._extruder_state_triggered( + options.on_retracting_start, pos.is_retracting_start ) - retracting_triggered = self._extruder_state_triggered( - options.on_retracting, self.current_pos.is_retracting + retracting_triggered = Position._extruder_state_triggered( + options.on_retracting, pos.is_retracting ) - partially_retracted_triggered = self._extruder_state_triggered( - options.on_partially_retracted, self.current_pos.is_partially_retracted + partially_retracted_triggered = Position._extruder_state_triggered( + options.on_partially_retracted, pos.is_partially_retracted ) - retracted_triggered = self._extruder_state_triggered( - options.on_retracted, self.current_pos.is_retracted + retracted_triggered = Position._extruder_state_triggered( + options.on_retracted, pos.is_retracted ) - deretracting_start_triggered = self._extruder_state_triggered( - options.on_deretracting_start, self.current_pos.is_deretracting_start + deretracting_start_triggered = Position._extruder_state_triggered( + options.on_deretracting_start, pos.is_deretracting_start ) - deretracting_triggered = self._extruder_state_triggered( - options.on_deretracting, self.current_pos.is_deretracting + deretracting_triggered = Position._extruder_state_triggered( + options.on_deretracting, pos.is_deretracting ) - deretracted_triggered = self._extruder_state_triggered( - options.on_deretracted, self.current_pos.is_deretracted + deretracted_triggered = Position._extruder_state_triggered( + options.on_deretracted, pos.is_deretracted ) ret_value = False diff --git a/octoprint_octolapse/static/js/octolapse.js b/octoprint_octolapse/static/js/octolapse.js index e5472407..7e834369 100644 --- a/octoprint_octolapse/static/js/octolapse.js +++ b/octoprint_octolapse/static/js/octolapse.js @@ -366,7 +366,8 @@ $(function () { delete Octolapse.ConfirmDialogs[key]; } } - } + }; + Octolapse.showConfirmDialog = function(key, title, text, onConfirm, onCancel, onComplete) { Octolapse.closeConfirmDialogsForKeys([key]); diff --git a/octoprint_octolapse/timelapse.py b/octoprint_octolapse/timelapse.py index ddaf4324..c4a2af09 100644 --- a/octoprint_octolapse/timelapse.py +++ b/octoprint_octolapse/timelapse.py @@ -787,7 +787,7 @@ def process_realtime_gcode(self, gcode, tags): thread.start() # undo the position update since we'll be suppressing this command - self._position.undo_update() + #self._position.undo_update() # suppress the current command, we'll send it later return None, @@ -1072,6 +1072,8 @@ def log_octolapse_gcode(self, logf, msg, cmd, tags): logf("Force E axis mode gcode - %s: %s", msg, cmd) elif "preview-stabilization" in tags: logf("Preview stabilization gcode - %s: %s", msg, cmd) + else: + logf("%s: %s", msg, cmd) # internal functions #################### diff --git a/octoprint_octolapse/trigger.py b/octoprint_octolapse/trigger.py index 81e7e87e..c12514df 100644 --- a/octoprint_octolapse/trigger.py +++ b/octoprint_octolapse/trigger.py @@ -96,8 +96,8 @@ def update(self, position, parsed_command): current_trigger.update(position) # Make sure there are no position errors (unknown position, out of bounds, etc) - if position.current_pos.has_position_error: - logger.error("A trigger has a position error:%s", position.current_pos.position_error) + if position.previous_pos.has_position_error: + logger.error("A trigger has a position error:%s", position.previous_pos.position_error) # see if the current trigger is triggering, indicting that a snapshot should be taken except Exception as e: logger.exception("Failed to update the snapshot triggers.") @@ -345,9 +345,12 @@ def update(self, position, parsed_command): state = GcodeTriggerState(state) # reset any variables that must be reset each update state.reset_state() + + # set the trigger position. It should be the previous position, not the current + trigger_position = position.previous_pos + # Don't update the trigger if we don't have a homed axis - # Make sure to use the previous value so the homing operation can complete - if not position.current_pos.has_homed_position: + if not trigger_position.has_homed_position: state.is_triggered = False state.is_homed = False else: @@ -355,17 +358,17 @@ def update(self, position, parsed_command): # check to see if we are in the proper position to take a snapshot # set is in position - state.is_in_position = position.current_pos.is_in_position and position.current_pos.is_in_bounds + state.is_in_position = trigger_position.is_in_position and trigger_position.is_in_bounds state.in_path_position = position.current_pos.in_path_position if self.snapshot_command.lower() == parsed_command.gcode.lower(): state.is_waiting = True if state.is_waiting: - if position.is_extruder_triggered(self.extruder_triggers): - if not position.previous_pos.has_homed_position: + if position.is_previous_extruder_triggered(self.extruder_triggers): + if not trigger_position.has_homed_position: state.is_waiting_for_homed_position = True logger.debug("GcodeTrigger - Triggering - Waiting for the previous position to be homed.") - elif self.require_zhop and not position.current_pos.is_zhop: + elif self.require_zhop and not trigger_position.is_zhop: state.is_waiting_on_zhop = True logger.debug("GcodeTrigger - Waiting on ZHop.") elif not state.is_in_position and not state.in_path_position: @@ -508,16 +511,20 @@ def update(self, position): # reset any variables that must be reset each update state.reset_state() + + # set the trigger position. It should be the previous position, not the current + trigger_position = position.previous_pos # Don't update the trigger if we don't have a homed axis - # Make sure to use the previous value so the homing operation can complete - if not position.current_pos.has_homed_position: + if not trigger_position.has_homed_position: state.is_triggered = False state.is_homed = False else: state.is_homed = True # set is in position - state.is_in_position = position.current_pos.is_in_position and position.current_pos.is_in_bounds + state.is_in_position = trigger_position.is_in_position and trigger_position.is_in_bounds + # the in path position will be our CURRENT POSITION not the trigger position + # (which is the previous position) state.in_path_position = position.current_pos.in_path_position # calculate height increment changed @@ -526,12 +533,12 @@ def update(self, position): and self.height_increment > 0 and position.current_pos.is_layer_change and ( - state.current_increment * self.height_increment < position.current_pos.height or + state.current_increment * self.height_increment < trigger_position.height or state.current_increment == 0 ) ): - new_increment = int(math.ceil(position.current_pos.height/self.height_increment)) + new_increment = int(math.ceil(trigger_position.height/self.height_increment)) if new_increment <= state.current_increment: message = ( @@ -556,7 +563,7 @@ def update(self, position): "Layer Trigger - Height Increment:%s, Current Increment:%s, Height: %s", self.height_increment, state.current_increment, - position.current_pos.height + trigger_position.height ) # see if we've encountered a layer or height change @@ -566,8 +573,9 @@ def update(self, position): state.is_waiting = True else: + # see if the CURRENT position is a layer change if position.current_pos.is_layer_change: - state.layer = position.current_pos.layer + state.layer = trigger_position.layer state.is_layer_change_wait = True state.is_layer_change = True state.is_waiting = True @@ -575,7 +583,7 @@ def update(self, position): if state.is_height_change_wait or state.is_layer_change_wait or state.is_waiting: state.is_waiting = True # see if the extruder is triggering - is_extruder_triggering = position.is_extruder_triggered(self.extruder_triggers) + is_extruder_triggering = position.is_previous_extruder_triggered(self.extruder_triggers) if not is_extruder_triggering: state.is_waiting_on_extruder = True if state.is_height_change_wait: @@ -583,10 +591,10 @@ def update(self, position): elif state.is_layer_change_wait: logger.debug("LayerTrigger - Layer change triggering, waiting on extruder.") else: - if not position.previous_pos.has_homed_position: + if not trigger_position.has_homed_position: state.is_waiting_for_homed_position = True logger.debug("LayerTrigger - Triggering - Waiting for the previous position to be homed.") - elif self.require_zhop and not position.current_pos.is_zhop: + elif self.require_zhop and not trigger_position.is_zhop: state.is_waiting_on_zhop = True logger.debug("LayerTrigger - Triggering - Waiting on ZHop.") elif not state.is_in_position and not state.in_path_position: @@ -750,9 +758,10 @@ def update(self, position): state.reset_state() state.is_triggered = False + # set the trigger position. It should be the previous position, not the current + trigger_position = position.previous_pos # Don't update the trigger if we don't have a homed axis - # Make sure to use the previous value so the homing operation can complete - if not position.current_pos.has_homed_position: + if not trigger_position.has_homed_position: state.is_triggered = False state.is_homed = False else: @@ -762,7 +771,7 @@ def update(self, position): current_time = time.time() # set is in position - state.is_in_position = position.current_pos.is_in_position and position.current_pos.is_in_bounds + state.is_in_position = trigger_position.is_in_position and trigger_position.is_in_bounds state.in_path_position = position.current_pos.in_path_position # if the trigger start time is null, set it now. @@ -790,11 +799,11 @@ def update(self, position): state.is_waiting = True # see if the exturder is in the right position - if position.is_extruder_triggered(self.extruder_triggers): - if not position.previous_pos.has_homed_position: + if position.is_previous_extruder_triggered(self.extruder_triggers): + if not trigger_position.has_homed_position: state.is_waiting_for_homed_position = True logger.debug("TimerTrigger - Triggering - Waiting for the previous position to be homed.") - if self.require_zhop and not position.current_pos.is_zhop: + if self.require_zhop and not trigger_position.is_zhop: logger.debug("TimerTrigger - Waiting on ZHop.") state.is_waiting_on_zhop = True elif not state.is_in_position and not state.in_path_position: From 9bb7faa45ae941e4117cf4411c135f83543ead4b Mon Sep 17 00:00:00 2001 From: FormerLurker Date: Sat, 17 Aug 2019 10:48:46 -0500 Subject: [PATCH 003/485] Add wiki images, rename slic3r-pe slicer type name to include all compatible slicers. --- .../slicer_type_dropdown_automatic.jpg | Bin 0 -> 32830 bytes octoprint_octolapse/settings.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 Extras/Wiki/assets/img/automatic_slicer_settings/slicer_type_dropdown_automatic.jpg diff --git a/Extras/Wiki/assets/img/automatic_slicer_settings/slicer_type_dropdown_automatic.jpg b/Extras/Wiki/assets/img/automatic_slicer_settings/slicer_type_dropdown_automatic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6eeeabfa27823479db7d9d67b1e8c8f82c5bcfa6 GIT binary patch literal 32830 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 S{ZDSq|J`x Date: Sat, 17 Aug 2019 10:58:41 -0500 Subject: [PATCH 004/485] Add wiki images for automatic settings extraction. --- .../cura_help_about.jpg | Bin 0 -> 24241 bytes .../cura_manage_printers.jpg | Bin 0 -> 24616 bytes .../automatic_slicer_settings/cura_version.jpg | Bin 0 -> 17476 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Extras/Wiki/assets/img/automatic_slicer_settings/cura_help_about.jpg create mode 100644 Extras/Wiki/assets/img/automatic_slicer_settings/cura_manage_printers.jpg create mode 100644 Extras/Wiki/assets/img/automatic_slicer_settings/cura_version.jpg diff --git a/Extras/Wiki/assets/img/automatic_slicer_settings/cura_help_about.jpg b/Extras/Wiki/assets/img/automatic_slicer_settings/cura_help_about.jpg new file mode 100644 index 0000000000000000000000000000000000000000..797492f34a552a1954e6e635f69ff1b095a919f8 GIT binary patch literal 24241 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 RzM|0h-?WSWm*0OJ{0~FhE+_y1 literal 0 HcmV?d00001 diff --git a/Extras/Wiki/assets/img/automatic_slicer_settings/cura_version.jpg b/Extras/Wiki/assets/img/automatic_slicer_settings/cura_version.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b3e2f42f11e6d459ad030f0d9672f83bab18a741 GIT binary patch literal 17476 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 literal 0 HcmV?d00001 From 282d0fd65df64f59f96a170d211f8700860c1db2 Mon Sep 17 00:00:00 2001 From: FormerLurker Date: Sat, 17 Aug 2019 11:14:27 -0500 Subject: [PATCH 005/485] Add wiki images for automatic settings extraction. --- .../cura_manage_printer.jpg | Bin 0 -> 24751 bytes .../automatic_slicer_settings/start_gcode.jpg | Bin 0 -> 25047 bytes .../start_gcode_cura_script_added.jpg | Bin 0 -> 62185 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Extras/Wiki/assets/img/automatic_slicer_settings/cura_manage_printer.jpg create mode 100644 Extras/Wiki/assets/img/automatic_slicer_settings/start_gcode.jpg create mode 100644 Extras/Wiki/assets/img/automatic_slicer_settings/start_gcode_cura_script_added.jpg diff --git a/Extras/Wiki/assets/img/automatic_slicer_settings/cura_manage_printer.jpg b/Extras/Wiki/assets/img/automatic_slicer_settings/cura_manage_printer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4f0e305aa2c79e2956685c82f75619e607fbf43b GIT binary patch literal 24751 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 literal 0 HcmV?d00001 diff --git a/Extras/Wiki/assets/img/automatic_slicer_settings/start_gcode_cura_script_added.jpg b/Extras/Wiki/assets/img/automatic_slicer_settings/start_gcode_cura_script_added.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ac4f8abfd4cc80734db8465fc1e3f502ee2ba7fd GIT binary patch literal 62185 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

_MSqYlUa{Ksg_@9NH3jlm+0w2=#ZZ?Q+ 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

+ // instead of: + //
  • - - a
+ // So, to prevent it, we will put a marker (¨A)in the beginning of the line + // Kind of hackish/monkey patching, but seems more effective than overcomplicating the list parser + item = item.replace(/^([-*+]|\d\.)[ \t]+[\S\n ]*/g, function (wm2) { + return '¨A' + wm2; + }); + + // m1 - Leading line or + // Has a double return (multi paragraph) or + // Has sublist + if (m1 || (item.search(/\n{2,}/) > -1)) { + item = showdown.subParser('githubCodeBlocks')(item, options, globals); + item = showdown.subParser('blockGamut')(item, options, globals); + } else { + // Recursion for sub-lists: + item = showdown.subParser('lists')(item, options, globals); + item = item.replace(/\n$/, ''); // chomp(item) + item = showdown.subParser('hashHTMLBlocks')(item, options, globals); + + // Colapse double linebreaks + item = item.replace(/\n\n+/g, '\n\n'); + if (isParagraphed) { + item = showdown.subParser('paragraphs')(item, options, globals); + } else { + item = showdown.subParser('spanGamut')(item, options, globals); + } + } + + // now we need to remove the marker (¨A) + item = item.replace('¨A', ''); + // we can finally wrap the line in list item tags + item = '' + item + '\n'; + + return item; + }); + + // attacklab: strip sentinel + listStr = listStr.replace(/¨0/g, ''); + + globals.gListLevel--; + + if (trimTrailing) { + listStr = listStr.replace(/\s+$/, ''); + } + + return listStr; + } + + function styleStartNumber (list, listType) { + // check if ol and starts by a number different than 1 + if (listType === 'ol') { + var res = list.match(/^ *(\d+)\./); + if (res && res[1] !== '1') { + return ' start="' + res[1] + '"'; + } + } + return ''; + } + + /** + * Check and parse consecutive lists (better fix for issue #142) + * @param {string} list + * @param {string} listType + * @param {boolean} trimTrailing + * @returns {string} + */ + function parseConsecutiveLists (list, listType, trimTrailing) { + // check if we caught 2 or more consecutive lists by mistake + // we use the counterRgx, meaning if listType is UL we look for OL and vice versa + var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm, + ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm, + counterRxg = (listType === 'ul') ? olRgx : ulRgx, + result = ''; + + if (list.search(counterRxg) !== -1) { + (function parseCL (txt) { + var pos = txt.search(counterRxg), + style = styleStartNumber(list, listType); + if (pos !== -1) { + // slice + result += '\n\n<' + listType + style + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '\n'; + + // invert counterType and listType + listType = (listType === 'ul') ? 'ol' : 'ul'; + counterRxg = (listType === 'ul') ? olRgx : ulRgx; + + //recurse + parseCL(txt.slice(pos)); + } else { + result += '\n\n<' + listType + style + '>\n' + processListItems(txt, !!trimTrailing) + '\n'; + } + })(list); + } else { + var style = styleStartNumber(list, listType); + result = '\n\n<' + listType + style + '>\n' + processListItems(list, !!trimTrailing) + '\n'; + } + + return result; + } + + /** Start of list parsing **/ + text = globals.converter._dispatch('lists.before', text, options, globals); + // add sentinel to hack around khtml/safari bug: + // http://bugs.webkit.org/show_bug.cgi?id=11231 + text += '¨0'; + + if (globals.gListLevel) { + text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm, + function (wholeMatch, list, m2) { + var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; + return parseConsecutiveLists(list, listType, true); + } + ); + } else { + text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm, + function (wholeMatch, m1, list, m3) { + var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; + return parseConsecutiveLists(list, listType, false); + } + ); + } + + // strip sentinel + text = text.replace(/¨0/, ''); + text = globals.converter._dispatch('lists.after', text, options, globals); + return text; +}); + +/** + * Parse metadata at the top of the document + */ +showdown.subParser('metadata', function (text, options, globals) { + 'use strict'; + + if (!options.metadata) { + return text; + } + + text = globals.converter._dispatch('metadata.before', text, options, globals); + + function parseMetadataContents (content) { + // raw is raw so it's not changed in any way + globals.metadata.raw = content; + + // escape chars forbidden in html attributes + // double quotes + content = content + // ampersand first + .replace(/&/g, '&') + // double quotes + .replace(/"/g, '"'); + + content = content.replace(/\n {4}/g, ' '); + content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) { + globals.metadata.parsed[key] = value; + return ''; + }); + } + + text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) { + parseMetadataContents(content); + return '¨M'; + }); + + text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) { + if (format) { + globals.metadata.format = format; + } + parseMetadataContents(content); + return '¨M'; + }); + + text = text.replace(/¨M/g, ''); + + text = globals.converter._dispatch('metadata.after', text, options, globals); + return text; +}); + +/** + * Remove one level of line-leading tabs or spaces + */ +showdown.subParser('outdent', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('outdent.before', text, options, globals); + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + text = text.replace(/^(\t|[ ]{1,4})/gm, '¨0'); // attacklab: g_tab_width + + // attacklab: clean up hack + text = text.replace(/¨0/g, ''); + + text = globals.converter._dispatch('outdent.after', text, options, globals); + return text; +}); + +/** + * + */ +showdown.subParser('paragraphs', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('paragraphs.before', text, options, globals); + // Strip leading and trailing lines: + text = text.replace(/^\n+/g, ''); + text = text.replace(/\n+$/g, ''); + + var grafs = text.split(/\n{2,}/g), + grafsOut = [], + end = grafs.length; // Wrap

tags + + for (var i = 0; i < end; i++) { + var str = grafs[i]; + // if this is an HTML marker, copy it + if (str.search(/¨(K|G)(\d+)\1/g) >= 0) { + grafsOut.push(str); + + // test for presence of characters to prevent empty lines being parsed + // as paragraphs (resulting in undesired extra empty paragraphs) + } else if (str.search(/\S/) >= 0) { + str = showdown.subParser('spanGamut')(str, options, globals); + str = str.replace(/^([ \t]*)/g, '

'); + str += '

'; + grafsOut.push(str); + } + } + + /** Unhashify HTML blocks */ + end = grafsOut.length; + for (i = 0; i < end; i++) { + var blockText = '', + grafsOutIt = grafsOut[i], + codeFlag = false; + // if this is a marker for an html block... + // use RegExp.test instead of string.search because of QML bug + while (/¨(K|G)(\d+)\1/.test(grafsOutIt)) { + var delim = RegExp.$1, + num = RegExp.$2; + + if (delim === 'K') { + blockText = globals.gHtmlBlocks[num]; + } else { + // we need to check if ghBlock is a false positive + if (codeFlag) { + // use encoded version of all text + blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text, options, globals); + } else { + blockText = globals.ghCodeBlocks[num].codeblock; + } + } + blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs + + grafsOutIt = grafsOutIt.replace(/(\n\n)?¨(K|G)\d+\2(\n\n)?/, blockText); + // Check if grafsOutIt is a pre->code + if (/^]*>\s*]*>/.test(grafsOutIt)) { + codeFlag = true; + } + } + grafsOut[i] = grafsOutIt; + } + text = grafsOut.join('\n'); + // Strip leading and trailing lines: + text = text.replace(/^\n+/g, ''); + text = text.replace(/\n+$/g, ''); + return globals.converter._dispatch('paragraphs.after', text, options, globals); +}); + +/** + * Run extension + */ +showdown.subParser('runExtension', function (ext, text, options, globals) { + 'use strict'; + + if (ext.filter) { + text = ext.filter(text, globals.converter, options); + + } else if (ext.regex) { + // TODO remove this when old extension loading mechanism is deprecated + var re = ext.regex; + if (!(re instanceof RegExp)) { + re = new RegExp(re, 'g'); + } + text = text.replace(re, ext.replace); + } + + return text; +}); + +/** + * These are all the transformations that occur *within* block-level + * tags like paragraphs, headers, and list items. + */ +showdown.subParser('spanGamut', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('spanGamut.before', text, options, globals); + text = showdown.subParser('codeSpans')(text, options, globals); + text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals); + text = showdown.subParser('encodeBackslashEscapes')(text, options, globals); + + // Process anchor and image tags. Images must come first, + // because ![foo][f] looks like an anchor. + text = showdown.subParser('images')(text, options, globals); + text = showdown.subParser('anchors')(text, options, globals); + + // Make links out of things like `` + // Must come after anchors, because you can use < and > + // delimiters in inline links like [this](). + text = showdown.subParser('autoLinks')(text, options, globals); + text = showdown.subParser('simplifiedAutoLinks')(text, options, globals); + text = showdown.subParser('emoji')(text, options, globals); + text = showdown.subParser('underline')(text, options, globals); + text = showdown.subParser('italicsAndBold')(text, options, globals); + text = showdown.subParser('strikethrough')(text, options, globals); + text = showdown.subParser('ellipsis')(text, options, globals); + + // we need to hash HTML tags inside spans + text = showdown.subParser('hashHTMLSpans')(text, options, globals); + + // now we encode amps and angles + text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals); + + // Do hard breaks + if (options.simpleLineBreaks) { + // GFM style hard breaks + // only add line breaks if the text does not contain a block (special case for lists) + if (!/\n\n¨K/.test(text)) { + text = text.replace(/\n+/g, '
\n'); + } + } else { + // Vanilla hard breaks + text = text.replace(/ +\n/g, '
\n'); + } + + text = globals.converter._dispatch('spanGamut.after', text, options, globals); + return text; +}); + +showdown.subParser('strikethrough', function (text, options, globals) { + 'use strict'; + + function parseInside (txt) { + if (options.simplifiedAutoLink) { + txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals); + } + return '' + txt + ''; + } + + if (options.strikethrough) { + text = globals.converter._dispatch('strikethrough.before', text, options, globals); + text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return parseInside(txt); }); + text = globals.converter._dispatch('strikethrough.after', text, options, globals); + } + + return text; +}); + +/** + * Strips link definitions from text, stores the URLs and titles in + * hash references. + * Link defs are in the form: ^[id]: url "optional title" + */ +showdown.subParser('stripLinkDefinitions', function (text, options, globals) { + 'use strict'; + + var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm, + base64Regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm; + + // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug + text += '¨0'; + + var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) { + linkId = linkId.toLowerCase(); + if (url.match(/^data:.+?\/.+?;base64,/)) { + // remove newlines + globals.gUrls[linkId] = url.replace(/\s/g, ''); + } else { + globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive + } + + if (blankLines) { + // Oops, found blank lines, so it's not a title. + // Put back the parenthetical statement we stole. + return blankLines + title; + + } else { + if (title) { + globals.gTitles[linkId] = title.replace(/"|'/g, '"'); + } + if (options.parseImgDimensions && width && height) { + globals.gDimensions[linkId] = { + width: width, + height: height + }; + } + } + // Completely remove the definition from the text + return ''; + }; + + // first we try to find base64 link references + text = text.replace(base64Regex, replaceFunc); + + text = text.replace(regex, replaceFunc); + + // attacklab: strip sentinel + text = text.replace(/¨0/, ''); + + return text; +}); + +showdown.subParser('tables', function (text, options, globals) { + 'use strict'; + + if (!options.tables) { + return text; + } + + var tableRgx = /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm, + //singeColTblRgx = /^ {0,3}\|.+\|\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n(?: {0,3}\|.+\|\n)+(?:\n\n|¨0)/gm; + singeColTblRgx = /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm; + + function parseStyles (sLine) { + if (/^:[ \t]*--*$/.test(sLine)) { + return ' style="text-align:left;"'; + } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) { + return ' style="text-align:right;"'; + } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) { + return ' style="text-align:center;"'; + } else { + return ''; + } + } + + function parseHeaders (header, style) { + var id = ''; + header = header.trim(); + // support both tablesHeaderId and tableHeaderId due to error in documentation so we don't break backwards compatibility + if (options.tablesHeaderId || options.tableHeaderId) { + id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; + } + header = showdown.subParser('spanGamut')(header, options, globals); + + return '' + header + '\n'; + } + + function parseCells (cell, style) { + var subText = showdown.subParser('spanGamut')(cell, options, globals); + return '' + subText + '\n'; + } + + function buildTable (headers, cells) { + var tb = '\n\n\n', + tblLgn = headers.length; + + for (var i = 0; i < tblLgn; ++i) { + tb += headers[i]; + } + tb += '\n\n\n'; + + for (i = 0; i < cells.length; ++i) { + tb += '\n'; + for (var ii = 0; ii < tblLgn; ++ii) { + tb += cells[i][ii]; + } + tb += '\n'; + } + tb += '\n
\n'; + return tb; + } + + function parseTable (rawTable) { + var i, tableLines = rawTable.split('\n'); + + for (i = 0; i < tableLines.length; ++i) { + // strip wrong first and last column if wrapped tables are used + if (/^ {0,3}\|/.test(tableLines[i])) { + tableLines[i] = tableLines[i].replace(/^ {0,3}\|/, ''); + } + if (/\|[ \t]*$/.test(tableLines[i])) { + tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, ''); + } + // parse code spans first, but we only support one line code spans + tableLines[i] = showdown.subParser('codeSpans')(tableLines[i], options, globals); + } + + var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}), + rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}), + rawCells = [], + headers = [], + styles = [], + cells = []; + + tableLines.shift(); + tableLines.shift(); + + for (i = 0; i < tableLines.length; ++i) { + if (tableLines[i].trim() === '') { + continue; + } + rawCells.push( + tableLines[i] + .split('|') + .map(function (s) { + return s.trim(); + }) + ); + } + + if (rawHeaders.length < rawStyles.length) { + return rawTable; + } + + for (i = 0; i < rawStyles.length; ++i) { + styles.push(parseStyles(rawStyles[i])); + } + + for (i = 0; i < rawHeaders.length; ++i) { + if (showdown.helper.isUndefined(styles[i])) { + styles[i] = ''; + } + headers.push(parseHeaders(rawHeaders[i], styles[i])); + } + + for (i = 0; i < rawCells.length; ++i) { + var row = []; + for (var ii = 0; ii < headers.length; ++ii) { + if (showdown.helper.isUndefined(rawCells[i][ii])) { + + } + row.push(parseCells(rawCells[i][ii], styles[ii])); + } + cells.push(row); + } + + return buildTable(headers, cells); + } + + text = globals.converter._dispatch('tables.before', text, options, globals); + + // find escaped pipe characters + text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback); + + // parse multi column tables + text = text.replace(tableRgx, parseTable); + + // parse one column tables + text = text.replace(singeColTblRgx, parseTable); + + text = globals.converter._dispatch('tables.after', text, options, globals); + + return text; +}); + +showdown.subParser('underline', function (text, options, globals) { + 'use strict'; + + if (!options.underline) { + return text; + } + + text = globals.converter._dispatch('underline.before', text, options, globals); + + if (options.literalMidWordUnderscores) { + text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) { + return '' + txt + ''; + }); + text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) { + return '' + txt + ''; + }); + } else { + text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) { + return (/\S$/.test(m)) ? '' + m + '' : wm; + }); + text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) { + return (/\S$/.test(m)) ? '' + m + '' : wm; + }); + } + + // escape remaining underscores to prevent them being parsed by italic and bold + text = text.replace(/(_)/g, showdown.helper.escapeCharactersCallback); + + text = globals.converter._dispatch('underline.after', text, options, globals); + + return text; +}); + +/** + * Swap back in all the special characters we've hidden. + */ +showdown.subParser('unescapeSpecialChars', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('unescapeSpecialChars.before', text, options, globals); + + text = text.replace(/¨E(\d+)E/g, function (wholeMatch, m1) { + var charCodeToReplace = parseInt(m1); + return String.fromCharCode(charCodeToReplace); + }); + + text = globals.converter._dispatch('unescapeSpecialChars.after', text, options, globals); + return text; +}); + +showdown.subParser('makeMarkdown.blockquote', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + var children = node.childNodes, + childrenLength = children.length; + + for (var i = 0; i < childrenLength; ++i) { + var innerTxt = showdown.subParser('makeMarkdown.node')(children[i], globals); + + if (innerTxt === '') { + continue; + } + txt += innerTxt; + } + } + // cleanup + txt = txt.trim(); + txt = '> ' + txt.split('\n').join('\n> '); + return txt; +}); + +showdown.subParser('makeMarkdown.codeBlock', function (node, globals) { + 'use strict'; + + var lang = node.getAttribute('language'), + num = node.getAttribute('precodenum'); + return '```' + lang + '\n' + globals.preList[num] + '\n```'; +}); + +showdown.subParser('makeMarkdown.codeSpan', function (node) { + 'use strict'; + + return '`' + node.innerHTML + '`'; +}); + +showdown.subParser('makeMarkdown.emphasis', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + txt += '*'; + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += '*'; + } + return txt; +}); + +showdown.subParser('makeMarkdown.header', function (node, globals, headerLevel) { + 'use strict'; + + var headerMark = new Array(headerLevel + 1).join('#'), + txt = ''; + + if (node.hasChildNodes()) { + txt = headerMark + ' '; + var children = node.childNodes, + childrenLength = children.length; + + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + } + return txt; +}); + +showdown.subParser('makeMarkdown.hr', function () { + 'use strict'; + + return '---'; +}); + +showdown.subParser('makeMarkdown.image', function (node) { + 'use strict'; + + var txt = ''; + if (node.hasAttribute('src')) { + txt += '![' + node.getAttribute('alt') + ']('; + txt += '<' + node.getAttribute('src') + '>'; + if (node.hasAttribute('width') && node.hasAttribute('height')) { + txt += ' =' + node.getAttribute('width') + 'x' + node.getAttribute('height'); + } + + if (node.hasAttribute('title')) { + txt += ' "' + node.getAttribute('title') + '"'; + } + txt += ')'; + } + return txt; +}); + +showdown.subParser('makeMarkdown.links', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes() && node.hasAttribute('href')) { + var children = node.childNodes, + childrenLength = children.length; + txt = '['; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += ']('; + txt += '<' + node.getAttribute('href') + '>'; + if (node.hasAttribute('title')) { + txt += ' "' + node.getAttribute('title') + '"'; + } + txt += ')'; + } + return txt; +}); + +showdown.subParser('makeMarkdown.list', function (node, globals, type) { + 'use strict'; + + var txt = ''; + if (!node.hasChildNodes()) { + return ''; + } + var listItems = node.childNodes, + listItemsLenght = listItems.length, + listNum = node.getAttribute('start') || 1; + + for (var i = 0; i < listItemsLenght; ++i) { + if (typeof listItems[i].tagName === 'undefined' || listItems[i].tagName.toLowerCase() !== 'li') { + continue; + } + + // define the bullet to use in list + var bullet = ''; + if (type === 'ol') { + bullet = listNum.toString() + '. '; + } else { + bullet = '- '; + } + + // parse list item + txt += bullet + showdown.subParser('makeMarkdown.listItem')(listItems[i], globals); + ++listNum; + } + + // add comment at the end to prevent consecutive lists to be parsed as one + txt += '\n\n'; + return txt.trim(); +}); + +showdown.subParser('makeMarkdown.listItem', function (node, globals) { + 'use strict'; + + var listItemTxt = ''; + + var children = node.childNodes, + childrenLenght = children.length; + + for (var i = 0; i < childrenLenght; ++i) { + listItemTxt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + // if it's only one liner, we need to add a newline at the end + if (!/\n$/.test(listItemTxt)) { + listItemTxt += '\n'; + } else { + // it's multiparagraph, so we need to indent + listItemTxt = listItemTxt + .split('\n') + .join('\n ') + .replace(/^ {4}$/gm, '') + .replace(/\n\n+/g, '\n\n'); + } + + return listItemTxt; +}); + + + +showdown.subParser('makeMarkdown.node', function (node, globals, spansOnly) { + 'use strict'; + + spansOnly = spansOnly || false; + + var txt = ''; + + // edge case of text without wrapper paragraph + if (node.nodeType === 3) { + return showdown.subParser('makeMarkdown.txt')(node, globals); + } + + // HTML comment + if (node.nodeType === 8) { + return '\n\n'; + } + + // process only node elements + if (node.nodeType !== 1) { + return ''; + } + + var tagName = node.tagName.toLowerCase(); + + switch (tagName) { + + // + // BLOCKS + // + case 'h1': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 1) + '\n\n'; } + break; + case 'h2': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 2) + '\n\n'; } + break; + case 'h3': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 3) + '\n\n'; } + break; + case 'h4': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 4) + '\n\n'; } + break; + case 'h5': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 5) + '\n\n'; } + break; + case 'h6': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 6) + '\n\n'; } + break; + + case 'p': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.paragraph')(node, globals) + '\n\n'; } + break; + + case 'blockquote': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.blockquote')(node, globals) + '\n\n'; } + break; + + case 'hr': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.hr')(node, globals) + '\n\n'; } + break; + + case 'ol': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ol') + '\n\n'; } + break; + + case 'ul': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ul') + '\n\n'; } + break; + + case 'precode': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.codeBlock')(node, globals) + '\n\n'; } + break; + + case 'pre': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.pre')(node, globals) + '\n\n'; } + break; + + case 'table': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.table')(node, globals) + '\n\n'; } + break; + + // + // SPANS + // + case 'code': + txt = showdown.subParser('makeMarkdown.codeSpan')(node, globals); + break; + + case 'em': + case 'i': + txt = showdown.subParser('makeMarkdown.emphasis')(node, globals); + break; + + case 'strong': + case 'b': + txt = showdown.subParser('makeMarkdown.strong')(node, globals); + break; + + case 'del': + txt = showdown.subParser('makeMarkdown.strikethrough')(node, globals); + break; + + case 'a': + txt = showdown.subParser('makeMarkdown.links')(node, globals); + break; + + case 'img': + txt = showdown.subParser('makeMarkdown.image')(node, globals); + break; + + default: + txt = node.outerHTML + '\n\n'; + } + + // common normalization + // TODO eventually + + return txt; +}); + +showdown.subParser('makeMarkdown.paragraph', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + } + + // some text normalization + txt = txt.trim(); + + return txt; +}); + +showdown.subParser('makeMarkdown.pre', function (node, globals) { + 'use strict'; + + var num = node.getAttribute('prenum'); + return '
' + globals.preList[num] + '
'; +}); + +showdown.subParser('makeMarkdown.strikethrough', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + txt += '~~'; + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += '~~'; + } + return txt; +}); + +showdown.subParser('makeMarkdown.strong', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + txt += '**'; + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += '**'; + } + return txt; +}); + +showdown.subParser('makeMarkdown.table', function (node, globals) { + 'use strict'; + + var txt = '', + tableArray = [[], []], + headings = node.querySelectorAll('thead>tr>th'), + rows = node.querySelectorAll('tbody>tr'), + i, ii; + for (i = 0; i < headings.length; ++i) { + var headContent = showdown.subParser('makeMarkdown.tableCell')(headings[i], globals), + allign = '---'; + + if (headings[i].hasAttribute('style')) { + var style = headings[i].getAttribute('style').toLowerCase().replace(/\s/g, ''); + switch (style) { + case 'text-align:left;': + allign = ':---'; + break; + case 'text-align:right;': + allign = '---:'; + break; + case 'text-align:center;': + allign = ':---:'; + break; + } + } + tableArray[0][i] = headContent.trim(); + tableArray[1][i] = allign; + } + + for (i = 0; i < rows.length; ++i) { + var r = tableArray.push([]) - 1, + cols = rows[i].getElementsByTagName('td'); + + for (ii = 0; ii < headings.length; ++ii) { + var cellContent = ' '; + if (typeof cols[ii] !== 'undefined') { + cellContent = showdown.subParser('makeMarkdown.tableCell')(cols[ii], globals); + } + tableArray[r].push(cellContent); + } + } + + var cellSpacesCount = 3; + for (i = 0; i < tableArray.length; ++i) { + for (ii = 0; ii < tableArray[i].length; ++ii) { + var strLen = tableArray[i][ii].length; + if (strLen > cellSpacesCount) { + cellSpacesCount = strLen; + } + } + } + + for (i = 0; i < tableArray.length; ++i) { + for (ii = 0; ii < tableArray[i].length; ++ii) { + if (i === 1) { + if (tableArray[i][ii].slice(-1) === ':') { + tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii].slice(-1), cellSpacesCount - 1, '-') + ':'; + } else { + tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount, '-'); + } + } else { + tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount); + } + } + txt += '| ' + tableArray[i].join(' | ') + ' |\n'; + } + + return txt.trim(); +}); + +showdown.subParser('makeMarkdown.tableCell', function (node, globals) { + 'use strict'; + + var txt = ''; + if (!node.hasChildNodes()) { + return ''; + } + var children = node.childNodes, + childrenLength = children.length; + + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals, true); + } + return txt.trim(); +}); + +showdown.subParser('makeMarkdown.txt', function (node) { + 'use strict'; + + var txt = node.nodeValue; + + // multiple spaces are collapsed + txt = txt.replace(/ +/g, ' '); + + // replace the custom ¨NBSP; with a space + txt = txt.replace(/¨NBSP;/g, ' '); + + // ", <, > and & should replace escaped html entities + txt = showdown.helper.unescapeHTMLEntities(txt); + + // escape markdown magic characters + // emphasis, strong and strikethrough - can appear everywhere + // we also escape pipe (|) because of tables + // and escape ` because of code blocks and spans + txt = txt.replace(/([*_~|`])/g, '\\$1'); + + // escape > because of blockquotes + txt = txt.replace(/^(\s*)>/g, '\\$1>'); + + // hash character, only troublesome at the beginning of a line because of headers + txt = txt.replace(/^#/gm, '\\#'); + + // horizontal rules + txt = txt.replace(/^(\s*)([-=]{3,})(\s*)$/, '$1\\$2$3'); + + // dot, because of ordered lists, only troublesome at the beginning of a line when preceded by an integer + txt = txt.replace(/^( {0,3}\d+)\./gm, '$1\\.'); + + // +, * and -, at the beginning of a line becomes a list, so we need to escape them also (asterisk was already escaped) + txt = txt.replace(/^( {0,3})([+-])/gm, '$1\\$2'); + + // images and links, ] followed by ( is problematic, so we escape it + txt = txt.replace(/]([\s]*)\(/g, '\\]$1\\('); + + // reference URIs must also be escaped + txt = txt.replace(/^ {0,3}\[([\S \t]*?)]:/gm, '\\[$1]:'); + + return txt; +}); + +var root = this; + +// AMD Loader +if (typeof define === 'function' && define.amd) { + define(function () { + 'use strict'; + return showdown; + }); + +// CommonJS/nodeJS Loader +} else if (typeof module !== 'undefined' && module.exports) { + module.exports = showdown; + +// Regular Browser loader +} else { + root.showdown = showdown; +} +}).call(this); + +//# sourceMappingURL=showdown.js.map diff --git a/octoprint_octolapse_setuptools/__init__.py b/octoprint_octolapse_setuptools/__init__.py index 5995ab41..e3602e27 100644 --- a/octoprint_octolapse_setuptools/__init__.py +++ b/octoprint_octolapse_setuptools/__init__.py @@ -29,7 +29,7 @@ class NumberedVersion(version.LooseVersion): # This is the current plugin version, not including any versioneer info, # which could be earlier or later - CurrentVersion = "0.4.1" + CurrentVersion = "0.4.2" # This is the CurrentVersion last time the settings were migrated. CurrentSettingsVersion = "0.4.0" ''' From d33011eda4bf1dead224d6a897927eaba117be2c Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Tue, 23 Nov 2021 14:39:36 -0600 Subject: [PATCH 462/485] Drop version bump - Need to wait until I push an RC --- octoprint_octolapse_setuptools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octoprint_octolapse_setuptools/__init__.py b/octoprint_octolapse_setuptools/__init__.py index e3602e27..5995ab41 100644 --- a/octoprint_octolapse_setuptools/__init__.py +++ b/octoprint_octolapse_setuptools/__init__.py @@ -29,7 +29,7 @@ class NumberedVersion(version.LooseVersion): # This is the current plugin version, not including any versioneer info, # which could be earlier or later - CurrentVersion = "0.4.2" + CurrentVersion = "0.4.1" # This is the CurrentVersion last time the settings were migrated. CurrentSettingsVersion = "0.4.0" ''' From a4b638dcecc2744c2d1428fc2a43e680987b407b Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Tue, 23 Nov 2021 16:43:50 -0600 Subject: [PATCH 463/485] Support comment sections in PrusaSlicer and SuperSlicer 2.3+, maintain backward compatibility. --- .../data/lib/c/gcode_comment_processor.cpp | 609 ++++++++++-------- .../data/lib/c/gcode_comment_processor.h | 21 +- 2 files changed, 371 insertions(+), 259 deletions(-) diff --git a/octoprint_octolapse/data/lib/c/gcode_comment_processor.cpp b/octoprint_octolapse/data/lib/c/gcode_comment_processor.cpp index 05366aed..4b9906cc 100644 --- a/octoprint_octolapse/data/lib/c/gcode_comment_processor.cpp +++ b/octoprint_octolapse/data/lib/c/gcode_comment_processor.cpp @@ -2,8 +2,10 @@ #include "utilities.h" gcode_comment_processor::gcode_comment_processor() { - current_section_ = section_type_no_section; - processing_type_ = comment_process_type_unknown; + current_section_ = section_type_no_section; + current_subsection_ = subsection_type_none; + processing_type_ = comment_process_type_unknown; + } gcode_comment_processor::~gcode_comment_processor() @@ -12,302 +14,399 @@ gcode_comment_processor::~gcode_comment_processor() comment_process_type gcode_comment_processor::get_comment_process_type() { - return processing_type_; + return processing_type_; } void gcode_comment_processor::update(position& pos) { - if (processing_type_ == comment_process_type_off) - return; + if (processing_type_ == comment_process_type_off) + return; - if (current_section_ != section_type_no_section) - { - update_feature_from_section(pos); - return; - } + if (current_section_ != section_type_no_section) + { + update_feature_from_section(pos); + return; + } - if (processing_type_ == comment_process_type_unknown || processing_type_ == comment_process_type_slic3r_pe) - { - if (update_feature_for_slic3r_pe_comment(pos, pos.command.comment)) - processing_type_ = comment_process_type_slic3r_pe; - } + if (processing_type_ == comment_process_type_unknown || processing_type_ == comment_process_type_slic3r_pe) + { + if (update_feature_for_slic3r_pe_comment(pos, pos.command.comment)) + processing_type_ = comment_process_type_slic3r_pe; + } } bool gcode_comment_processor::update_feature_for_slic3r_pe_comment(position& pos, std::string& comment) const { - // External Perimeter - SuperSlicer - // Also, adding overhang perimeter here too. - if (utilities::is_in_caseless_trim(comment, SLICER_PE_OUTER_PERIMETER_COMMENTS)) - { - pos.feature_type_tag = feature_type_outer_perimeter_feature; - return true; - } - // Internal Perimeter - SuperSlicer - if (utilities::is_in_caseless_trim(comment, SLICER_PE_INNER_PERIMETER_COMMENTS)) - { - pos.feature_type_tag = feature_type_inner_perimeter_feature; - return true; - } - if (utilities::is_in_caseless_trim(comment, SLICER_PE_UNKNOWN_PERIMETER_COMMENTS)) - { - pos.feature_type_tag = feature_type_unknown_perimeter_feature; - return true; - } + // External Perimeter - SuperSlicer + // Also, adding overhang perimeter here too. + if (utilities::is_in_caseless_trim(comment, SLICER_PE_OUTER_PERIMETER_COMMENTS)) + { + pos.feature_type_tag = feature_type_outer_perimeter_feature; + return true; + } + // Internal Perimeter - SuperSlicer + if (utilities::is_in_caseless_trim(comment, SLICER_PE_INNER_PERIMETER_COMMENTS)) + { + pos.feature_type_tag = feature_type_inner_perimeter_feature; + return true; + } + if (utilities::is_in_caseless_trim(comment, SLICER_PE_UNKNOWN_PERIMETER_COMMENTS)) + { + pos.feature_type_tag = feature_type_unknown_perimeter_feature; + return true; + } - if (utilities::is_in_caseless_trim(comment, SLICER_PE_INFILL_COMMENTS)) - { - pos.feature_type_tag = feature_type_infill_feature; - return true; - } - // Solid Infill/Top Solid Infill - SuperSlicer - if (utilities::is_in_caseless_trim(comment, SLICER_PE_SOLID_INFILL_COMMENTS)) - { - pos.feature_type_tag = feature_type_solid_infill_feature; - return true; - } + if (utilities::is_in_caseless_trim(comment, SLICER_PE_INFILL_COMMENTS)) + { + pos.feature_type_tag = feature_type_infill_feature; + return true; + } + // Solid Infill/Top Solid Infill - SuperSlicer + if (utilities::is_in_caseless_trim(comment, SLICER_PE_SOLID_INFILL_COMMENTS)) + { + pos.feature_type_tag = feature_type_solid_infill_feature; + return true; + } - // Gap Fill - SuperSlicer - if (utilities::is_in_caseless_trim(comment, SLICER_PE_GAP_FILL_COMMENTS)) - { - pos.feature_type_tag = feature_type_gap_fill_feature; - return true; - } + // Gap Fill - SuperSlicer + if (utilities::is_in_caseless_trim(comment, SLICER_PE_GAP_FILL_COMMENTS)) + { + pos.feature_type_tag = feature_type_gap_fill_feature; + return true; + } - // bridge infill - SuperSlicer - if (utilities::is_in_caseless_trim(comment, SLICER_PE_BRIDGE_COMMENTS)) - { - pos.feature_type_tag = feature_type_bridge_feature; - return true; - } + // bridge infill - SuperSlicer + if (utilities::is_in_caseless_trim(comment, SLICER_PE_BRIDGE_COMMENTS)) + { + pos.feature_type_tag = feature_type_bridge_feature; + return true; + } - if (utilities::is_in_caseless_trim(comment, SLICER_PE_SKIRT_COMMENTS)) - { - pos.feature_type_tag = feature_type_skirt_feature; - return true; - } - return false; + if (utilities::is_in_caseless_trim(comment, SLICER_PE_SKIRT_COMMENTS)) + { + pos.feature_type_tag = feature_type_skirt_feature; + return true; + } + return false; } void gcode_comment_processor::update_feature_from_section(position& pos) const { - if (processing_type_ == comment_process_type_off || current_section_ == section_type_no_section) - return; + // Don't update if we're not processing comments, if we have no section, or if we are in a wipe subsection. + if (processing_type_ == comment_process_type_off || current_section_ == section_type_no_section || current_subsection_ == subsection_type_wipe) + return; - switch (current_section_) - { - case(section_type_outer_perimeter_section): - pos.feature_type_tag = feature_type_outer_perimeter_feature; - break; - case(section_type_inner_perimeter_section): - pos.feature_type_tag = feature_type_inner_perimeter_feature; - break; - case(section_type_skirt_section): - pos.feature_type_tag = feature_type_skirt_feature; - break; - case(section_type_solid_infill_section): - pos.feature_type_tag = feature_type_solid_infill_feature; - break; - case(section_type_ooze_shield_section): - pos.feature_type_tag = feature_type_ooze_shield_feature; - break; - case(section_type_infill_section): - pos.feature_type_tag = feature_type_infill_feature; - break; - case(section_type_prime_pillar_section): - pos.feature_type_tag = feature_type_prime_pillar_feature; - break; - case(section_type_gap_fill_section): - pos.feature_type_tag = feature_type_gap_fill_feature; - } + switch (current_section_) + { + case(section_type_outer_perimeter_section): + pos.feature_type_tag = feature_type_outer_perimeter_feature; + break; + case(section_type_inner_perimeter_section): + pos.feature_type_tag = feature_type_inner_perimeter_feature; + break; + case(section_type_skirt_section): + pos.feature_type_tag = feature_type_skirt_feature; + break; + case(section_type_solid_infill_section): + pos.feature_type_tag = feature_type_solid_infill_feature; + break; + case(section_type_ooze_shield_section): + pos.feature_type_tag = feature_type_ooze_shield_feature; + break; + case(section_type_infill_section): + pos.feature_type_tag = feature_type_infill_feature; + break; + case(section_type_prime_pillar_section): + pos.feature_type_tag = feature_type_prime_pillar_feature; + break; + case(section_type_gap_fill_section): + pos.feature_type_tag = feature_type_gap_fill_feature; + break; + case (section_type_bridge_section): + pos.feature_type_tag = feature_type_bridge_feature; + break; + case (section_type_support_section): + pos.feature_type_tag = feature_type_support_feature; + } } void gcode_comment_processor::update(std::string& comment) { - switch (processing_type_) - { - case comment_process_type_off: - break; - case comment_process_type_unknown: - update_unknown_section(comment); - break; - case comment_process_type_cura: - update_cura_section(comment); - break; - case comment_process_type_slic3r_pe: - update_slic3r_pe_section(comment); - break; - case comment_process_type_simplify_3d: - update_simplify_3d_section(comment); - break; - } + switch (processing_type_) + { + case comment_process_type_off: + break; + case comment_process_type_unknown: + update_unknown_section(comment); + break; + case comment_process_type_cura: + update_cura_section(comment); + break; + case comment_process_type_slic3r_pe: + update_slic3r_pe_section(comment); + break; + case comment_process_type_simplify_3d: + update_simplify_3d_section(comment); + break; + } } void gcode_comment_processor::update_unknown_section(std::string& comment) { - if (comment.length() == 0) - return; + if (comment.length() == 0) + return; - if (update_cura_section(comment)) - { - processing_type_ = comment_process_type_cura; - return; - } + if (update_cura_section(comment)) + { + processing_type_ = comment_process_type_cura; + return; + } - if (update_simplify_3d_section(comment)) - { - processing_type_ = comment_process_type_simplify_3d; - return; - } - if (update_slic3r_pe_section(comment)) - { - processing_type_ = comment_process_type_slic3r_pe; - return; - } + if (update_simplify_3d_section(comment)) + { + processing_type_ = comment_process_type_simplify_3d; + return; + } + if (update_slic3r_pe_section(comment)) + { + processing_type_ = comment_process_type_slic3r_pe; + return; + } } bool gcode_comment_processor::update_cura_section(std::string& comment) { - if (comment == "TYPE:WALL-OUTER") - { - current_section_ = section_type_outer_perimeter_section; - return true; - } - else if (comment == "TYPE:WALL-INNER") - { - current_section_ = section_type_inner_perimeter_section; - return true; - } - if (comment == "TYPE:FILL") - { - current_section_ = section_type_infill_section; - return true; - } - if (comment == "TYPE:SKIN") - { - current_section_ = section_type_solid_infill_section; - return true; - } - if (comment.rfind("LAYER:", 0) != std::string::npos || comment.rfind(";MESH:NONMESH", 0) != std::string::npos) - { - current_section_ = section_type_no_section; - return false; - } - if (comment == "TYPE:SKIRT") - { - current_section_ = section_type_skirt_section; - return true; - } - return false; + if (comment == "TYPE:WALL-OUTER") + { + current_section_ = section_type_outer_perimeter_section; + return true; + } + else if (comment == "TYPE:WALL-INNER") + { + current_section_ = section_type_inner_perimeter_section; + return true; + } + if (comment == "TYPE:FILL") + { + current_section_ = section_type_infill_section; + return true; + } + if (comment == "TYPE:SKIN") + { + current_section_ = section_type_solid_infill_section; + return true; + } + if (comment.rfind("LAYER:", 0) != std::string::npos || comment.rfind(";MESH:NONMESH", 0) != std::string::npos) + { + current_section_ = section_type_no_section; + return false; + } + if (comment == "TYPE:SKIRT") + { + current_section_ = section_type_skirt_section; + return true; + } + return false; } bool gcode_comment_processor::update_simplify_3d_section(std::string& comment) { - // Apparently simplify 3d added the word 'feature' to the their feature comments - // at some point to make my life more difficult :P - if (comment.rfind("feature", 0) != std::string::npos) - { - if (comment == "feature outer perimeter") - { - current_section_ = section_type_outer_perimeter_section; - return true; - } - if (comment == "feature inner perimeter") - { - current_section_ = section_type_inner_perimeter_section; - return true; - } - if (comment == "feature infill") - { - current_section_ = section_type_infill_section; - return true; - } - if (comment == "feature solid layer") - { - current_section_ = section_type_solid_infill_section; - return true; - } - if (comment == "feature skirt") - { - current_section_ = section_type_skirt_section; - return true; - } - if (comment == "feature ooze shield") - { - current_section_ = section_type_ooze_shield_section; - return true; - } - if (comment == "feature prime pillar") - { - current_section_ = section_type_prime_pillar_section; - return true; - } - if (comment == "feature gap fill") - { - current_section_ = section_type_gap_fill_section; - return true; - } - } - else - { - if (comment == "outer perimeter") - { - current_section_ = section_type_outer_perimeter_section; - return true; - } - if (comment == "inner perimeter") - { - current_section_ = section_type_inner_perimeter_section; - return true; - } - if (comment == "infill") - { - current_section_ = section_type_infill_section; - return true; - } - if (comment == "solid layer") - { - current_section_ = section_type_solid_infill_section; - return true; - } - if (comment == "skirt") - { - current_section_ = section_type_skirt_section; - return true; - } - if (comment == "ooze shield") - { - current_section_ = section_type_ooze_shield_section; - return true; - } + // Apparently simplify 3d added the word 'feature' to the their feature comments + // at some point to make my life more difficult :P + if (comment.rfind("feature", 0) != std::string::npos) + { + if (comment == "feature outer perimeter") + { + current_section_ = section_type_outer_perimeter_section; + return true; + } + if (comment == "feature inner perimeter") + { + current_section_ = section_type_inner_perimeter_section; + return true; + } + if (comment == "feature infill") + { + current_section_ = section_type_infill_section; + return true; + } + if (comment == "feature solid layer") + { + current_section_ = section_type_solid_infill_section; + return true; + } + if (comment == "feature skirt") + { + current_section_ = section_type_skirt_section; + return true; + } + if (comment == "feature ooze shield") + { + current_section_ = section_type_ooze_shield_section; + return true; + } + if (comment == "feature prime pillar") + { + current_section_ = section_type_prime_pillar_section; + return true; + } + if (comment == "feature gap fill") + { + current_section_ = section_type_gap_fill_section; + return true; + } + } + else + { + if (comment == "outer perimeter") + { + current_section_ = section_type_outer_perimeter_section; + return true; + } + if (comment == "inner perimeter") + { + current_section_ = section_type_inner_perimeter_section; + return true; + } + if (comment == "infill") + { + current_section_ = section_type_infill_section; + return true; + } + if (comment == "solid layer") + { + current_section_ = section_type_solid_infill_section; + return true; + } + if (comment == "skirt") + { + current_section_ = section_type_skirt_section; + return true; + } + if (comment == "ooze shield") + { + current_section_ = section_type_ooze_shield_section; + return true; + } - if (comment == "prime pillar") - { - current_section_ = section_type_prime_pillar_section; - return true; - } + if (comment == "prime pillar") + { + current_section_ = section_type_prime_pillar_section; + return true; + } - if (comment == "gap fill") - { - current_section_ = section_type_gap_fill_section; - return true; - } - } + if (comment == "gap fill") + { + current_section_ = section_type_gap_fill_section; + return true; + } + } - return false; + return false; } bool gcode_comment_processor::update_slic3r_pe_section(std::string& comment) { - if (comment == "CP TOOLCHANGE WIPE") - { - current_section_ = section_type_prime_pillar_section; - return true; - } - if (comment == "CP TOOLCHANGE END") - { - current_section_ = section_type_no_section; - return true; - } - return false; + // PrusaSlicer / SuperSlicer tags + if (comment == "TYPE:Internal perimeter" + || comment == "TYPE:Perimeter") + { + current_section_ = section_type_inner_perimeter_section; + return true; + } + if (comment == "TYPE:External perimeter" + || comment == "TYPE:Thin wall") + { + current_section_ = section_type_outer_perimeter_section; + return true; + } + if (comment == "TYPE:Internal infill") + { + current_section_ = section_type_infill_section; + return true; + } + if (comment == "TYPE:Solid infill" + || comment == "TYPE:Top solid infill") + { + current_section_ = section_type_solid_infill_section; + + return true; + } + if (comment == "TYPE:Gap fill") + { + current_section_ = section_type_gap_fill_section; + return true; + } + if (comment == "TYPE:Bridge infill" + || comment == "TYPE:Internal bridge infill" + || comment == "TYPE:Overhang perimeter") + { + current_section_ = section_type_bridge_section; + return true; + } + if (comment == "TYPE:Skirt") + { + current_section_ = section_type_skirt_section; + return true; + } + if (comment == "TYPE:Support material" + || comment == "TYPE:Support material interface") + { + // no "support" feature on octolapse. Maybe feature_type_prime_pillar_feature ? + // If we lable this feature as unknown, it will be absolutely last on the 'quality' order, which is what we want. + current_section_ = section_type_support_section; + return true; + } + if (comment == "TYPE:Wipe tower") + { + current_section_ = section_type_prime_pillar_section; + return true; + } + + // Here are some features we want to just ignore. Might want to think about ironing. + if (comment == "TYPE:Mill" + || comment == "TYPE:Unknown" + || comment == "TYPE:Custom" + || comment == "TYPE:Mixed" + || comment == "TYPE:Ironing") + { + current_section_ = section_type_no_section; + return true; + } + + + if (comment == "CP TOOLCHANGE WIPE") + { + current_section_ = section_type_prime_pillar_section; + return true; + } + + // End section codes (we don't want to apply ANY features if we hit these comments) + + if (comment == "CP TOOLCHANGE END" // Reset section on end of tool change + || comment == "LAYER_CHANGE" // Reset section on layer change + || comment == "BEFORE_LAYER_CHANGE" // Reset section on layer change + || comment == "AFTER_LAYER_CHANGE" // Reset section on layer change + ) + { + current_section_ = section_type_no_section; + return true; + } + + // Now look for subsection codes (wipe so far) + if (comment == "WIPE_START") + { + current_subsection_ = subsection_type_wipe; + return true; + } + + if (comment == "WIPE_END") + { + current_subsection_ = subsection_type_none; + return true; + } + + + return false; } diff --git a/octoprint_octolapse/data/lib/c/gcode_comment_processor.h b/octoprint_octolapse/data/lib/c/gcode_comment_processor.h index 96c7e0c4..b437c66c 100644 --- a/octoprint_octolapse/data/lib/c/gcode_comment_processor.h +++ b/octoprint_octolapse/data/lib/c/gcode_comment_processor.h @@ -1,9 +1,9 @@ #pragma once #include "position.h" -#define NUM_FEATURE_TYPES 11 +#define NUM_FEATURE_TYPES 12 static const std::string feature_type_name[NUM_FEATURE_TYPES] = { - "unknown_feature", "bridge_feature", "outer_perimeter_feature", "unknown_perimeter_feature", + "unknown_feature", "bridge_feature", "support_feature", "outer_perimeter_feature", "unknown_perimeter_feature", "inner_perimeter_feature", "skirt_feature", "gap_fill_feature", "solid_infill_feature", "ooze_shield_feature", "infill_feature", "prime_pillar_feature" }; @@ -12,6 +12,7 @@ enum feature_type { feature_type_unknown_feature, feature_type_bridge_feature, + feature_type_support_feature, feature_type_outer_perimeter_feature, feature_type_unknown_perimeter_feature, feature_type_inner_perimeter_feature, @@ -20,7 +21,7 @@ enum feature_type feature_type_solid_infill_feature, feature_type_ooze_shield_feature, feature_type_infill_feature, - feature_type_prime_pillar_feature + feature_type_prime_pillar_feature, }; enum comment_process_type @@ -43,7 +44,16 @@ enum section_type section_type_skirt_section, section_type_solid_infill_section, section_type_ooze_shield_section, - section_type_prime_pillar_section + section_type_prime_pillar_section, + section_type_bridge_section, + section_type_support_section +}; + +// Used for sections inside of sections (depth = 1) +enum subsection_type +{ + subsection_type_none, + subsection_type_wipe }; // Static strings for slicer comment extraction @@ -78,7 +88,10 @@ class gcode_comment_processor private: section_type current_section_; + subsection_type current_subsection_; + comment_process_type processing_type_; + void update_feature_from_section(position& pos) const; bool update_feature_from_section_from_section(position& pos) const; bool update_feature_from_section_for_cura(position& pos) const; From ccab7f334df6dd89ec6a6ec1f33ba9ab383f08fb Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Sat, 11 Dec 2021 11:40:40 -0600 Subject: [PATCH 464/485] First attempt at wide char support for stabilization (c++), which could break c++ compilation on the RPI. Attempt to support characters removed by slugify in rendered file names etc. --- octoprint_octolapse/__init__.py | 4 +++- .../data/lib/c/gcode_position_processor.cpp | 24 +++++++++++++++---- .../data/lib/c/python_helpers.cpp | 20 ++++++++++++++++ .../data/lib/c/python_helpers.h | 1 + .../data/lib/c/stabilization.cpp | 6 ++--- .../data/lib/c/stabilization.h | 6 ++--- octoprint_octolapse/render.py | 19 +++++++++++---- octoprint_octolapse/utility.py | 14 ++++------- 8 files changed, 68 insertions(+), 26 deletions(-) diff --git a/octoprint_octolapse/__init__.py b/octoprint_octolapse/__init__.py index 3472f540..ccdd5b3c 100644 --- a/octoprint_octolapse/__init__.py +++ b/octoprint_octolapse/__init__.py @@ -328,7 +328,8 @@ def clean_temp_folder(file_path=None, file_directory=None): } full_path = temp_archive_path elif file_type in ['timelapse_octolapse', 'snapshot_archive', 'timelapse_octoprint']: - file_name = utility.unquote(request_handler.get_query_arguments('name')[0]) + #file_name = utility.unquote(request_handler.get_query_arguments('name')[0]) + file_name = request_handler.get_query_arguments('name')[0] # Don't allow any subdirectory access if not OctolapsePlugin.file_name_allowed(file_name): raise tornado.web.HTTPError(500) @@ -3534,6 +3535,7 @@ def on_render_success(self, payload, job): output_file_name = "{0}.{1}".format(payload.RenderingFilename, payload.RenderingExtension) gcode_filename = "{0}.{1}".format(payload.GcodeFilename, payload.GcodeFileExtension) + # Todo: Make sure this path is correct output_file_path = payload.get_rendering_path() # Notify the plugin that a timelapse has been added, if it exists diff --git a/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp b/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp index 63c5ee11..a4c2cb73 100644 --- a/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp +++ b/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp @@ -1467,15 +1467,29 @@ static bool ParseStabilizationArgs(PyObject* py_args, stabilization_args* args, // file_path - PyObject* py_file_path = PyDict_GetItemString(py_args, "file_path"); - if (py_file_path == NULL) + PyObject* py_dict_key = PyString_SafeFromString("file_path"); + PyObject* py_dict_item = PyDict_GetItem(py_args, py_dict_key); + if (py_dict_item == NULL) + { + std::string message = + "GcodePositionProcessor.ParseStabilizationArgs - Unable to retrieve the file_path from the stabilization args dict."; + octolapse_log_exception(octolapse_log::SNAPSHOT_PLAN, message); + return false; + } + Py_DecRef(py_dict_key); + /* + Py_UNICODE* py_dict_item_unicode = PyUnicode_AsUnicode(py_dict_item); + //PyObject* py_file_path = PyDict_GetItemString(py_args, "file_path"); + if (py_dict_item_unicode == NULL) { std::string message = - "GcodePositionProcessor.ParseStabilizationArgs - Unable to retrieve file_path from the stabilization args."; + "GcodePositionProcessor.ParseStabilizationArgs - Unable to convert the file_path dict item to a Py_UNICODE ."; octolapse_log_exception(octolapse_log::SNAPSHOT_PLAN, message); return false; - } - args->file_path = PyUnicode_SafeAsString(py_file_path); + }*/ + + //args->file_path = PyUnicode_SafeAsString(py_file_path); + args->file_path = PyObject_SafeFileNameAsWstring(py_dict_item); //std::cout << "Stabilization Args parsed successfully.\r\n"; return true; } diff --git a/octoprint_octolapse/data/lib/c/python_helpers.cpp b/octoprint_octolapse/data/lib/c/python_helpers.cpp index a22c4c97..cb7605cc 100644 --- a/octoprint_octolapse/data/lib/c/python_helpers.cpp +++ b/octoprint_octolapse/data/lib/c/python_helpers.cpp @@ -19,6 +19,26 @@ const char* PyUnicode_SafeAsString(PyObject* py) #endif } +std::wstring PyObject_SafeFileNameAsWstring(PyObject* py) +{ +#if PY_MAJOR_VERSION >= 3 + Py_ssize_t size = PyUnicode_GET_SIZE(py); + wchar_t* wchar_array = new wchar_t[size]; + PyUnicode_AsWideChar(py, wchar_array, size); + std::wstring result = std::wstring(wchar_array); + delete wchar_array; + return result; +#else + // In python 2, octoprint does not store unicode file names (I don't think), so just return a string as a wstring + Py_ssize_t size = PyUnicode_GET_SIZE(py); + wchar_t* wchar_array = new wchar_t[size]; + PyUnicode_AsWideChar((PyUnicodeObject*)py, wchar_array, size); + std::wstring result = std::wstring(wchar_array); + delete wchar_array; + return result; +#endif +} + PyObject* PyString_SafeFromString(const char* str) { #if PY_MAJOR_VERSION >= 3 diff --git a/octoprint_octolapse/data/lib/c/python_helpers.h b/octoprint_octolapse/data/lib/c/python_helpers.h index ad735747..47532e8f 100644 --- a/octoprint_octolapse/data/lib/c/python_helpers.h +++ b/octoprint_octolapse/data/lib/c/python_helpers.h @@ -11,6 +11,7 @@ int PyUnicode_SafeCheck(PyObject* py); const char* PyUnicode_SafeAsString(PyObject* py); PyObject* PyString_SafeFromString(const char* str); PyObject* PyUnicode_SafeFromString(std::string str); +std::wstring PyObject_SafeFileNameAsWstring(PyObject* py); double PyFloatOrInt_AsDouble(PyObject* py_double_or_int); long PyIntOrLong_AsLong(PyObject* value); bool PyFloatLongOrInt_Check(PyObject* value); diff --git a/octoprint_octolapse/data/lib/c/stabilization.cpp b/octoprint_octolapse/data/lib/c/stabilization.cpp index 524c3e07..1c70f840 100644 --- a/octoprint_octolapse/data/lib/c/stabilization.cpp +++ b/octoprint_octolapse/data/lib/c/stabilization.cpp @@ -150,7 +150,7 @@ void stabilization::delete_gcode_position() } } -long stabilization::get_file_size(const std::string& file_path) +long stabilization::get_file_size(const std::wstring& file_path) { // Todo: Fix this function. This is a pretty weak implementation :( std::ifstream file(file_path.c_str(), std::ios::in | std::ios::binary); @@ -193,8 +193,8 @@ stabilization_results stabilization::process_file() snapshots_enabled_ = true; int read_lines_before_clock_check = 2000; //std::cout << "stabilization::process_file - Processing file.\r\n"; - stream << "Stabilizing file at: " << stabilization_args_.file_path; - octolapse_log(octolapse_log::SNAPSHOT_PLAN, octolapse_log::INFO, stream.str()); + //stream << "Stabilizing file at: " << stabilization_args_.file_path; + //octolapse_log(octolapse_log::SNAPSHOT_PLAN, octolapse_log::INFO, stream.str()); is_running_ = true; double next_update_time = get_next_update_time(); diff --git a/octoprint_octolapse/data/lib/c/stabilization.h b/octoprint_octolapse/data/lib/c/stabilization.h index 3a516495..a0c276b6 100644 --- a/octoprint_octolapse/data/lib/c/stabilization.h +++ b/octoprint_octolapse/data/lib/c/stabilization.h @@ -50,7 +50,7 @@ class stabilization_args { height_increment = 0.0; notification_period_seconds = 0.25; - file_path = ""; + file_path = L""; x_coordinate = 0; y_coordinate = 0; x_stabilization_disabled = false; @@ -61,7 +61,7 @@ class stabilization_args { } - std::string file_path; + std::wstring file_path; double height_increment; double notification_period_seconds; @@ -141,7 +141,7 @@ class stabilization pythonProgressCallback progress_callback_; gcode_position* gcode_position_; gcode_parser* gcode_parser_; - long get_file_size(const std::string& file_path); + long get_file_size(const std::wstring& file_path); long file_size_; int lines_processed_; int gcodes_processed_; diff --git a/octoprint_octolapse/render.py b/octoprint_octolapse/render.py index 8027609e..471a4954 100644 --- a/octoprint_octolapse/render.py +++ b/octoprint_octolapse/render.py @@ -372,11 +372,11 @@ def __init__( ) self.snapshot_filename_format = os.path.basename( utility.get_snapshot_filename( - timelapse_job_info.PrintFileName, utility.SnapshotNumberFormat + timelapse_job_info.PrintFileName.replace("%", "%%"), utility.SnapshotNumberFormat ) ) self.pre_roll_snapshot_filename_format = utility.get_pre_roll_snapshot_filename( - timelapse_job_info.PrintFileName, utility.SnapshotNumberFormat + timelapse_job_info.PrintFileName.replace("%", "%%"), utility.SnapshotNumberFormat ) # rendering directory path self.output_tokens = self._get_output_tokens(self.temporary_directory) @@ -522,14 +522,22 @@ def get_output_tokens_from_metadata(metadata): ) @staticmethod - def get_rendering_filename(output_template, output_tokens): + def get_sanitized_rendering_filename(output_template, output_tokens): return utility.sanitize_filename(output_template.format(**output_tokens)) @staticmethod - def get_rendering_name_from_metadata(metadata): + def get_rendering_filename(output_template, output_tokens): + return output_template.format(**output_tokens) + + @staticmethod + def get_sanitized_rendering_name_from_metadata(metadata): output_tokens = RenderJobInfo.get_output_tokens_from_metadata(metadata) - return RenderJobInfo.get_rendering_filename(metadata["output_template"], output_tokens) + return RenderJobInfo.get_sanitized_rendering_filename(metadata["output_template"], output_tokens) + @staticmethod + def get_rendering_name_from_metadata(metadata): + output_tokens = RenderJobInfo.get_output_tokens_from_metadata(metadata) + return RenderJobInfo.get_rendering_name_from_metadata(metadata["output_template"], output_tokens) class RenderingProcessor(threading.Thread): """Watch for rendering jobs via a rendering queue. Extract jobs from the queue, and spawn a rendering thread, @@ -728,6 +736,7 @@ def archive_unfinished_job( if is_download: metadata = self._get_metadata_for_rendering_files(job_guid, camera_guid, temporary_directory) target_extension = utility.get_extension_from_full_path(target_path) + # TODO: MAKE SURE THIS WORKS! USED TO BE SANITIZED return "{0}.{1}".format(RenderJobInfo.get_rendering_name_from_metadata(metadata), target_extension) return None diff --git a/octoprint_octolapse/utility.py b/octoprint_octolapse/utility.py index b34c4479..4490b9b1 100644 --- a/octoprint_octolapse/utility.py +++ b/octoprint_octolapse/utility.py @@ -349,18 +349,10 @@ def get_no_snapshot_image_download_path(base_folder): return get_images_download_path(base_folder, "no_snapshot.png") -def get_rendering_directory_template(): - return os.path.join("{DATADIRECTORY}", "timelapses") - - def get_rendering_base_filename_template(): return "{FILENAME}_{DATETIMESTAMP}" -def get_rendering_filename(template, tokens): - template.format(**tokens) - - def get_rendering_base_filename(print_name, print_start_time, print_end_time=None): file_template = get_rendering_base_filename_template() file_template = file_template.replace("{FILENAME}", get_string(print_name, "")) @@ -396,7 +388,11 @@ def is_valid_temporary_extension(extension): def get_snapshot_filename(print_name, snapshot_number): - return "{0}{1}.{2}".format(print_name, format_snapshot_number(snapshot_number), default_snapshot_extension) + return "{0}{1}.{2}".format( + print_name, + format_snapshot_number(snapshot_number), + default_snapshot_extension + ) def get_pre_roll_snapshot_filename(print_name, snapshot_number): From 0e6ba93f37cc98ee08873c3cb59ec3c96e06dcf3 Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Sat, 11 Dec 2021 12:33:01 -0600 Subject: [PATCH 465/485] Second attempt at wide char support for stabilization (c++). This time separate out linux and windows specific code. --- octoprint_octolapse/data/lib/c/stabilization.cpp | 11 ++++++++++- octoprint_octolapse/data/lib/c/utilities.cpp | 14 ++++++++++++++ octoprint_octolapse/data/lib/c/utilities.h | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/octoprint_octolapse/data/lib/c/stabilization.cpp b/octoprint_octolapse/data/lib/c/stabilization.cpp index 1c70f840..b806ab8a 100644 --- a/octoprint_octolapse/data/lib/c/stabilization.cpp +++ b/octoprint_octolapse/data/lib/c/stabilization.cpp @@ -200,7 +200,16 @@ stabilization_results stabilization::process_file() double next_update_time = get_next_update_time(); const clock_t start_clock = clock(); file_size_ = get_file_size(stabilization_args_.file_path); - std::ifstream gcodeFile(stabilization_args_.file_path.c_str()); + +#ifdef __linux__ + std::ifstream gcodeFile(utilities::wstring_to_utf8(stabilization_args_.file_path)); +#elif _WIN32 + std::ifstream gcodeFile(stabilization_args_.file_path.c_str()); +#else + std::ifstream gcodeFile(utilities::wstring_to_utf8(stabilization_args_.file_path)); +#endif + + std::string line; int lines_with_no_commands = 0; if (gcodeFile.is_open()) diff --git a/octoprint_octolapse/data/lib/c/utilities.cpp b/octoprint_octolapse/data/lib/c/utilities.cpp index 1863a115..2aaa902c 100644 --- a/octoprint_octolapse/data/lib/c/utilities.cpp +++ b/octoprint_octolapse/data/lib/c/utilities.cpp @@ -5,6 +5,12 @@ #include #include +#ifdef __linux__ +#include +#include +#endif + + // Had to increase the zero tolerance because prusa slicer doesn't always retract enough while wiping. const double ZERO_TOLERANCE = 0.00005; const std::string utilities::WHITESPACE_ = " \n\r\t\f\v"; @@ -158,3 +164,11 @@ bool utilities::is_in_caseless_trim(const std::string& lhs, const char** rhs) // If we are here, this string does not appear return false; } + +#ifdef __linux__ +std::string utilities::wstring_to_utf8(std::wstring str) { + std::wstring_convert> utf8_conv; + return utf8_conv.to_bytes(str); +} +#endif + diff --git a/octoprint_octolapse/data/lib/c/utilities.h b/octoprint_octolapse/data/lib/c/utilities.h index 2e02c46b..e841e1a5 100644 --- a/octoprint_octolapse/data/lib/c/utilities.h +++ b/octoprint_octolapse/data/lib/c/utilities.h @@ -18,6 +18,10 @@ class utilities static std::string trim(const std::string& s); static std::istream& safe_get_line(std::istream& is, std::string& t); static bool is_in_caseless_trim(const std::string& lhs, const char** rhs); +#ifdef __linux__ + static std::string wstring_to_utf8(std::wstring str); +#endif + protected: static const std::string WHITESPACE_; private: From c39075dfffab5d3116689531f4cfaad5342b5376 Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Sat, 11 Dec 2021 12:45:57 -0600 Subject: [PATCH 466/485] Switch defines to make them less specific for UTF8 filenames in non-windows platforms. --- octoprint_octolapse/data/lib/c/stabilization.cpp | 6 ++---- octoprint_octolapse/data/lib/c/utilities.cpp | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/octoprint_octolapse/data/lib/c/stabilization.cpp b/octoprint_octolapse/data/lib/c/stabilization.cpp index b806ab8a..01fba3b6 100644 --- a/octoprint_octolapse/data/lib/c/stabilization.cpp +++ b/octoprint_octolapse/data/lib/c/stabilization.cpp @@ -201,12 +201,10 @@ stabilization_results stabilization::process_file() const clock_t start_clock = clock(); file_size_ = get_file_size(stabilization_args_.file_path); -#ifdef __linux__ +#ifndef _WIN32 std::ifstream gcodeFile(utilities::wstring_to_utf8(stabilization_args_.file_path)); -#elif _WIN32 - std::ifstream gcodeFile(stabilization_args_.file_path.c_str()); #else - std::ifstream gcodeFile(utilities::wstring_to_utf8(stabilization_args_.file_path)); + std::ifstream gcodeFile(stabilization_args_.file_path.c_str()); #endif diff --git a/octoprint_octolapse/data/lib/c/utilities.cpp b/octoprint_octolapse/data/lib/c/utilities.cpp index 2aaa902c..699dabe7 100644 --- a/octoprint_octolapse/data/lib/c/utilities.cpp +++ b/octoprint_octolapse/data/lib/c/utilities.cpp @@ -4,8 +4,7 @@ #include #include #include - -#ifdef __linux__ +#ifndef _WIN32 #include #include #endif @@ -165,7 +164,7 @@ bool utilities::is_in_caseless_trim(const std::string& lhs, const char** rhs) return false; } -#ifdef __linux__ +#ifndef _WIN32 std::string utilities::wstring_to_utf8(std::wstring str) { std::wstring_convert> utf8_conv; return utf8_conv.to_bytes(str); From 7d9479091232ed88e8789754cce6ce9f5a03a398 Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Sat, 11 Dec 2021 14:28:57 -0600 Subject: [PATCH 467/485] Create defines to detect windows and python. Use UTF7 for command line tests, and UTF8 encoding for windows if the python extension is being used, else use file_name.c_str() for linux and RPI. --- .../data/lib/c/gcode_position_processor.cpp | 4 +-- .../data/lib/c/python_helpers.cpp | 20 ------------ .../data/lib/c/stabilization.cpp | 25 +++++++++++---- .../data/lib/c/stabilization.h | 6 ++-- octoprint_octolapse/data/lib/c/utilities.cpp | 32 +++++++++++++------ octoprint_octolapse/data/lib/c/utilities.h | 6 ++-- setup.py | 24 +++++++------- 7 files changed, 61 insertions(+), 56 deletions(-) diff --git a/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp b/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp index a4c2cb73..d77ad35f 100644 --- a/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp +++ b/octoprint_octolapse/data/lib/c/gcode_position_processor.cpp @@ -1488,8 +1488,8 @@ static bool ParseStabilizationArgs(PyObject* py_args, stabilization_args* args, return false; }*/ - //args->file_path = PyUnicode_SafeAsString(py_file_path); - args->file_path = PyObject_SafeFileNameAsWstring(py_dict_item); + args->file_path = PyUnicode_SafeAsString(py_dict_item); + //std::cout << "Stabilization Args parsed successfully.\r\n"; return true; } diff --git a/octoprint_octolapse/data/lib/c/python_helpers.cpp b/octoprint_octolapse/data/lib/c/python_helpers.cpp index cb7605cc..a22c4c97 100644 --- a/octoprint_octolapse/data/lib/c/python_helpers.cpp +++ b/octoprint_octolapse/data/lib/c/python_helpers.cpp @@ -19,26 +19,6 @@ const char* PyUnicode_SafeAsString(PyObject* py) #endif } -std::wstring PyObject_SafeFileNameAsWstring(PyObject* py) -{ -#if PY_MAJOR_VERSION >= 3 - Py_ssize_t size = PyUnicode_GET_SIZE(py); - wchar_t* wchar_array = new wchar_t[size]; - PyUnicode_AsWideChar(py, wchar_array, size); - std::wstring result = std::wstring(wchar_array); - delete wchar_array; - return result; -#else - // In python 2, octoprint does not store unicode file names (I don't think), so just return a string as a wstring - Py_ssize_t size = PyUnicode_GET_SIZE(py); - wchar_t* wchar_array = new wchar_t[size]; - PyUnicode_AsWideChar((PyUnicodeObject*)py, wchar_array, size); - std::wstring result = std::wstring(wchar_array); - delete wchar_array; - return result; -#endif -} - PyObject* PyString_SafeFromString(const char* str) { #if PY_MAJOR_VERSION >= 3 diff --git a/octoprint_octolapse/data/lib/c/stabilization.cpp b/octoprint_octolapse/data/lib/c/stabilization.cpp index 01fba3b6..ca038d2b 100644 --- a/octoprint_octolapse/data/lib/c/stabilization.cpp +++ b/octoprint_octolapse/data/lib/c/stabilization.cpp @@ -150,7 +150,7 @@ void stabilization::delete_gcode_position() } } -long stabilization::get_file_size(const std::wstring& file_path) +long stabilization::get_file_size(const std::string& file_path) { // Todo: Fix this function. This is a pretty weak implementation :( std::ifstream file(file_path.c_str(), std::ios::in | std::ios::binary); @@ -192,22 +192,33 @@ stabilization_results stabilization::process_file() // Make sure snapshots are enabled at the start of the process. snapshots_enabled_ = true; int read_lines_before_clock_check = 2000; - //std::cout << "stabilization::process_file - Processing file.\r\n"; - //stream << "Stabilizing file at: " << stabilization_args_.file_path; - //octolapse_log(octolapse_log::SNAPSHOT_PLAN, octolapse_log::INFO, stream.str()); + std::cout << "stabilization::process_file - Processing file.\r\n"; + stream << "Stabilizing file at: " << stabilization_args_.file_path; + octolapse_log(octolapse_log::SNAPSHOT_PLAN, octolapse_log::INFO, stream.str()); is_running_ = true; double next_update_time = get_next_update_time(); const clock_t start_clock = clock(); file_size_ = get_file_size(stabilization_args_.file_path); -#ifndef _WIN32 - std::ifstream gcodeFile(utilities::wstring_to_utf8(stabilization_args_.file_path)); + std::string path = stabilization_args_.file_path; + + +#ifdef _MSC_VER + + + std::wstring wpath = utilities::ToUtf16(path); + stream << "Windows detected, encoding file as UTF16"; + octolapse_log(octolapse_log::SNAPSHOT_PLAN, octolapse_log::INFO, stream.str()); + std::ifstream gcodeFile(wpath.c_str()); + + #else - std::ifstream gcodeFile(stabilization_args_.file_path.c_str()); + std::ifstream gcodeFile(path.c_str()); #endif + std::string line; int lines_with_no_commands = 0; if (gcodeFile.is_open()) diff --git a/octoprint_octolapse/data/lib/c/stabilization.h b/octoprint_octolapse/data/lib/c/stabilization.h index a0c276b6..3a516495 100644 --- a/octoprint_octolapse/data/lib/c/stabilization.h +++ b/octoprint_octolapse/data/lib/c/stabilization.h @@ -50,7 +50,7 @@ class stabilization_args { height_increment = 0.0; notification_period_seconds = 0.25; - file_path = L""; + file_path = ""; x_coordinate = 0; y_coordinate = 0; x_stabilization_disabled = false; @@ -61,7 +61,7 @@ class stabilization_args { } - std::wstring file_path; + std::string file_path; double height_increment; double notification_period_seconds; @@ -141,7 +141,7 @@ class stabilization pythonProgressCallback progress_callback_; gcode_position* gcode_position_; gcode_parser* gcode_parser_; - long get_file_size(const std::wstring& file_path); + long get_file_size(const std::string& file_path); long file_size_; int lines_processed_; int gcodes_processed_; diff --git a/octoprint_octolapse/data/lib/c/utilities.cpp b/octoprint_octolapse/data/lib/c/utilities.cpp index 699dabe7..9f9f539e 100644 --- a/octoprint_octolapse/data/lib/c/utilities.cpp +++ b/octoprint_octolapse/data/lib/c/utilities.cpp @@ -4,9 +4,29 @@ #include #include #include -#ifndef _WIN32 -#include -#include + + +#ifdef _MSC_VER + #include + #include + + std::wstring utilities::ToUtf16(std::string str) + { + UINT codepage = 65001; + #ifdef IS_PYTHON_EXTENSION + codepage = 65001; // CP_UTF8 + #else + codepage = 65000; // CP_UTF7 + #endif + std::wstring ret; + int len = MultiByteToWideChar(codepage, 0, str.c_str(), str.length(), NULL, 0); + if (len > 0) + { + ret.resize(len); + MultiByteToWideChar(codepage, 0, str.c_str(), str.length(), &ret[0], len); + } + return ret; + } #endif @@ -164,10 +184,4 @@ bool utilities::is_in_caseless_trim(const std::string& lhs, const char** rhs) return false; } -#ifndef _WIN32 -std::string utilities::wstring_to_utf8(std::wstring str) { - std::wstring_convert> utf8_conv; - return utf8_conv.to_bytes(str); -} -#endif diff --git a/octoprint_octolapse/data/lib/c/utilities.h b/octoprint_octolapse/data/lib/c/utilities.h index e841e1a5..4dd7ac51 100644 --- a/octoprint_octolapse/data/lib/c/utilities.h +++ b/octoprint_octolapse/data/lib/c/utilities.h @@ -18,10 +18,10 @@ class utilities static std::string trim(const std::string& s); static std::istream& safe_get_line(std::istream& is, std::string& t); static bool is_in_caseless_trim(const std::string& lhs, const char** rhs); -#ifdef __linux__ - static std::string wstring_to_utf8(std::wstring str); -#endif +#ifdef _MSC_VER + static std::wstring ToUtf16(std::string str); +#endif protected: static const std::string WHITESPACE_; private: diff --git a/setup.py b/setup.py index 023f4c72..93e42735 100644 --- a/setup.py +++ b/setup.py @@ -100,27 +100,27 @@ CCompiler.compiler_type: { 'extra_compile_args': ['-O3', '-std=c++11'], 'extra_link_args': [], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] }, MSVCCompiler.compiler_type: { 'extra_compile_args': ['/O2', '/fp:fast', '/GL', '/analyze', '/Gy', '/MD', '/EHsc'], 'extra_link_args': [], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] }, UnixCCompiler.compiler_type: { 'extra_compile_args': ['-O3', '-std=c++11'], 'extra_link_args': [], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] }, BCPPCompiler.compiler_type: { 'extra_compile_args': ['-O3', '-std=c++11'], 'extra_link_args': [], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] }, CygwinCCompiler.compiler_type: { 'extra_compile_args': ['-O3', '-std=c++11'], 'extra_link_args': [], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] } } @@ -129,27 +129,27 @@ CCompiler.compiler_type: { 'extra_compile_args': [], 'extra_link_args': [], - 'define_macros': [('DEBUG_chardet', '1')] + 'define_macros': [('DEBUG_chardet', '1'), ('IS_PYTHON_EXTENSION', '1')] }, MSVCCompiler.compiler_type: { 'extra_compile_args': ['/EHsc', '/Z7'], 'extra_link_args': ['/DEBUG'], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] }, UnixCCompiler.compiler_type: { 'extra_compile_args': ['-g'], 'extra_link_args': ['-g'], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] }, BCPPCompiler.compiler_type: { 'extra_compile_args': [], 'extra_link_args': [], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] }, CygwinCCompiler.compiler_type: { 'extra_compile_args': [], 'extra_link_args': [], - 'define_macros': [] + 'define_macros': [('IS_PYTHON_EXTENSION', '1')] } } @@ -166,8 +166,8 @@ def build_extensions(self): build_ext.build_extensions(self) for extension in self.extensions: - print("Build Extensions for {0} - extra_compile_args:{1} - extra_link_args:{2}".format( - extension.name, extension.extra_compile_args, extension.extra_link_args) + print("Build Extensions for {0} - extra_compile_args:{1} - extra_link_args:{2} - define_macros:{3}".format( + extension.name, extension.extra_compile_args, extension.extra_link_args, extension.define_macros) ) From ae38ed1d6d60a03e2acd86f31b38d7d0f4d90e81 Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Tue, 26 Apr 2022 12:45:37 -0500 Subject: [PATCH 468/485] Add additional startup logging to assist in issue debugging. --- octoprint_octolapse/__init__.py | 61 +++++++++++++++---- .../stabilization_preprocessing.py | 14 +++++ octoprint_octolapse/timelapse.py | 6 ++ 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/octoprint_octolapse/__init__.py b/octoprint_octolapse/__init__.py index ccdd5b3c..aac369f8 100644 --- a/octoprint_octolapse/__init__.py +++ b/octoprint_octolapse/__init__.py @@ -2459,38 +2459,57 @@ def on_print_start(self, parsed_command): self.on_print_start_failed([error], parsed_command=parsed_command) def test_timelapse_config(self): + logger.debug("Testing timelapse configuration.") + + logger.verbose("Verify that Octolapse is enabled.") if not self._octolapse_settings.main_settings.is_octolapse_enabled: + logger.error("Octolapse is not enabled. Cannot start timelapse.") error = error_messages.get_error(["init","octolapse_is_disabled"]) logger.error(error["description"]) return {"success": False, "error": error} - # make sure we have an active printer + logger.verbose("Ensure that we have at least one printer profile.") + # make sure that at least one profile is available + if len(self._octolapse_settings.profiles.printers) == 0: + logger.error("No printer profiles exist. Cannot start timelapse.") + error = error_messages.get_error(["init", "no_printer_profile_exists"]) + return {"success": False, "error": error} + + # make sure we have an active printer (a selected profile) + logger.verbose("Ensure one printer profile is active and selected.") if self._octolapse_settings.profiles.current_printer() is None: + logger.error("There is no currently selected printer profile. Cannot start timelapse.") error = error_messages.get_error(["init", "no_printer_profile_selected"]) logger.error(error["description"]) return {"success": False, "error": error} # see if the printer profile has been configured + logger.verbose("Ensure the current printer profile has been configured or that the configuration is automatic.") if ( not self._octolapse_settings.profiles.current_printer().has_been_saved_by_user and not self._octolapse_settings.profiles.current_printer().slicer_type == "automatic" ): + logger.error("The selected printer profile is not configured, or the slicer type is not automatic. Cannot start timelapse.") error = error_messages.get_error(["init", "printer_not_configured"]) logger.error(error["description"]) return {"success": False, "error": error} # determine the file source + logger.verbose("Ensure data exists about the current job.") printer_data = self._printer.get_current_data() current_job = printer_data.get("job", None) if not current_job: + logger.error("Data about the current job could not be found. Cannot start timelapse.") error = error_messages.get_error(["init", "no_current_job_data_found"]) log_message = "Failed to get current job data on_print_start:" \ " Current printer data: {0}".format(printer_data) logger.error(log_message) return {"success": False, "error": error} + logger.verbose("Ensure current job has an associated file.") current_file = current_job.get("file", None) if not current_file: + logger.error("The current job has no associated file. Cannot start timelapse.") error = error_messages.get_error(["init", "no_current_job_file_data_found"]) log_message = "Failed to get current file data on_print_start:" \ " Current job data: {0}".format(current_job) @@ -2498,23 +2517,29 @@ def test_timelapse_config(self): # ERROR_REPLACEMENT return {"success": False, "error": error} + logger.verbose("Ensure origin information is available for the current job.") current_origin = current_file.get("origin", "unknown") if not current_origin: + logger.error("File origin information does not exist within the current job info. Cannot start timelapse.") error = error_messages.get_error(["init", "unknown_file_origin"]) log_message = "Failed to get current origin data on_print_start:" \ "Current file data: {0}".format(current_file) # ERROR_REPLACEMENT logger.error(log_message) return {"success": False, "error": error} + + logger.verbose("Ensure the file is being printed locally (not from SD).") if current_origin != "local": + logger.error("Octolapse does not support printing from SD. Cannot start timelapse.") error = error_messages.get_error(["init", "cant_print_from_sd"]) log_message = "Unable to start Octolapse when printing from {0}.".format(current_origin) logger.error(log_message) # ERROR_REPLACEMENT return {"success": False, "error": error} + logger.verbose("Ensure that Octolapse is in the correct state (Initializing).") if self._timelapse.get_current_state() != TimelapseState.Initializing: - + logger.error("Octolapse is not in the correct state. Cannot start timelapse.") error = error_messages.get_error(["init", "incorrect_printer_state"]) log_message = "Octolapse was in the wrong state at print start. StateId: {0}".format( self._timelapse.get_current_state()) @@ -2523,6 +2548,7 @@ def test_timelapse_config(self): return {"success": False, "error": error} # test all cameras and look for at least one enabled camera + logger.verbose("Testing all enabled cameras.") found_camera = False for current_camera in self._octolapse_settings.profiles.active_cameras(): if current_camera.enabled: @@ -2536,73 +2562,78 @@ def test_timelapse_config(self): return {"success": False, "error": error} if not found_camera: + logger.error("No enabled cameras could be found, please enable at least one camera profile and ensure it passes tests.") error = error_messages.get_error(["init", "no_enabled_cameras"]) return {"success": False, "error": error} + logger.verbose("Attempting to apply any existing custom camera preferences, if any are set.") success, camera_settings_apply_errors = camera.CameraControl.apply_camera_settings( self._octolapse_settings.profiles.before_print_start_webcameras() ) if not success: + logger.error("Unable to apply custom camera preferences, failed to start timelapse.") error = error_messages.get_error( ["init", "camera_settings_apply_failed"], error=camera_settings_apply_errors) return {"success": False, "error": error} # run before print start camera scripts + logger.verbose("Running 'before print' camera scripts, if any exist.") success, before_print_start_camera_script_errors = camera.CameraControl.run_on_print_start_script( self._octolapse_settings.profiles.cameras ) if not success: + logger.error("Errors were encountered running 'before print' camera scripts. Failed to start timelapse.") error = error_messages.get_error( ["init", "before_print_start_camera_script_apply_failed"], error=before_print_start_camera_script_errors) return {"success": False, "error": error} # check for version 1.3.8 min + logger.verbose("Ensure we are running at least OctoPrint version 1.3.8.") if not (LooseVersion(octoprint.server.VERSION) > LooseVersion("1.3.8")): + logger.error("The current octoprint version is older than V1.3.8, and is not supported. Failed to start " + "timelapse.") error = error_messages.get_error( ["init", "incorrect_octoprint_version"], installed_version=octoprint.server.DISPLAY_VERSION ) return {"success": False, "error": error} # Check the rendering filename template + logger.verbose("Validating the rendering file template.") if not render.is_rendering_template_valid( self._octolapse_settings.profiles.current_rendering().output_template, self._octolapse_settings.profiles.options.rendering['rendering_file_templates'], ): + logger.error("The rendering file template is invalid. Cannot start timelapse.") error = error_messages.get_error(["init", "rendering_file_template_invalid"]) return {"success": False, "error": error} - # make sure that at least one profile is available - if len(self._octolapse_settings.profiles.printers) == 0: - error = error_messages.get_error(["init", "no_printer_profile_exists"]) - return {"success": False, "error": error} - # check to make sure a printer is selected - if self._octolapse_settings.profiles.current_printer() is None: - error = error_messages.get_error(["init", "no_printer_profile_selected"]) - return {"success": False, "error": error} - # test the main settings directories + logger.verbose("Verify that the folder structure is valid.") success, errors = self._octolapse_settings.main_settings.test_directories( self.get_plugin_data_folder(), self.get_octoprint_timelapse_location() ) if not success: + logger.error("The Octolapse folder structure is invalid. Cannot start timelapse.") error_folders = ",".join([x["name"] for x in errors]) error = error_messages.get_error(["init", "directory_test_failed"], failed_directories=error_folders) return {"success": False, "error": error} # test rendering overlay font path + logger.verbose("Verify the overlay font path is correct, if rendering overlays are being used.") current_rendering_profile = self._octolapse_settings.profiles.current_rendering() if ( current_rendering_profile.overlay_text_template and not os.path.isfile(current_rendering_profile.overlay_font_path) ): + logger.error("The rendering overlay template font path is invalid. Cannot start timelapse.") error = error_messages.get_error( ["init", "overlay_font_path_not_found"], overlay_font_path=current_rendering_profile.overlay_font_path) return {"success": False, "error": error} - + logger.debug("Timelapse configuration is valid!") return {"success": True} def get_octoprint_timelapse_location(self): @@ -2618,6 +2649,7 @@ def get_timelapse_settings(self): # Create a copy of the settings to send to the Timelapse object. # We make this copy here so that editing settings vis the GUI won't affect the # current timelapse. + logger.debug("Getting timelapse settings.") settings_clone = self._octolapse_settings.clone() current_printer_clone = settings_clone.profiles.current_printer() return_value = { @@ -2772,9 +2804,11 @@ def get_timelapse_settings(self): return_value["overridable_printer_profile_settings"] = overridable_printer_profile_settings return_value["ffmpeg_path"] = ffmpeg_path return_value["gcode_file_path"] = gcode_file_path + logger.debug("Timelapse settings retrieved.") return return_value def start_timelapse(self, timelapse_settings, snapshot_plans=None): + try: self._timelapse.start_timelapse( timelapse_settings["settings"], @@ -2849,6 +2883,9 @@ def on_preprocessing_failed(self, errors): def pre_process_stabilization( self, timelapse_settings, parsed_command ): + logger.debug( + "Pre-Processing trigger detected, starting pre-processing thread." + ) if self._stabilization_preprocessor_thread is not None: self._stabilization_preprocessor_thread.join() self._stabilization_preprocessor_thread = None diff --git a/octoprint_octolapse/stabilization_preprocessing.py b/octoprint_octolapse/stabilization_preprocessing.py index f22ca7be..5e69e5ef 100644 --- a/octoprint_octolapse/stabilization_preprocessing.py +++ b/octoprint_octolapse/stabilization_preprocessing.py @@ -47,6 +47,9 @@ def __init__( ): super(StabilizationPreprocessingThread, self).__init__() + logger.debug( + "Pre-Processing thread is being constructed." + ) printer = timelapse_settings["settings"].profiles.current_printer() stabilization = timelapse_settings["settings"].profiles.current_stabilization() trigger = timelapse_settings["settings"].profiles.current_trigger() @@ -82,8 +85,15 @@ def __init__( self.lines_processed = 0 self.cpp_position_args = printer.get_position_args(timelapse_settings["overridable_printer_profile_settings"]) + logger.debug( + "Pre-Processing thread is constructed." + ) + def run(self): try: + logger.debug( + "Pre-Processing thread is running." + ) # perform the start callback self.start_callback() ret_val, options = self._run_stabilization() @@ -153,6 +163,10 @@ def run(self): missed_snapshots, quality_issues, errors, self.timelapse_settings, self.parsed_command ) + logger.debug( + "Pre-Processing thread is completed." + ) + def _get_quality_issues_from_cpp(self, issues): quality_issues = [] for issue in issues: diff --git a/octoprint_octolapse/timelapse.py b/octoprint_octolapse/timelapse.py index 3e7fa4dc..81478232 100644 --- a/octoprint_octolapse/timelapse.py +++ b/octoprint_octolapse/timelapse.py @@ -161,6 +161,9 @@ def start_timelapse( self, settings, overridable_printer_profile_settings, gcode_file_path, snapshot_plans=None ): + logger.debug( + "Starting the timelapse with the current configuration." + ) # we must supply the settings first! Else reset won't work properly. self._reset() # in case the settings have been destroyed and recreated @@ -227,6 +230,9 @@ def start_timelapse( raise TimelapseStartException(message, 'm114_not_supported') self._state = TimelapseState.WaitingForTrigger self.was_started = True + logger.debug( + "The timelapse configuration is set, waiting to stop the job-on-hold lock." + ) _stabilization_gcode_tags = { 'snapshot-init', From a3ab8ac5afc12e84a26831d233e2cac8af1a78f0 Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Tue, 26 Apr 2022 14:41:17 -0500 Subject: [PATCH 469/485] Switch to streaming download for camera test, which can timeout appropriately if the user enters a streaming url by mistake. --- octoprint_octolapse/camera.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/octoprint_octolapse/camera.py b/octoprint_octolapse/camera.py index b8791726..77b26897 100644 --- a/octoprint_octolapse/camera.py +++ b/octoprint_octolapse/camera.py @@ -562,9 +562,20 @@ def _test_web_camera(camera_profile, timeout_seconds=2, is_before_print_test=Fal camera_profile.webcam_settings.username, camera_profile.webcam_settings.password ) - - r = utility.requests_retry_session(session=s).request("GET", url, timeout=timeout_seconds) - + retry_session = utility.requests_retry_session(session=s) + r = retry_session.get(url, stream=True, timeout=timeout_seconds) + + body = [] + start_time = time.time() + for chunk in r.iter_content(1024): + body.append(chunk) + if time.time() > (start_time + timeout_seconds): + message = ( + "A read timeout occurred while connecting to {0} for the '{1}' camera profile. Are you using the video stream URL by mistake?" + .format(url, camera_profile.name) + ) + logger.exception(message) + raise CameraError('read-timeout', message) #if len(camera_profile.webcam_settings.username) > 0: # r = requests.get(url, auth=HTTPBasicAuth(camera_profile.webcam_settings.username, # camera_profile.webcam_settings.password), From 9f41e329ff1f8bb7fe24bd618893b79db58a53b8 Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Sun, 12 Feb 2023 10:16:23 -0600 Subject: [PATCH 470/485] Start removing support for Python 2.x --- octoprint_octolapse/__init__.py | 53 ++++--- octoprint_octolapse/camera.py | 40 +++-- octoprint_octolapse/gcode_commands.py | 15 +- octoprint_octolapse/log.py | 8 +- octoprint_octolapse/messenger_worker.py | 4 +- octoprint_octolapse/migration.py | 33 +++- octoprint_octolapse/position.py | 5 +- octoprint_octolapse/render.py | 27 +++- octoprint_octolapse/settings.py | 146 ++++++++++++------ octoprint_octolapse/settings_external.py | 7 +- octoprint_octolapse/settings_preprocessor.py | 21 ++- octoprint_octolapse/snapshot.py | 10 +- octoprint_octolapse/stabilization_gcode.py | 3 +- .../stabilization_preprocessing.py | 4 +- octoprint_octolapse/timelapse.py | 19 ++- octoprint_octolapse/utility.py | 1 + 16 files changed, 279 insertions(+), 117 deletions(-) diff --git a/octoprint_octolapse/__init__.py b/octoprint_octolapse/__init__.py index aac369f8..a151d4a5 100644 --- a/octoprint_octolapse/__init__.py +++ b/octoprint_octolapse/__init__.py @@ -40,13 +40,17 @@ import base64 import json import os -from flask import request, send_file, jsonify, Response, stream_with_context, send_from_directory, current_app, Flask - +# remove unused imports +#from flask import request, send_file, jsonify, Response, stream_with_context, send_from_directory, current_app, Flask +from flask import request, jsonify import threading import uuid -import six +# remove unused imports +# import six import time -from six.moves import queue +# Remove python 2 support +# from six.moves import queue +import queue as queue from tempfile import mkdtemp from distutils.version import LooseVersion from io import BytesIO @@ -54,17 +58,20 @@ import octoprint.filemanager from octoprint.events import Events from octoprint.server.util.flask import restricted_access -import octoprint_octolapse.stabilization_preprocessing +# remove unused import +# import octoprint_octolapse.stabilization_preprocessing import octoprint_octolapse.camera as camera import octoprint_octolapse.render as render import octoprint_octolapse.snapshot as snapshot import octoprint_octolapse.utility as utility import octoprint_octolapse.error_messages as error_messages from octoprint_octolapse.migration import migrate_files, get_version_from_settings_index -from octoprint_octolapse.position import Position +# remove unused import +# from octoprint_octolapse.position import Position from octoprint_octolapse.stabilization_gcode import SnapshotGcodeGenerator from octoprint_octolapse.gcode_commands import Commands -from octoprint_octolapse.render import TimelapseRenderJob, RenderingCallbackArgs +# remove unused import +# from octoprint_octolapse.render import TimelapseRenderJob, RenderingCallbackArgs from octoprint_octolapse.settings import OctolapseSettings, PrinterProfile, StabilizationProfile, TriggerProfile, \ CameraProfile, RenderingProfile, LoggingProfile, SlicerSettings, CuraSettings, OtherSlicerSettings, \ Simplify3dSettings, Slic3rPeSettings, SettingsJsonEncoder, MjpgStreamer, MainSettings @@ -75,14 +82,14 @@ from octoprint_octolapse.render import RenderError, RenderingProcessor, RenderingCallbackArgs, RenderJobInfo #import octoprint_octolapse_setuptools as octoprint_octolapse_setuptools #import octoprint_octolapse_setuptools.github_release as github_release - -try: - # noinspection PyCompatibility - from urlparse import urlparse as urlparse -except ImportError: - # noinspection PyUnresolvedReferences - from urllib.parse import urlparse as urlparse - +# remove python 2 compatibility +#try: +# # noinspection PyCompatibility +# from urlparse import urlparse as urlparse +#except ImportError: +# # noinspection PyUnresolvedReferences +# from urllib.parse import urlparse as urlparse +from urllib.parse import urlparse as urlparse # configure all imported loggers logging_configurator.configure_loggers() @@ -831,7 +838,9 @@ def check_for_profile_updates_request(self): available_profiles = self.check_for_updates(ignore_suppression=ignore_suppression) available_profile_count = 0 if available_profiles: - for key, profile_type in six.iteritems(available_profiles): + # remove python 2 support + # for key, profile_type in six.iteritems(available_profiles): + for key, profile_type in available_profiles.items(): available_profile_count += len(profile_type) return jsonify({ "success": True, @@ -853,7 +862,9 @@ def update_profiles_from_server(self): "num_updated": 0 }) num_profiles = 0 - for profile_type, updatable_profiles in six.iteritems(profiles_to_update): + # remove python 2 support + # for profile_type, updatable_profiles in six.iteritems(profiles_to_update): + for profile_type, updatable_profiles in profiles_to_update.items(): # now iterate through the printer profiles for updatable_profile in updatable_profiles: current_profile = self._octolapse_settings.profiles.get_profile( @@ -897,7 +908,9 @@ def suppress_server_updates(self): with self.automatic_update_lock: has_updated = False - for profile_type, updatable_profiles in six.iteritems(self._octolapse_settings.profiles.get_updatable_profiles_dict()): + # remove python 2 support + # for profile_type, updatable_profiles in six.iteritems(self._octolapse_settings.profiles.get_updatable_profiles_dict()): + for profile_type, updatable_profiles in self._octolapse_settings.profiles.get_updatable_profiles_dict().items(): # now iterate through the printer profiles for updatable_profile in updatable_profiles: current_profile = self._octolapse_settings.profiles.get_profile( @@ -2285,7 +2298,9 @@ def check_for_updates(self, force_updates=False, notify=False, ignore_suppressio if profiles_to_update: if notify: num_available = 0 - for key, profile_type in six.iteritems(profiles_to_update): + # remove python 2 support + # for key, profile_type in six.iteritems(profiles_to_update): + for key, profile_type in profiles_to_update.items(): num_available += len(profile_type) data = { "type": "updated-profiles-available", diff --git a/octoprint_octolapse/camera.py b/octoprint_octolapse/camera.py index 77b26897..11463722 100644 --- a/octoprint_octolapse/camera.py +++ b/octoprint_octolapse/camera.py @@ -22,7 +22,8 @@ ################################################################################## from __future__ import unicode_literals import json -import six +# remove unused imports +# import six import octoprint_octolapse.utility as utility import octoprint_octolapse.script as script from threading import Thread @@ -35,7 +36,8 @@ import os import errno # Todo: Do we need to add this to setup.py? -from requests.auth import HTTPBasicAuth +# remove unused import +# from requests.auth import HTTPBasicAuth from requests.exceptions import SSLError from octoprint_octolapse.settings import CameraProfile, MjpgStreamerControl, MjpgStreamer from tempfile import mkdtemp @@ -78,7 +80,9 @@ def _load_camera_types(data_path): server_types = camera_files["server_types"] # loop through each server type - for key, server_type in six.iteritems(server_types): + # remove python 2 compatibility + # for key, server_type in six.iteritems(server_types): + for key, server_type in server_types.items(): camera_types["server_types"][key] = {'cameras': {}} # load all of the individual camera type info files for the current server for file_name in server_type["file_names"]: @@ -125,7 +129,9 @@ def get_webcam_type(data_path, server_type, data): control_setting.update(control) controls[control_setting.id] = control_setting elif isinstance(data, dict): - for key, control in six.iteritems(data): + # remove python 2 compatibility + # for key, control in six.iteritems(data): + for key, control in data.items(): control_setting = MjpgStreamerControl() control_setting.update(control) controls[key] = control_setting @@ -141,7 +147,9 @@ def get_webcam_type(data_path, server_type, data): cameras = CameraControl.camera_types["server_types"][server_type]["cameras"] # iterate the cameras dictionary for the given server type if possible - for key, camera in six.iteritems(cameras): + # remove python 2 compatibility + # for key, camera in six.iteritems(cameras): + for key, camera in cameras.items(): # see if the current camera type matches the given data if CameraControl._is_webcam_type(server_type, camera, data): return { @@ -209,7 +217,9 @@ def apply_camera_settings(cameras, retries=3, backoff_factor=0.1, no_wait=False) def run_on_print_start_script(cameras): # build the arg list args_list = [] - for key, camera in six.iteritems(cameras): + # remove python 2 compatibility + # for key, camera in six.iteritems(cameras): + for key, camera in cameras.items(): script_path = camera.on_print_start_script.strip() if not script_path or not camera.enabled: # skip any cameras where there is no on print start script or the camera is disabled @@ -234,7 +244,9 @@ def run_on_print_start_script(cameras): def run_on_print_end_script(cameras): # build the arg list args_list = [] - for key, camera in six.iteritems(cameras): + # remove python 2 compatibility + # for key, camera in six.iteritems(cameras): + for key, camera in cameras.items(): script_path = camera.on_print_end_script.strip() if not script_path or not camera.enabled: # skip any cameras where there is no on print start script or the camera is disabled @@ -954,8 +966,9 @@ def load_webcam_defaults( return False, errors # set the control values to the defaults controls = settings["webcam_settings"]["mjpg_streamer"]["controls"] - for key, control in six.iteritems(controls): - + # remove python 2 compatibility + # for key, control in six.iteritems(controls): + for key, control in controls.items(): control.value = control.default controls[key] = control.to_dict() @@ -1007,7 +1020,9 @@ def __init__( self.password = password self.ignore_ssl_error = ignore_ssl_error self.camera_name = camera_name - if isinstance(self.address, six.string_types): + # remove python 2 compatibility + # if isinstance(self.address, six.string_types): + if isinstance(self.address, str): self.address = self.address.strip() self.retries = retries self.backoff_factor=backoff_factor @@ -1159,8 +1174,9 @@ def apply_mjpg_streamer_settings(self): controls_list = [] - - for key, control in six.iteritems(self.controls): + # remove python 2 compatibility + # for key, control in six.iteritems(self.controls): + for key, control in self.controls.items(): controls_list.append(control) # Sort the controls. They need to be sent in order. diff --git a/octoprint_octolapse/gcode_commands.py b/octoprint_octolapse/gcode_commands.py index cdeec390..508a5896 100644 --- a/octoprint_octolapse/gcode_commands.py +++ b/octoprint_octolapse/gcode_commands.py @@ -21,7 +21,8 @@ # following email address: FormerLurker@pm.me ################################################################################## from __future__ import unicode_literals -from six import string_types +# remove unused using +# from six import string_types import operator import re @@ -49,7 +50,9 @@ def parse_float_positive(parameter_string): @staticmethod def parse_float(parameter_string): - assert (isinstance(parameter_string, string_types)) + # remove python 2 support + # assert (isinstance(parameter_string, string_types)) + assert (isinstance(parameter_string, str)) parameter_string = parameter_string.lstrip() index = 0 @@ -90,7 +93,9 @@ def parse_float(parameter_string): @staticmethod def parse_int(parameter_string): - assert (isinstance(parameter_string, string_types)) + # remove python 2 support + # assert (isinstance(parameter_string, string_types)) + assert (isinstance(parameter_string, str)) parameter_string = parameter_string.lstrip() index = 0 int_string = "" @@ -115,7 +120,9 @@ def parse_int(parameter_string): @staticmethod def parse_tool(parameter_string): - assert (isinstance(parameter_string, string_types)) + # remove python 2 support + # assert (isinstance(parameter_string, string_types)) + assert (isinstance(parameter_string, str)) parameter_string = parameter_string.strip().upper() if parameter_string[0] in ['?','X','C']: diff --git a/octoprint_octolapse/log.py b/octoprint_octolapse/log.py index 15f35e7e..898cd8c1 100644 --- a/octoprint_octolapse/log.py +++ b/octoprint_octolapse/log.py @@ -24,7 +24,8 @@ import logging import datetime as datetime import os -import six +# Remove python 2 support +# import six from octoprint.logging.handlers import AsyncLogHandlerMixin, CleaningTimedRotatingFileHandler @@ -92,9 +93,10 @@ def delete_all_backups(self): os.remove(s) self.backupCount = backup_count - -@six.add_metaclass(Singleton) +# remove python 2 support +# @six.add_metaclass(Singleton) class LoggingConfigurator(object): + __metaclass__ = Singleton BACKUP_COUNT = 3 def __init__(self): diff --git a/octoprint_octolapse/messenger_worker.py b/octoprint_octolapse/messenger_worker.py index 0f6eb577..42860da6 100644 --- a/octoprint_octolapse/messenger_worker.py +++ b/octoprint_octolapse/messenger_worker.py @@ -23,7 +23,9 @@ from __future__ import absolute_import from __future__ import unicode_literals from threading import Thread, Lock -from six.moves import queue +# Remove python 2 support +# from six.moves import queue +import queue as queue from collections import deque import time from octoprint_octolapse.log import LoggingConfigurator diff --git a/octoprint_octolapse/migration.py b/octoprint_octolapse/migration.py index f221b74c..292119a4 100644 --- a/octoprint_octolapse/migration.py +++ b/octoprint_octolapse/migration.py @@ -3,7 +3,8 @@ import json import sys import os -import six +# remove unused using +# import six import copy import shutil # create the module level logger @@ -337,8 +338,11 @@ def migrate_pre_0_3_5_rc1_dev(current_version, settings_dict, default_settings_p # set the current trigger profile profiles['current_trigger_profile_guid'] = settings_dict['current_snapshot_profile_guid'] + + # Remove python 2 support + # for key, default_profile in six.iteritems(default_settings["profiles"]["triggers"]): # add all of the new triggers (the non-real time triggers) - for key, default_profile in six.iteritems(default_settings["profiles"]["triggers"]): + for key, default_profile in default_settings["profiles"]["triggers"].items(): if default_profile["trigger_type"] != 'real-time': # add this setting, it's new! profiles["triggers"][key] = default_profile @@ -432,8 +436,11 @@ def migrate_pre_0_3_5_rc1_dev(current_version, settings_dict, default_settings_p del camera['powerline_frequency'], profiles['cameras'][camera['guid']] = camera + # Remove python 2 support + # for key, default_profile in six.iteritems(default_settings['profiles']['debug']): + # UPGRADE THE DEBUG PROFILES - remove and replace the debug profiles, they are too different to salvage - for key, default_profile in six.iteritems(default_settings['profiles']['debug']): + for key, default_profile in default_settings['profiles']['debug'].items(): profiles['debug'][key] = default_profile # set the default debug profile profiles["current_debug_profile_guid"] = default_settings['profiles']['current_debug_profile_guid'] @@ -488,7 +495,9 @@ def migrate_pre_0_3_5_rc1_dev(current_version, settings_dict, default_settings_p for profile_default in profile_default_types: profile_type = profile_default["profile_type"] default_profile_name = profile_default["default_profile_name"] - for key, profile in six.iteritems(profiles[profile_type]): + # Remove python 2 support + # for key, profile in six.iteritems(profiles[profile_type]): + for key, profile in profiles[profile_type].items(): defaults_copy = copy.deepcopy(default_settings["profiles"]["defaults"][default_profile_name]) defaults_copy.update(profile) profiles[profile_type][key] = defaults_copy @@ -507,7 +516,9 @@ def migrate_pre_0_4_0_rc1_dev2(current_version, settings_dict, default_settings_ settings_dict["profiles"]["triggers"] = {} # add the default triggers triggers = default_settings["profiles"]["triggers"] - for key, trigger in six.iteritems(triggers): + # Remove python 2 support + # for key, trigger in six.iteritems(triggers): + for key, trigger in triggers.items(): # Add default triggers settings_dict["profiles"]["triggers"][key] = trigger @@ -522,7 +533,9 @@ def migrate_pre_0_4_0_rc1_dev3(current_version, settings_dict, default_settings_ # adjust each slicer profile to account for the new multiple extruder settings printers = settings_dict["profiles"]["printers"] default_settings = get_default_settings(default_settings_path) - for key, printer in six.iteritems(printers): + # Remove python 2 support + # for key, printer in six.iteritems(printers): + for key, printer in printers.items(): # Adjust gcode generation settings if "gcode_generation_settings" in printer: @@ -647,7 +660,9 @@ def migrate_pre_0_4_0_rc1_dev3(current_version, settings_dict, default_settings_ slic3r_pe.pop("deretract_speed", None) renderings = settings_dict["profiles"]["renderings"] - for key, render in six.iteritems(renderings): + # Remove python 2 support + # for key, render in six.iteritems(renderings): + for key, render in renderings.items(): render["archive_snapshots"] = not render.get("cleanup_after_render_complete", True) render.pop("cleanup_after_render_complete",None) render.pop("cleanup_after_render_fail",None) @@ -656,7 +671,9 @@ def migrate_pre_0_4_0_rc1_dev3(current_version, settings_dict, default_settings_ render.pop("snapshots_to_skip_beginning", None) triggers = settings_dict["profiles"]["triggers"] - for key, trigger in six.iteritems(triggers): + # Remove python 2 support + #for key, trigger in six.iteritems(triggers): + for key, trigger in triggers.items(): if "trigger_type" in trigger and trigger["trigger_type"] == 'smart-layer': trigger["trigger_type"] = "smart" diff --git a/octoprint_octolapse/position.py b/octoprint_octolapse/position.py index 37a1b667..0821e59c 100644 --- a/octoprint_octolapse/position.py +++ b/octoprint_octolapse/position.py @@ -23,8 +23,9 @@ from __future__ import unicode_literals import octoprint_octolapse.utility as utility from octoprint_octolapse.settings import OctolapseGcodeSettings -from octoprint_octolapse.gcode_processor import GcodeProcessor, Pos, Extruder - +# remove unused import +# from octoprint_octolapse.gcode_processor import GcodeProcessor, Pos, Extruder +from octoprint_octolapse.gcode_processor import GcodeProcessor, Pos # create the module level logger from octoprint_octolapse.log import LoggingConfigurator logging_configurator = LoggingConfigurator() diff --git a/octoprint_octolapse/render.py b/octoprint_octolapse/render.py index 471a4954..c0e6c524 100644 --- a/octoprint_octolapse/render.py +++ b/octoprint_octolapse/render.py @@ -26,8 +26,11 @@ import os import sys import threading -from six.moves import queue -from six import string_types, iteritems +# Remove python 2 support +# from six.moves import queue +import queue as queue +# remove unused usings +# from six import string_types, iteritems import time import json import copy @@ -825,7 +828,9 @@ def import_snapshot_archive(self, snapshot_archive_path, prevent_archive=False): archive_files_temp_dict = {} # now create the directories and files and place them in the temp snapshot directory - for job_guid, job in iteritems(archive_files_dict): + # remove python 2 support + # for job_guid, job in iteritems(archive_files_dict): + for job_guid, job in archive_files_dict.items(): job_path = utility.get_temporary_snapshot_job_path(temporary_directory, job_guid) if not os.path.isdir(job_path): os.makedirs(job_path) @@ -835,7 +840,9 @@ def import_snapshot_archive(self, snapshot_archive_path, prevent_archive=False): with zip_file.open(job_info_file["fileinfo"]) as info_file: with open(file_path, 'wb') as target_file: target_file.write(info_file.read()) - for camera_guid, camera in iteritems(job["cameras"]): + # remove python 2 support + # for camera_guid, camera in iteritems(job["cameras"]): + for camera_guid, camera in job["cameras"].items(): camera_path = utility.get_temporary_snapshot_job_camera_path( temporary_directory, job_guid, camera_guid ) @@ -860,9 +867,13 @@ def import_snapshot_archive(self, snapshot_archive_path, prevent_archive=False): } # now we should have extracted all of the items, add the job to the queue for these cameras has_created_jobs = False - for job_guid, job in iteritems(archive_files_dict): + # remove python 2 support + # for job_guid, job in iteritems(archive_files_dict): + for job_guid, job in archive_files_dict.items(): has_created_jobs = True - for camera_guid, camera in iteritems(job["cameras"]): + # remove python 2 support + # for camera_guid, camera in iteritems(job["cameras"]): + for camera_guid, camera in job["cameras"].items(): # add this job to the queue as an imported item if prevent_archive: # add a file that will signify to the rendering engine that no archive should be created @@ -2226,7 +2237,9 @@ def add_overlay(image, text_template, format_vars, font_path, font_size, overlay d = ImageDraw.Draw(text_image) # Process the text position to improve the alignment. - if isinstance(overlay_location, string_types): + # remove python 2 support + # if isinstance(overlay_location, string_types): + if isinstance(overlay_location, str): overlay_location = json.loads(overlay_location) x, y = tuple(overlay_location) # valign. diff --git a/octoprint_octolapse/settings.py b/octoprint_octolapse/settings.py index 038cbc29..1932ed62 100644 --- a/octoprint_octolapse/settings.py +++ b/octoprint_octolapse/settings.py @@ -34,15 +34,18 @@ import octoprint_octolapse.utility as utility import octoprint_octolapse.log as log import math -try: - from collections.abc import Iterable -except ImportError: - # Python 2.7 - from collections import Iterable +# remove python 2 support +#try: +# from collections.abc import Iterable +#except ImportError: +# # Python 2.7 +# from collections import Iterable +from collections.abc import Iterable import octoprint_octolapse.settings_preprocessor as settings_preprocessor import octoprint_octolapse.migration as migration from octoprint_octolapse.gcode_processor import GcodeProcessor, ParsedCommand -import six +# remove unused usings +# import six from octoprint_octolapse.error_messages import OctolapseException import inspect # create the module level logger @@ -54,7 +57,9 @@ class SettingsJsonEncoder(json.JSONEncoder): def default(self, obj): if issubclass(type(obj), Settings): - return {k: v for (k, v) in six.iteritems(obj.to_dict()) if not k.startswith("_")} + # Remove python 2 support + # return {k: v for (k, v) in six.iteritems(obj.to_dict()) if not k.startswith("_")} + return {k: v for (k, v) in obj.to_dict().items() if not k.startswith("_")} elif issubclass(type(obj), StaticSettings): return obj.__dict__ # Let the base class default method raise the TypeError @@ -101,7 +106,9 @@ def to_dict(self): def to_json(self): # remove private variables - filtered_dict = {k: v for (k, v) in six.iteritems(self.to_dict()) if not k.startswith("_")} + # Remove python 2 support + # filtered_dict = {k: v for (k, v) in six.iteritems(self.to_dict()) if not k.startswith("_")} + filtered_dict = {k: v for (k, v) in self.to_dict().items() if not k.startswith("_")} return json.dumps(filtered_dict, cls=SettingsJsonEncoder) @classmethod @@ -129,7 +136,9 @@ def _update(source, iterable): if key.startswith("_"): continue class_item = getattr(source, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if isinstance(class_item, Settings): class_item.update(value) elif isinstance(class_item, StaticSettings): @@ -1080,7 +1089,9 @@ def get_options(): @staticmethod def get_extruder_trigger_value(value): - if isinstance(value, six.string_types): + # Remove python 2 support + #if isinstance(value, six.string_types): + if isinstance(value, str): if value is None or len(value) == 0: return None elif value.lower() == TriggerProfile.EXTRUDER_TRIGGER_REQUIRED_VALUE: @@ -1172,7 +1183,9 @@ def get_overlay_outline_color(self): @staticmethod def _get_color_(rgba_color): overlay_text_color = [255, 255, 255, 1.0] - if isinstance(rgba_color, six.string_types): + # Remove python 2 support + # if isinstance(rgba_color, six.string_types): + if isinstance(rgba_color, str): overlay_text_color = json.loads(rgba_color) elif isinstance(rgba_color, list): # make sure to copy the list so we don't alter the original @@ -1194,7 +1207,9 @@ def _get_color_(rgba_color): @classmethod def try_convert_value(cls, destination, value, key): if key in ['overlay_text_color', 'overlay_outline_color']: - if isinstance(value, six.string_types): + # Remove python 2 support + # if isinstance(value, six.string_types): + if isinstance(value, str): value = json.loads(value) if not isinstance(value, list): return None @@ -1297,8 +1312,9 @@ def controls_match_server(self, server_settings): # first see if the number of controls match if len(self.controls) != len(server_settings): return False - - for key, control in six.iteritems(self.controls): + # Remove python 2 support + # for key, control in six.iteritems(self.controls): + for key, control in self.controls.items(): # convert the key to a string key = str(key) # if the key is not in the server_settings_dict, they don't match @@ -1320,7 +1336,9 @@ def update(self, iterable, **kwargs): for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == 'controls': self.controls = {} if isinstance(value, dict): @@ -1462,14 +1480,18 @@ def update(self, iterable, **kwargs): for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == 'mjpg_streamer': self.mjpg_streamer.controls = {} if "controls" in value: controls = value["controls"] # controls might be a dict or a list, iterate appropriately if isinstance(controls, dict): - for key, control in six.iteritems(value["controls"]): + # Remove python 2 support + # for key, control in six.iteritems(value["controls"]): + for key, control in value["controls"].items(): if "id" in control: self.mjpg_streamer.controls[key] = ( MjpgStreamerControl.create_from(control) @@ -2085,7 +2107,9 @@ def update(self, iterable, **kwargs): for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): profiles_found = True if key == 'printers': @@ -2164,37 +2188,44 @@ def get_updatable_profiles_dict(self): "logging": [] } has_profiles = False - for key, printer_profile in six.iteritems(self.printers): + # Remove python 2 support + # for key, printer_profile in six.iteritems(self.printers): + for key, printer_profile in self.printers.items(): if printer_profile.is_updatable_from_server(): has_profiles = True identifiers = printer_profile.get_server_update_identifiers_dict() profiles["printer"].append(identifiers) - - for key, stabilization_profile in six.iteritems(self.stabilizations): + # Remove python 2 support + # for key, stabilization_profile in six.iteritems(self.stabilizations): + for key, stabilization_profile in self.stabilizations.items(): if stabilization_profile.is_updatable_from_server(): has_profiles = True identifiers = stabilization_profile.get_server_update_identifiers_dict() profiles["stabilization"].append(identifiers) - - for key, trigger_profile in six.iteritems(self.triggers): + # Remove python 2 support + # for key, trigger_profile in six.iteritems(self.triggers): + for key, trigger_profile in self.triggers.items(): if trigger_profile.is_updatable_from_server(): has_profiles = True identifiers = trigger_profile.get_server_update_identifiers_dict() profiles["trigger"].append(identifiers) - - for key, rendering_profile in six.iteritems(self.renderings): + # Remove python 2 support + # for key, rendering_profile in six.iteritems(self.renderings): + for key, rendering_profile in self.renderings.items(): if rendering_profile.is_updatable_from_server(): has_profiles = True identifiers = rendering_profile.get_server_update_identifiers_dict() profiles["rendering"].append(identifiers) - - for key, camera_profile in six.iteritems(self.cameras): + # Remove python 2 support + # for key, camera_profile in six.iteritems(self.cameras): + for key, camera_profile in self.cameras.items(): if camera_profile.is_updatable_from_server(): has_profiles = True identifiers = camera_profile.get_server_update_identifiers_dict() profiles["camera"].append(identifiers) - - for key, logging_profile in six.iteritems(self.logging): + # Remove python 2 support + # for key, logging_profile in six.iteritems(self.logging): + for key, logging_profile in self.logging.items(): if logging_profile.is_updatable_from_server(): has_profiles = True identifiers = logging_profile.get_server_update_identifiers_dict() @@ -2729,7 +2760,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): try: if key in ['retraction_enable', 'retraction_hop_enabled']: self.__dict__[key] = None if value is None else bool(value) @@ -2839,13 +2872,18 @@ def update_settings_from_gcode(self, settings_dict, printer_profile): extruder = self.extruders[extruder_index] # for fun, make sure the setting exists in the extruder class class_item = getattr(extruder, extruder_setting_name, '{octolapse_no_property_found}') + # Remove python 2 support if not ( - isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}' + # Remove python 2 support + # isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}' + isinstance(class_item, str) and class_item == '{octolapse_no_property_found}' ): extruder.__dict__[extruder_setting_name] = value continue class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == 'version': self.version = value elif isinstance(class_item, Settings): @@ -2863,7 +2901,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == "extruders": self.extruders = [] for extruder in value: @@ -2923,7 +2963,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): try: if key in ['extruder_use_retract']: self.__dict__[key] = None if value is None else bool(value) @@ -3078,7 +3120,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == "extruders": self.extruders = [] for extruder in value: @@ -3173,7 +3217,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): try: self.__dict__[key] = None if value is None else float(value) except ValueError as e: @@ -3221,7 +3267,9 @@ def parse_percent(parse_string): try: if parse_string is None: return None - if isinstance(parse_string, six.string_types): + # Remove python 2 support + # if isinstance(parse_string, six.string_types): + if isinstance(parse_string, str): percent_index = "{}".format(parse_string).strip().find('%') if percent_index < 1: return None @@ -3268,14 +3316,18 @@ def update_settings_from_gcode(self, settings_dict, printer_profile): extruder = self.extruders[extruder_index] class_item = getattr(extruder, key, '{octolapse_no_property_found}') if not ( - isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}' + # Remove python 2 support + # isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}' + isinstance(class_item, str) and class_item == '{octolapse_no_property_found}' ): # All of the extruder values are floats extruder.__dict__[key] = float(extruder_value) extruder_index += 1 continue class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + #if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == 'version': self.version = value["version"] elif isinstance(class_item, Settings): @@ -3287,7 +3339,9 @@ def update_settings_from_gcode(self, settings_dict, printer_profile): @classmethod def try_convert_value(cls, destination, value, key): - if value is None or (isinstance(value, six.string_types) and value == 'None'): + # Remove python 2 support + # if value is None or (isinstance(value, six.string_types) and value == 'None'): + if value is None or (isinstance(value, str) and value == 'None'): return None return super(Slic3rPeSettings, cls).try_convert_value(destination, value, key) @@ -3299,7 +3353,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + #if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == "extruders": self.extruders = [] for extruder in value: @@ -3360,7 +3416,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): try: if key in ['retract_before_move','lift_when_retracted']: self.__dict__[key] = None if value is None else bool(value) @@ -3419,7 +3477,9 @@ def update(self, iterable, **kwargs): item_to_iterate = iterable.__dict__ for key, value in item_to_iterate.items(): class_item = getattr(self, key, '{octolapse_no_property_found}') - if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + # Remove python 2 support + # if not (isinstance(class_item, six.string_types) and class_item == '{octolapse_no_property_found}'): + if not (isinstance(class_item, str) and class_item == '{octolapse_no_property_found}'): if key == "extruders": self.extruders = [] for extruder in value: diff --git a/octoprint_octolapse/settings_external.py b/octoprint_octolapse/settings_external.py index 4c0881dc..bca76f9b 100644 --- a/octoprint_octolapse/settings_external.py +++ b/octoprint_octolapse/settings_external.py @@ -25,7 +25,8 @@ import requests import uuid import json -import six +# remove unused usings +# import six import os import copy @@ -136,7 +137,9 @@ def check_for_updates(available_profiles, updatable_profiles, force_updates, ign available_profiles is not None ): # loop through the updatable profile dicts - for profile_type, value in six.iteritems(updatable_profiles): + # Remove python 2 support + # for profile_type, value in six.iteritems(updatable_profiles): + for profile_type, value in updatable_profiles.items(): # loop through the profiles for updatable_profile in value: # get the available profile for the updatable profile keys diff --git a/octoprint_octolapse/settings_preprocessor.py b/octoprint_octolapse/settings_preprocessor.py index 2cc8a974..bd7ce0b5 100644 --- a/octoprint_octolapse/settings_preprocessor.py +++ b/octoprint_octolapse/settings_preprocessor.py @@ -27,8 +27,9 @@ import datetime from file_read_backwards import FileReadBackwards import re -import six -import string +# remove unused usings +# import six +# import string # create the module level logger from octoprint_octolapse.log import LoggingConfigurator logging_configurator = LoggingConfigurator() @@ -229,7 +230,9 @@ def is_complete(self): @staticmethod def get_comment(self, line): - assert (isinstance(line, six.string_types)) + # Remove python 2 support + # assert (isinstance(line, six.string_types)) + assert (isinstance(line, str)) match_position = line.find(u';') if match_position > -1 and len(line) > match_position + 1: return line[match_position + 1:].strip() @@ -275,7 +278,9 @@ def on_apply_filter(self, filter_tags=None): self.active_settings_dictionary = {} self.active_regex_definitions = {} # copy any matching settings definitions - for key, setting in six.iteritems(self.all_settings_dictionary): + # Remove python 2 support + # for key, setting in six.iteritems(self.all_settings_dictionary): + for key, setting in self.all_settings_dictionary.items(): if ( filter_tags is None or len(filter_tags) == 0 @@ -285,7 +290,9 @@ def on_apply_filter(self, filter_tags=None): setting.name, setting.parsing_function, setting.tags ) # apply regex filters - for key, regex in six.iteritems(self.all_regex_definitions): + # Remove python 2 support + # for key, regex in six.iteritems(self.all_regex_definitions): + for key, regex in self.all_regex_definitions.items(): if ( filter_tags is None or len(filter_tags) == 0 @@ -316,7 +323,9 @@ def process_line(self, line, line_number, process_type): self.reverse_lines_processed += 1 logger.verbose("Process type: %s, line: %s, gcode: %s", process_type, line_number, line) - for key, regex_definition in six.iteritems(self.active_regex_definitions): + # Remove python 2 support + # for key, regex_definition in six.iteritems(self.active_regex_definitions): + for key, regex_definition in self.active_regex_definitions.items(): if regex_definition.match_once and regex_definition.has_matched: continue try: diff --git a/octoprint_octolapse/snapshot.py b/octoprint_octolapse/snapshot.py index c14405f1..b90ef9a4 100644 --- a/octoprint_octolapse/snapshot.py +++ b/octoprint_octolapse/snapshot.py @@ -21,14 +21,16 @@ # following email address: FormerLurker@pm.me ################################################################################## from __future__ import unicode_literals -import six +# remove unused usings +# import six import os import json from csv import DictWriter from time import sleep import requests from PIL import ImageFile -import sys +# remove unused usings +# import sys # PIL is in fact in setup.py. from requests.auth import HTTPBasicAuth from threading import Thread, Event @@ -675,7 +677,9 @@ def __init__( on_post_processing_error_callback=on_post_processing_error_callback ) self.address = self.snapshot_job_info.camera.webcam_settings.address - if isinstance(self.address, six.string_types): + # Remove python 2 support + # if isinstance(self.address, six.string_types): + if isinstance(self.address, str): self.address = self.address.strip() self.username = self.snapshot_job_info.camera.webcam_settings.username self.password = self.snapshot_job_info.camera.webcam_settings.password diff --git a/octoprint_octolapse/stabilization_gcode.py b/octoprint_octolapse/stabilization_gcode.py index df590cd2..c9c13bcd 100644 --- a/octoprint_octolapse/stabilization_gcode.py +++ b/octoprint_octolapse/stabilization_gcode.py @@ -27,7 +27,8 @@ from octoprint_octolapse.trigger import Triggers # create the module level logger from octoprint_octolapse.log import LoggingConfigurator -import json +# remove unused using +# import json logging_configurator = LoggingConfigurator() logger = logging_configurator.get_logger(__name__) diff --git a/octoprint_octolapse/stabilization_preprocessing.py b/octoprint_octolapse/stabilization_preprocessing.py index 5e69e5ef..bf067628 100644 --- a/octoprint_octolapse/stabilization_preprocessing.py +++ b/octoprint_octolapse/stabilization_preprocessing.py @@ -22,7 +22,9 @@ ################################################################################## from __future__ import unicode_literals from threading import Thread -from six.moves import queue +# Remove python 2 support +# from six.moves import queue +import queue as queue from octoprint_octolapse.stabilization_gcode import SnapshotPlan, SnapshotGcodeGenerator from octoprint_octolapse.settings import PrinterProfile, TriggerProfile, StabilizationProfile import GcodePositionProcessor diff --git a/octoprint_octolapse/timelapse.py b/octoprint_octolapse/timelapse.py index 81478232..d5a60be8 100644 --- a/octoprint_octolapse/timelapse.py +++ b/octoprint_octolapse/timelapse.py @@ -24,8 +24,11 @@ import threading import time import uuid -from six import iteritems, string_types -from six.moves import queue +# remove unused usings +# from six import iteritems, string_types +# Remove python 2 support +# from six.moves import queue +import queue as queue import os import octoprint_octolapse.utility as utility from octoprint_octolapse.stabilization_gcode import SnapshotGcodeGenerator, SnapshotGcode @@ -339,7 +342,9 @@ def _take_snapshots(self, metadata): error_count += 1 if isinstance(result.error, SnapshotError): error_message = result.error.message - elif isinstance(result.error, string_types): + # remove python 2 support + # elif isinstance(result.error, string_types): + elif isinstance(result.error, str): error_message = result.error if result.job_type not in errors: @@ -353,7 +358,9 @@ def _take_snapshots(self, metadata): error_message = "" if error_count == 1: - for key, value in iteritems(errors): + # remove python 2 support + # for key, value in iteritems(errors): + for key, value in errors.items(): error = value["error"] if key == 'before-snapshot': error_message = "Before Snapshot Script Error: {0}" @@ -368,7 +375,9 @@ def _take_snapshots(self, metadata): before_snapshot_error_count = False after_snapshot_error_count = False snapshot_error_count = False - for key, value in iteritems(errors): + # remove python 2 support + # for key, value in iteritems(errors): + for key, value in errors.items(): if key == 'before-snapshot': before_snapshot_error_count = value["count"] elif key == 'after-snapshot': diff --git a/octoprint_octolapse/utility.py b/octoprint_octolapse/utility.py index 4490b9b1..f242223d 100644 --- a/octoprint_octolapse/utility.py +++ b/octoprint_octolapse/utility.py @@ -38,6 +38,7 @@ import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry +# Todo: Determin if this is still necessary. try: from slugify import Slugify except ImportError: From 215845c743e56d026adcdc529998263e5ff91091 Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Sun, 12 Feb 2023 11:15:42 -0600 Subject: [PATCH 471/485] Fix font-awesome icons. --- .../octolapse_profiles_logging.jinja2 | 6 ++--- .../octolapse_status_printer_state.jinja2 | 26 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/octoprint_octolapse/templates/octolapse_profiles_logging.jinja2 b/octoprint_octolapse/templates/octolapse_profiles_logging.jinja2 index 41c25925..44085fb5 100644 --- a/octoprint_octolapse/templates/octolapse_profiles_logging.jinja2 +++ b/octoprint_octolapse/templates/octolapse_profiles_logging.jinja2 @@ -46,8 +46,8 @@

Manage Log Files

- - + + Download Log @@ -112,7 +112,7 @@ value: log_level">
- +
diff --git a/octoprint_octolapse/templates/octolapse_status_printer_state.jinja2 b/octoprint_octolapse/templates/octolapse_status_printer_state.jinja2 index 5ce3569e..1393714a 100644 --- a/octoprint_octolapse/templates/octolapse_status_printer_state.jinja2 +++ b/octoprint_octolapse/templates/octolapse_status_printer_state.jinja2 @@ -31,10 +31,10 @@ - Layer: + Layer: - Height:  mm + Height:  mm @@ -56,7 +56,7 @@ - +
@@ -68,7 +68,7 @@ - +
@@ -77,7 +77,7 @@ - +
@@ -85,7 +85,7 @@ - +
@@ -99,7 +99,7 @@ + data-bind="css:PrinterState.getCheckedIconClass(PrinterState.is_printer_primed(),'fa-check','fa-times','fa-times')">
@@ -117,7 +117,7 @@ - + @@ -131,7 +131,7 @@ - + @@ -145,7 +145,7 @@ - + @@ -160,7 +160,7 @@ - + @@ -176,7 +176,7 @@ - + @@ -191,7 +191,7 @@ - + From c6c86782cdbfc3590fa4b1bf2e63064eb459b16b Mon Sep 17 00:00:00 2001 From: Formerlurker Date: Sun, 2 Apr 2023 10:23:21 -0500 Subject: [PATCH 472/485] Bump version to 0.4.2, update dates. --- README.md | 2 +- octoprint_octolapse/__init__.py | 2 +- octoprint_octolapse/camera.py | 2 +- octoprint_octolapse/error_messages.py | 2 +- octoprint_octolapse/gcode_commands.py | 2 +- octoprint_octolapse/gcode_processor.py | 2 +- octoprint_octolapse/log.py | 2 +- octoprint_octolapse/messenger_worker.py | 2 +- octoprint_octolapse/migration.py | 21 ++++++++++++++++ octoprint_octolapse/position.py | 2 +- octoprint_octolapse/render.py | 2 +- octoprint_octolapse/script.py | 2 +- octoprint_octolapse/settings.py | 2 +- octoprint_octolapse/settings_external.py | 2 +- octoprint_octolapse/settings_preprocessor.py | 2 +- octoprint_octolapse/snapshot.py | 2 +- octoprint_octolapse/stabilization_gcode.py | 2 +- .../stabilization_preprocessing.py | 2 +- octoprint_octolapse/static/css/octolapse.css | 2 +- .../static/js/octolapse.dialog.js | 2 +- .../octolapse.dialog.renderings.in_process.js | 2 +- .../octolapse.dialog.renderings.unfinished.js | 2 +- .../js/octolapse.dialog.timelapse_files.js | 2 +- .../static/js/octolapse.file_browser.js | 2 +- .../static/js/octolapse.help.js | 23 ++++++++++++++++++ .../static/js/octolapse.helpers.js | 2 +- octoprint_octolapse/static/js/octolapse.js | 2 +- .../static/js/octolapse.profiles.camera.js | 2 +- .../js/octolapse.profiles.camera.webcam.js | 2 +- ...se.profiles.camera.webcam.mjpg_streamer.js | 2 +- .../static/js/octolapse.profiles.js | 2 +- .../static/js/octolapse.profiles.library.js | 2 +- .../static/js/octolapse.profiles.logging.js | 2 +- .../static/js/octolapse.profiles.printer.js | 2 +- .../octolapse.profiles.printer.slicer.cura.js | 23 ++++++++++++++++++ ...octolapse.profiles.printer.slicer.other.js | 23 ++++++++++++++++++ ...pse.profiles.printer.slicer.simplify_3d.js | 23 ++++++++++++++++++ ...lapse.profiles.printer.slicer.slic3r_pe.js | 23 ++++++++++++++++++ .../static/js/octolapse.profiles.rendering.js | 2 +- .../js/octolapse.profiles.stabilization.js | 2 +- .../static/js/octolapse.profiles.trigger.js | 2 +- .../static/js/octolapse.settings.import.js | 2 +- .../static/js/octolapse.settings.js | 2 +- .../static/js/octolapse.settings.main.js | 2 +- .../static/js/octolapse.status.js | 2 +- .../js/octolapse.status.snapshotplan.js | 2 +- .../octolapse.status.snapshotplan_preview.js | 2 +- .../js/webcams/mjpg_streamer/raspi_cam_v2.js | 2 +- .../templates/octolapse_dialog.jinja2 | 2 +- ...tolapse_dialog_rendering_in_process.jinja2 | 2 +- ...tolapse_dialog_rendering_unfinished.jinja2 | 2 +- .../octolapse_dialog_timelapse_files.jinja2 | 2 +- .../templates/octolapse_file_browser.jinja2 | 2 +- .../templates/octolapse_helpers.jinja2 | 2 +- .../templates/octolapse_navbar.jinja2 | 2 +- .../octolapse_profile_dialog_add_edit.jinja2 | 2 +- .../templates/octolapse_profiles.jinja2 | 2 +- .../octolapse_profiles_camera.jinja2 | 2 +- .../octolapse_profiles_camera_webcam.jinja2 | 2 +- ...rofiles_camera_webcam_mjpg_streamer.jinja2 | 23 ++++++++++++++++++ .../octolapse_profiles_library.jinja2 | 23 ++++++++++++++++++ .../octolapse_profiles_logging.jinja2 | 2 +- .../octolapse_profiles_printer.jinja2 | 2 +- ...ctolapse_profiles_printer_automatic.jinja2 | 23 ++++++++++++++++++ ...e_profiles_printer_slicer_automatic.jinja2 | 23 ++++++++++++++++++ ...olapse_profiles_printer_slicer_cura.jinja2 | 23 ++++++++++++++++++ ...se_profiles_printer_slicer_cura_4.2.jinja2 | 23 ++++++++++++++++++ ...lapse_profiles_printer_slicer_other.jinja2 | 23 ++++++++++++++++++ ..._profiles_printer_slicer_simplify3d.jinja2 | 23 ++++++++++++++++++ ...e_profiles_printer_slicer_slic3r_pe.jinja2 | 23 ++++++++++++++++++ .../octolapse_profiles_rendering.jinja2 | 2 +- .../octolapse_profiles_stabilization.jinja2 | 2 +- .../octolapse_profiles_trigger.jinja2 | 2 +- .../templates/octolapse_settings.jinja2 | 8 ++++++- .../octolapse_settings_import.jinja2 | 23 ++++++++++++++++++ .../templates/octolapse_settings_main.jinja2 | 2 +- .../templates/octolapse_snapshot_plan.jinja2 | 2 +- .../octolapse_status_extruder.jinja2 | 2 +- .../octolapse_status_position.jinja2 | 2 +- .../octolapse_status_printer_state.jinja2 | 2 +- .../octolapse_status_snapshot_plan.jinja2 | 2 +- .../octolapse_status_triggers.jinja2 | 2 +- .../templates/octolapse_tab.jinja2 | 2 +- .../octolapse_tab_latest_snapshot.jinja2 | 2 +- .../octolapse_tab_settings_current.jinja2 | 2 +- ...olapse_tab_settings_current_cameras.jinja2 | 24 ++++++++++++++++++- ...pse_tab_snapshot_plan_preview_popup.jinja2 | 2 +- ...octolapse_tab_webcam_settings_popup.jinja2 | 2 +- octoprint_octolapse/test/__init__.py | 2 +- octoprint_octolapse/test/test_command.py | 2 +- octoprint_octolapse/test/test_extruder.py | 2 +- octoprint_octolapse/test/test_intersection.py | 2 +- .../test/test_migration_version.py | 21 ++++++++++++++++ .../test/test_octolapseplugin.py | 2 +- octoprint_octolapse/test/test_parsing.py | 2 +- octoprint_octolapse/test/test_position.py | 2 +- octoprint_octolapse/test/test_render.py | 2 +- octoprint_octolapse/test/test_settings.py | 2 +- .../test/test_settings_slicer.py | 2 +- .../test/test_snapshotGcode.py | 2 +- octoprint_octolapse/test/test_timelapse.py | 2 +- octoprint_octolapse/test/test_trigger.py | 2 +- .../test/test_trigger_gcode.py | 2 +- .../test/test_trigger_layer.py | 2 +- .../test/test_trigger_timer.py | 2 +- octoprint_octolapse/test/test_utility.py | 2 +- octoprint_octolapse/test/testing_utilities.py | 23 ++++++++++++++++++ octoprint_octolapse/timelapse.py | 2 +- octoprint_octolapse/trigger.py | 2 +- octoprint_octolapse/utility.py | 2 +- octoprint_octolapse_setuptools/__init__.py | 4 ++-- 111 files changed, 532 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index e7847306..ab95f474 100644 --- a/README.md +++ b/README.md @@ -263,4 +263,4 @@ View the [Octolapse license](https://github.com/FormerLurker/Octolapse/blob/mast
-_Copyright (C) 2020 Brad Hochgesang - FormerLurker@pm.me_ +_Copyright (C) 2023 Brad Hochgesang - FormerLurker@pm.me_ diff --git a/octoprint_octolapse/__init__.py b/octoprint_octolapse/__init__.py index a151d4a5..6cb288c6 100644 --- a/octoprint_octolapse/__init__.py +++ b/octoprint_octolapse/__init__.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/camera.py b/octoprint_octolapse/camera.py index 11463722..811cb195 100644 --- a/octoprint_octolapse/camera.py +++ b/octoprint_octolapse/camera.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/error_messages.py b/octoprint_octolapse/error_messages.py index c7e68acc..307942d4 100644 --- a/octoprint_octolapse/error_messages.py +++ b/octoprint_octolapse/error_messages.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/gcode_commands.py b/octoprint_octolapse/gcode_commands.py index 508a5896..a4d9519d 100644 --- a/octoprint_octolapse/gcode_commands.py +++ b/octoprint_octolapse/gcode_commands.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/gcode_processor.py b/octoprint_octolapse/gcode_processor.py index 372bef92..190ec75b 100644 --- a/octoprint_octolapse/gcode_processor.py +++ b/octoprint_octolapse/gcode_processor.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/log.py b/octoprint_octolapse/log.py index 898cd8c1..114aab72 100644 --- a/octoprint_octolapse/log.py +++ b/octoprint_octolapse/log.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/messenger_worker.py b/octoprint_octolapse/messenger_worker.py index 42860da6..276ae8e8 100644 --- a/octoprint_octolapse/messenger_worker.py +++ b/octoprint_octolapse/messenger_worker.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/migration.py b/octoprint_octolapse/migration.py index 292119a4..3765aca6 100644 --- a/octoprint_octolapse/migration.py +++ b/octoprint_octolapse/migration.py @@ -1,3 +1,24 @@ +################################################################################## +# Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. +# Copyright (C) 2023 Brad Hochgesang +################################################################################## +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see the following: +# https://github.com/FormerLurker/Octolapse/blob/master/LICENSE +# +# You can contact the author either through the git-hub repository, or at the +# following email address: FormerLurker@pm.me +################################################################################## from octoprint_octolapse_setuptools import NumberedVersion import json diff --git a/octoprint_octolapse/position.py b/octoprint_octolapse/position.py index 0821e59c..29067974 100644 --- a/octoprint_octolapse/position.py +++ b/octoprint_octolapse/position.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/render.py b/octoprint_octolapse/render.py index c0e6c524..8fef0f80 100644 --- a/octoprint_octolapse/render.py +++ b/octoprint_octolapse/render.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/script.py b/octoprint_octolapse/script.py index 7f3bca3c..715bdc9a 100644 --- a/octoprint_octolapse/script.py +++ b/octoprint_octolapse/script.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/settings.py b/octoprint_octolapse/settings.py index 1932ed62..615639dd 100644 --- a/octoprint_octolapse/settings.py +++ b/octoprint_octolapse/settings.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/settings_external.py b/octoprint_octolapse/settings_external.py index bca76f9b..e3663203 100644 --- a/octoprint_octolapse/settings_external.py +++ b/octoprint_octolapse/settings_external.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/settings_preprocessor.py b/octoprint_octolapse/settings_preprocessor.py index bd7ce0b5..5bb1b45d 100644 --- a/octoprint_octolapse/settings_preprocessor.py +++ b/octoprint_octolapse/settings_preprocessor.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/snapshot.py b/octoprint_octolapse/snapshot.py index b90ef9a4..274aec55 100644 --- a/octoprint_octolapse/snapshot.py +++ b/octoprint_octolapse/snapshot.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/stabilization_gcode.py b/octoprint_octolapse/stabilization_gcode.py index c9c13bcd..3e63a688 100644 --- a/octoprint_octolapse/stabilization_gcode.py +++ b/octoprint_octolapse/stabilization_gcode.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/stabilization_preprocessing.py b/octoprint_octolapse/stabilization_preprocessing.py index bf067628..f21234f7 100644 --- a/octoprint_octolapse/stabilization_preprocessing.py +++ b/octoprint_octolapse/stabilization_preprocessing.py @@ -1,7 +1,7 @@ # coding=utf-8 ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/css/octolapse.css b/octoprint_octolapse/static/css/octolapse.css index 46724e11..65ed941d 100644 --- a/octoprint_octolapse/static/css/octolapse.css +++ b/octoprint_octolapse/static/css/octolapse.css @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.dialog.js b/octoprint_octolapse/static/js/octolapse.dialog.js index 00326832..88703157 100644 --- a/octoprint_octolapse/static/js/octolapse.dialog.js +++ b/octoprint_octolapse/static/js/octolapse.dialog.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.dialog.renderings.in_process.js b/octoprint_octolapse/static/js/octolapse.dialog.renderings.in_process.js index 27ba90af..7fe2435c 100644 --- a/octoprint_octolapse/static/js/octolapse.dialog.renderings.in_process.js +++ b/octoprint_octolapse/static/js/octolapse.dialog.renderings.in_process.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.dialog.renderings.unfinished.js b/octoprint_octolapse/static/js/octolapse.dialog.renderings.unfinished.js index 8862cbfb..792f5267 100644 --- a/octoprint_octolapse/static/js/octolapse.dialog.renderings.unfinished.js +++ b/octoprint_octolapse/static/js/octolapse.dialog.renderings.unfinished.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.dialog.timelapse_files.js b/octoprint_octolapse/static/js/octolapse.dialog.timelapse_files.js index 4cf30072..3ca61321 100644 --- a/octoprint_octolapse/static/js/octolapse.dialog.timelapse_files.js +++ b/octoprint_octolapse/static/js/octolapse.dialog.timelapse_files.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.file_browser.js b/octoprint_octolapse/static/js/octolapse.file_browser.js index d6a6ffd1..d61320d1 100644 --- a/octoprint_octolapse/static/js/octolapse.file_browser.js +++ b/octoprint_octolapse/static/js/octolapse.file_browser.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.help.js b/octoprint_octolapse/static/js/octolapse.help.js index 9566381f..df831a3d 100644 --- a/octoprint_octolapse/static/js/octolapse.help.js +++ b/octoprint_octolapse/static/js/octolapse.help.js @@ -1,3 +1,26 @@ +/* +################################################################################## +# Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. +# Copyright (C) 2023 Brad Hochgesang +################################################################################## +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see the following: +# https://github.com/FormerLurker/Octolapse/blob/master/LICENSE +# +# You can contact the author either through the git-hub repository, or at the +# following email address: FormerLurker@pm.me +################################################################################## +*/ $(function () { OctolapseHelp = function () { var self = this; diff --git a/octoprint_octolapse/static/js/octolapse.helpers.js b/octoprint_octolapse/static/js/octolapse.helpers.js index 688c042a..0a13da23 100644 --- a/octoprint_octolapse/static/js/octolapse.helpers.js +++ b/octoprint_octolapse/static/js/octolapse.helpers.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.js b/octoprint_octolapse/static/js/octolapse.js index 78fd48a7..5f804680 100644 --- a/octoprint_octolapse/static/js/octolapse.js +++ b/octoprint_octolapse/static/js/octolapse.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.camera.js b/octoprint_octolapse/static/js/octolapse.profiles.camera.js index a7a3629c..68182176 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.camera.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.camera.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.js b/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.js index 6e674e59..ed4aeb66 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.mjpg_streamer.js b/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.mjpg_streamer.js index c6158ed4..72327943 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.mjpg_streamer.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.camera.webcam.mjpg_streamer.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.js b/octoprint_octolapse/static/js/octolapse.profiles.js index 9635a0a0..ab26076e 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.library.js b/octoprint_octolapse/static/js/octolapse.profiles.library.js index a850eadf..e854bcab 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.library.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.library.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.logging.js b/octoprint_octolapse/static/js/octolapse.profiles.logging.js index e96c7e1f..311ec693 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.logging.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.logging.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.printer.js b/octoprint_octolapse/static/js/octolapse.profiles.printer.js index 4fa8d800..95f6c049 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.printer.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.printer.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.cura.js b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.cura.js index c378209c..a7003a87 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.cura.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.cura.js @@ -1,3 +1,26 @@ +/* +################################################################################## +# Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. +# Copyright (C) 2023 Brad Hochgesang +################################################################################## +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see the following: +# https://github.com/FormerLurker/Octolapse/blob/master/LICENSE +# +# You can contact the author either through the git-hub repository, or at the +# following email address: FormerLurker@pm.me +################################################################################## +*/ Octolapse.CuraExtruderViewModel = function (values, extruder_index) { var self=this; self.index = extruder_index; diff --git a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.other.js b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.other.js index 72f258a9..7123e0d8 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.other.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.other.js @@ -1,3 +1,26 @@ +/* +################################################################################## +# Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. +# Copyright (C) 2023 Brad Hochgesang +################################################################################## +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see the following: +# https://github.com/FormerLurker/Octolapse/blob/master/LICENSE +# +# You can contact the author either through the git-hub repository, or at the +# following email address: FormerLurker@pm.me +################################################################################## +*/ Octolapse.OtherSlicerExtruderViewModel = function (values, extruder_index) { var self=this; self.index = extruder_index; diff --git a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.simplify_3d.js b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.simplify_3d.js index f5216214..637dbb55 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.simplify_3d.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.simplify_3d.js @@ -1,3 +1,26 @@ +/* +################################################################################## +# Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. +# Copyright (C) 2023 Brad Hochgesang +################################################################################## +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see the following: +# https://github.com/FormerLurker/Octolapse/blob/master/LICENSE +# +# You can contact the author either through the git-hub repository, or at the +# following email address: FormerLurker@pm.me +################################################################################## +*/ Octolapse.Simplify3dExtruderViewModel = function (values, extruder_index) { var self=this; self.index = extruder_index; diff --git a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.slic3r_pe.js b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.slic3r_pe.js index 6fbc3641..7177434c 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.slic3r_pe.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.printer.slicer.slic3r_pe.js @@ -1,3 +1,26 @@ +/* +################################################################################## +# Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. +# Copyright (C) 2023 Brad Hochgesang +################################################################################## +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see the following: +# https://github.com/FormerLurker/Octolapse/blob/master/LICENSE +# +# You can contact the author either through the git-hub repository, or at the +# following email address: FormerLurker@pm.me +################################################################################## +*/ Octolapse.Slic3rPeExtruderViewModel = function (values, extruder_index) { var self=this; self.index = extruder_index; diff --git a/octoprint_octolapse/static/js/octolapse.profiles.rendering.js b/octoprint_octolapse/static/js/octolapse.profiles.rendering.js index 9c31f96e..3706b73e 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.rendering.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.rendering.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.stabilization.js b/octoprint_octolapse/static/js/octolapse.profiles.stabilization.js index 335c1bdb..1feb4fbc 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.stabilization.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.stabilization.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.profiles.trigger.js b/octoprint_octolapse/static/js/octolapse.profiles.trigger.js index f258b917..dacba0ea 100644 --- a/octoprint_octolapse/static/js/octolapse.profiles.trigger.js +++ b/octoprint_octolapse/static/js/octolapse.profiles.trigger.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.settings.import.js b/octoprint_octolapse/static/js/octolapse.settings.import.js index ea444c3d..7c57d724 100644 --- a/octoprint_octolapse/static/js/octolapse.settings.import.js +++ b/octoprint_octolapse/static/js/octolapse.settings.import.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.settings.js b/octoprint_octolapse/static/js/octolapse.settings.js index 7d52200a..dfa72da7 100644 --- a/octoprint_octolapse/static/js/octolapse.settings.js +++ b/octoprint_octolapse/static/js/octolapse.settings.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.settings.main.js b/octoprint_octolapse/static/js/octolapse.settings.main.js index 5521991f..c71625a1 100644 --- a/octoprint_octolapse/static/js/octolapse.settings.main.js +++ b/octoprint_octolapse/static/js/octolapse.settings.main.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.status.js b/octoprint_octolapse/static/js/octolapse.status.js index 7ef8d477..9f81c9e3 100644 --- a/octoprint_octolapse/static/js/octolapse.status.js +++ b/octoprint_octolapse/static/js/octolapse.status.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.status.snapshotplan.js b/octoprint_octolapse/static/js/octolapse.status.snapshotplan.js index 19e769a7..830c5d1a 100644 --- a/octoprint_octolapse/static/js/octolapse.status.snapshotplan.js +++ b/octoprint_octolapse/static/js/octolapse.status.snapshotplan.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/octolapse.status.snapshotplan_preview.js b/octoprint_octolapse/static/js/octolapse.status.snapshotplan_preview.js index df7860ad..0b5439e9 100644 --- a/octoprint_octolapse/static/js/octolapse.status.snapshotplan_preview.js +++ b/octoprint_octolapse/static/js/octolapse.status.snapshotplan_preview.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/static/js/webcams/mjpg_streamer/raspi_cam_v2.js b/octoprint_octolapse/static/js/webcams/mjpg_streamer/raspi_cam_v2.js index c8cd266d..061d47d7 100644 --- a/octoprint_octolapse/static/js/webcams/mjpg_streamer/raspi_cam_v2.js +++ b/octoprint_octolapse/static/js/webcams/mjpg_streamer/raspi_cam_v2.js @@ -1,7 +1,7 @@ /* ################################################################################## # Octolapse - A plugin for OctoPrint used for making stabilized timelapse videos. -# Copyright (C) 2020 Brad Hochgesang +# Copyright (C) 2023 Brad Hochgesang ################################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published diff --git a/octoprint_octolapse/templates/octolapse_dialog.jinja2 b/octoprint_octolapse/templates/octolapse_dialog.jinja2 index 508bdd72..be4c4c75 100644 --- a/octoprint_octolapse/templates/octolapse_dialog.jinja2 +++ b/octoprint_octolapse/templates/octolapse_dialog.jinja2 @@ -1,7 +1,7 @@