diff --git a/.gitignore b/.gitignore index ecfcd6f..2343769 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist *.egg* .DS_Store *.zip +/git-flow-plus.config diff --git a/octoprint_prusaslicerthumbnails/__init__.py b/octoprint_prusaslicerthumbnails/__init__.py index 33821c7..e09f14e 100644 --- a/octoprint_prusaslicerthumbnails/__init__.py +++ b/octoprint_prusaslicerthumbnails/__init__.py @@ -10,6 +10,8 @@ import datetime import io from PIL import Image +import re +import base64 class PrusaslicerthumbnailsPlugin(octoprint.plugin.SettingsPlugin, @@ -27,8 +29,9 @@ def __init__(self): self._folderRemovalLastAdded = {} self._waitForAnalysis = False self._analysis_active = False + self.regex_extension = re.compile("\.(?:gco(?:de)?|tft)$") - ##~~ SettingsPlugin mixin + # ~~ SettingsPlugin mixin def get_settings_defaults(self): return dict( @@ -46,7 +49,7 @@ def get_settings_defaults(self): scale_inline_thumbnail_position=False ) - ##~~ AssetPlugin mixin + # ~~ AssetPlugin mixin def get_assets(self): return dict( @@ -54,7 +57,7 @@ def get_assets(self): css=["css/prusaslicerthumbnails.css"] ) - ##~~ TemplatePlugin mixin + # ~~ TemplatePlugin mixin def get_template_configs(self): return [ @@ -62,8 +65,6 @@ def get_template_configs(self): ] def _extract_thumbnail(self, gcode_filename, thumbnail_filename): - import re - import base64 regex = r"(?:^; thumbnail begin \d+[x ]\d+ \d+)(?:\n|\r\n?)((?:.+(?:\n|\r\n?))+?)(?:^; thumbnail end)" regex_mks = re.compile('(?:;(?:simage|;gimage):).*?M10086 ;[\r\n]', re.DOTALL) lineNum = 0 @@ -133,6 +134,7 @@ def find_best_thumbnail(self, gcode_encoded_images): # Return size and trimmed string return (200, 200), image[9:] + # Check for simage for image in gcode_encoded_images: if image.startswith(';simage:'): @@ -142,7 +144,16 @@ def find_best_thumbnail(self, gcode_encoded_images): # Image not found return None - ##~~ EventHandlerPlugin mixin + # Check for simage + for image in gcode_encoded_images: + if image.startswith(';simage:'): + # Return size and trimmed string + return (100, 100), image[8:] + + # Image not found + return None + + # ~~ EventHandlerPlugin mixin def on_event(self, event, payload): if event not in ["FileAdded", "FileRemoved", "FolderRemoved", "FolderAdded"]: @@ -158,22 +169,20 @@ def on_event(self, event, payload): results = self._process_gcode(local_files[file_key], results) self._logger.debug("Scan results: {}".format(results)) if event in ["FileAdded", "FileRemoved"] and payload["storage"] == "local" and "gcode" in payload["type"]: - thumbnail_filename = self.get_plugin_data_folder() + "/" + payload["path"].replace(".gcode", ".png") + thumbnail_path = self.regex_extension.sub(".png", payload["path"]) + thumbnail_filename = "{}/{}".format(self.get_plugin_data_folder(), thumbnail_path) + if os.path.exists(thumbnail_filename): os.remove(thumbnail_filename) if event == "FileAdded": gcode_filename = self._file_manager.path_on_disk("local", payload["path"]) self._extract_thumbnail(gcode_filename, thumbnail_filename) if os.path.exists(thumbnail_filename): - thumbnail_url = "plugin/prusaslicerthumbnails/thumbnail/" + payload["path"].replace(".gcode", - ".png") + "?" + "{:%Y%m%d%H%M%S}".format( - datetime.datetime.now()) - self._file_manager.set_additional_metadata("local", payload["path"], "thumbnail", - thumbnail_url.replace("//", "/"), overwrite=True) - self._file_manager.set_additional_metadata("local", payload["path"], "thumbnail_src", - self._identifier, overwrite=True) + thumbnail_url = "plugin/prusaslicerthumbnails/thumbnail/{}?{:%Y%m%d%H%M%S}".format(thumbnail_path, datetime.datetime.now()) + self._file_manager.set_additional_metadata("local", payload["path"], "thumbnail", thumbnail_url.replace("//", "/"), overwrite=True) + self._file_manager.set_additional_metadata("local", payload["path"], "thumbnail_src", self._identifier, overwrite=True) - ##~~ SimpleApiPlugin mixin + # ~~ SimpleApiPlugin mixin def _process_gcode(self, gcode_file, results=[]): self._logger.debug(gcode_file["path"]) @@ -199,7 +208,6 @@ def get_api_commands(self): def on_api_command(self, command, data): import flask - import json from octoprint.server import user_permission if not user_permission.can(): return flask.make_response("Insufficient rights", 403) @@ -214,9 +222,17 @@ def on_api_command(self, command, data): results = self._process_gcode(LocalFiles[key], results) return flask.jsonify(results) - ##~~ Routes hook + # ~~ extension_tree hook + def get_extension_tree(self, *args, **kwargs): + return dict( + machinecode=dict( + gcode=["tft"] + ) + ) + + # ~~ Routes hook def route_hook(self, server_routes, *args, **kwargs): - from octoprint.server.util.tornado import LargeResponseHandler, UrlProxyHandler, path_validation_factory + from octoprint.server.util.tornado import LargeResponseHandler, path_validation_factory from octoprint.util import is_hidden_path return [ (r"thumbnail/(.*)", LargeResponseHandler, dict(path=self.get_plugin_data_folder(), @@ -225,7 +241,7 @@ def route_hook(self, server_routes, *args, **kwargs): lambda path: not is_hidden_path(path), status_code=404))) ] - ##~~ Softwareupdate hook + # ~~ Softwareupdate hook def get_update_information(self): return dict( @@ -266,5 +282,6 @@ def __plugin_load__(): global __plugin_hooks__ __plugin_hooks__ = { "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information, + "octoprint.filemanager.extension_tree": __plugin_implementation__.get_extension_tree, "octoprint.server.http.routes": __plugin_implementation__.route_hook } diff --git a/octoprint_prusaslicerthumbnails/static/js/prusaslicerthumbnails.js b/octoprint_prusaslicerthumbnails/static/js/prusaslicerthumbnails.js index 66152e1..d9846ab 100644 --- a/octoprint_prusaslicerthumbnails/static/js/prusaslicerthumbnails.js +++ b/octoprint_prusaslicerthumbnails/static/js/prusaslicerthumbnails.js @@ -20,14 +20,14 @@ $(function() { self.crawl_results = ko.observableArray([]); self.filesViewModel.prusaslicerthumbnails_open_thumbnail = function(data) { - if(data.name.indexOf('.gcode') > 0){ - var thumbnail_title = data.path.replace('.gcode',''); + if(data.thumbnail_src === "prusaslicerthumbnails"){ + var thumbnail_title = data.name.replace(/\.(?:gco(?:de)?|tft)$/,''); self.thumbnail_url(data.thumbnail); self.thumbnail_title(thumbnail_title); self.file_details(data); $('div#prusa_thumbnail_viewer').modal("show"); } - } + }; self.DEFAULT_THUMBNAIL_SCALE = "100%"; self.filesViewModel.thumbnailScaleValue = ko.observable(self.DEFAULT_THUMBNAIL_SCALE); @@ -55,19 +55,20 @@ $(function() { self.crawl_results.push({name: ko.observable(key), files: ko.observableArray(data[key])}); } } - if(self.crawl_results().length == 0){ + if(self.crawl_results().length === 0){ self.crawl_results.push({name: ko.observable('No convertible files found'), files: ko.observableArray([])}); } self.filesViewModel.requestData({force: true}); self.crawling_files(false); }).fail(function(data){ self.crawling_files(false); - }) - } + }); + }; self.onBeforeBinding = function() { - // inject filelist thumpnail into template - let regex = /
'; + $("#files_template_machinecode").text(function () { var return_value = inline_thumbnail_template + $(this).text(); return_value = return_value.replace(regex, ' '); - return return_value + return return_value; }); // assign initial scaling @@ -116,7 +118,8 @@ $(function() { self.filesViewModel.thumbnailScaleValue(newValue + "%"); }); self.settingsViewModel.settings.plugins.prusaslicerthumbnails.state_panel_thumbnail_scale_value.subscribe(function(newValue){ - $('#prusaslicer_state_thumbnail').attr({'width': self.settingsViewModel.settings.plugins.prusaslicerthumbnails.state_panel_thumbnail_scale_value() + '%'}) + $('#prusaslicer_state_thumbnail').attr({'width': self.settingsViewModel.settings.plugins.prusaslicerthumbnails.state_panel_thumbnail_scale_value() + '%'}); + }); // observe alignment changes @@ -168,7 +171,8 @@ $(function() { $('#prusaslicer_state_thumbnail').remove(); } }); - } + }; + } OCTOPRINT_VIEWMODELS.push({ diff --git a/setup.py b/setup.py index 45fed45..89c48fd 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ plugin_name = "Slicer Thumbnails" # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module -plugin_version = "1.0.0" +plugin_version = "1.0.1rc1" # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin # module