list timelapses on printer with thumbnail and download button
This commit is contained in:
		| @@ -1,12 +1,19 @@ | |||||||
| # coding=utf-8 | # coding=utf-8 | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import | ||||||
|  |  | ||||||
|  | import os | ||||||
| import threading | import threading | ||||||
| import time | import time | ||||||
|  | import flask | ||||||
|  | import datetime | ||||||
|  |  | ||||||
| import octoprint.plugin | import octoprint.plugin | ||||||
| from octoprint.events import Events | from octoprint.events import Events | ||||||
|  | from octoprint.util import get_formatted_size, get_formatted_datetime, is_hidden_path | ||||||
|  | from octoprint.server.util.flask import no_firstrun_access | ||||||
|  | from octoprint.server.util.tornado import LargeResponseHandler, UrlProxyHandler, path_validation_factory | ||||||
|  | from octoprint.access.permissions import Permissions | ||||||
|  | from urllib.parse import quote as urlquote | ||||||
| from .ftpsclient import IoTFTPSClient | from .ftpsclient import IoTFTPSClient | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -14,13 +21,15 @@ class BambuPrintPlugin(octoprint.plugin.SettingsPlugin, | |||||||
|                        octoprint.plugin.TemplatePlugin, |                        octoprint.plugin.TemplatePlugin, | ||||||
|                        octoprint.plugin.AssetPlugin, |                        octoprint.plugin.AssetPlugin, | ||||||
|                        octoprint.plugin.EventHandlerPlugin, |                        octoprint.plugin.EventHandlerPlugin, | ||||||
|                        octoprint.plugin.SimpleApiPlugin): |                        octoprint.plugin.SimpleApiPlugin, | ||||||
|  |                        octoprint.plugin.BlueprintPlugin): | ||||||
|  |  | ||||||
|  |  | ||||||
|     def get_assets(self): |     def get_assets(self): | ||||||
|         return {'js': ["js/bambu_printer.js"]} |         return {'js': ["js/bambu_printer.js"]} | ||||||
|     def get_template_configs(self): |     def get_template_configs(self): | ||||||
|         return [{"type": "settings", "custom_bindings": True}] #, {"type": "generic", "custom_bindings": True, "template": "bambu_printer.jinja2"}] |         return [{"type": "settings", "custom_bindings": True}, | ||||||
|  |                 {"type": "generic", "custom_bindings": True, "template": "bambu_timelapse.jinja2"}] #, {"type": "generic", "custom_bindings": True, "template": "bambu_printer.jinja2"}] | ||||||
|  |  | ||||||
|     def get_settings_defaults(self): |     def get_settings_defaults(self): | ||||||
|         return {"device_type": "X1C", |         return {"device_type": "X1C", | ||||||
| @@ -47,7 +56,6 @@ class BambuPrintPlugin(octoprint.plugin.SettingsPlugin, | |||||||
|     def get_api_commands(self): |     def get_api_commands(self): | ||||||
|         return {"register": ["email", "password", "region", "auth_token"]} |         return {"register": ["email", "password", "region", "auth_token"]} | ||||||
|     def on_api_command(self, command, data): |     def on_api_command(self, command, data): | ||||||
|         import flask |  | ||||||
|         if command == "register": |         if command == "register": | ||||||
|             if "email" in data and "password" in data and "region" in data and "auth_token" in data: |             if "email" in data and "password" in data and "region" in data and "auth_token" in data: | ||||||
|                 self._logger.info(f"Registering user {data['email']}") |                 self._logger.info(f"Registering user {data['email']}") | ||||||
| @@ -129,6 +137,100 @@ class BambuPrintPlugin(octoprint.plugin.SettingsPlugin, | |||||||
|         else: |         else: | ||||||
|             return [] |             return [] | ||||||
|  |  | ||||||
|  |     def get_timelapse_file_list(self): | ||||||
|  |         if flask.request.path.startswith('/api/timelapse'): | ||||||
|  |             def process(): | ||||||
|  |                 host = self._settings.get(["host"]) | ||||||
|  |                 access_code = self._settings.get(["access_code"]) | ||||||
|  |                 return_file_list = [] | ||||||
|  |  | ||||||
|  |                 try: | ||||||
|  |                     ftp = IoTFTPSClient(f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True) | ||||||
|  |                     timelapse_file_list = ftp.list_files("timelapse/", ".mp4") or [] | ||||||
|  |  | ||||||
|  |                     for entry in timelapse_file_list: | ||||||
|  |                         if entry.startswith("/"): | ||||||
|  |                             filename = entry[1:].replace("timelapse/", "") | ||||||
|  |                         else: | ||||||
|  |                             filename = entry.replace("timelapse/", "") | ||||||
|  |  | ||||||
|  |                         filesize = ftp.ftps_session.size(f"timelapse/{filename}") | ||||||
|  |                         date_str = ftp.ftps_session.sendcmd(f"MDTM timelapse/{filename}").replace("213 ", "") | ||||||
|  |                         filedate = datetime.datetime.strptime(date_str, "%Y%m%d%H%M%S").replace(tzinfo=datetime.timezone.utc).timestamp() | ||||||
|  |  | ||||||
|  |                         return_file_list.append( | ||||||
|  |                             { | ||||||
|  |                                 "bytes": filesize, | ||||||
|  |                                 "date": get_formatted_datetime(datetime.datetime.fromtimestamp(filedate)), | ||||||
|  |                                 "name": filename, | ||||||
|  |                                 "size": get_formatted_size(filesize), | ||||||
|  |                                 "thumbnail": "/plugin/bambu_printer/thumbnail/" + filename.replace(".mp4", ".jpg"), | ||||||
|  |                                 "timestamp": filedate, | ||||||
|  |                                 "url": f"/plugin/bambu_printer/timelapse/{filename}" | ||||||
|  |                             }) | ||||||
|  |  | ||||||
|  |                     self._plugin_manager.send_plugin_message(self._identifier, {'files': return_file_list}) | ||||||
|  |  | ||||||
|  |                 except Exception as e: | ||||||
|  |                     self._logger.debug(f"Error getting timelapse files: {e}") | ||||||
|  |  | ||||||
|  |             thread = threading.Thread(target=process) | ||||||
|  |             thread.daemon = True | ||||||
|  |             thread.start() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def _hook_octoprint_server_api_before_request(self, *args, **kwargs): | ||||||
|  |         return [self.get_timelapse_file_list] | ||||||
|  |  | ||||||
|  |     @octoprint.plugin.BlueprintPlugin.route("/timelapse/<filename>", methods=["GET"]) | ||||||
|  |     @octoprint.server.util.flask.restricted_access | ||||||
|  |     @no_firstrun_access | ||||||
|  |     @Permissions.TIMELAPSE_DOWNLOAD.require(403) | ||||||
|  |     def downloadTimelapse(self, filename): | ||||||
|  |         dest_filename = os.path.join(self.get_plugin_data_folder(), filename) | ||||||
|  |         host = self._settings.get(["host"]) | ||||||
|  |         access_code = self._settings.get(["access_code"]) | ||||||
|  |  | ||||||
|  |         if not os.path.exists(dest_filename): | ||||||
|  |             ftp = IoTFTPSClient(f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True) | ||||||
|  |             download_result = ftp.download_file( | ||||||
|  |                 source=f"timelapse/{filename}", | ||||||
|  |                 dest=dest_filename, | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         return flask.redirect("/plugin/bambu_printer/download/timelapse/" + urlquote(filename), code=302) | ||||||
|  |  | ||||||
|  |     @octoprint.plugin.BlueprintPlugin.route("/thumbnail/<filename>", methods=["GET"]) | ||||||
|  |     @octoprint.server.util.flask.restricted_access | ||||||
|  |     @no_firstrun_access | ||||||
|  |     @Permissions.TIMELAPSE_DOWNLOAD.require(403) | ||||||
|  |     def downloadTimelapse(self, filename): | ||||||
|  |         dest_filename = os.path.join(self.get_plugin_data_folder(), filename) | ||||||
|  |         host = self._settings.get(["host"]) | ||||||
|  |         access_code = self._settings.get(["access_code"]) | ||||||
|  |  | ||||||
|  |         if not os.path.exists(dest_filename): | ||||||
|  |             ftp = IoTFTPSClient(f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True) | ||||||
|  |             download_result = ftp.download_file( | ||||||
|  |                 source=f"timelapse/thumbnail/{filename}", | ||||||
|  |                 dest=dest_filename, | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         return flask.redirect("/plugin/bambu_printer/download/thumbnail/" + urlquote(filename), code=302) | ||||||
|  |  | ||||||
|  |     def is_blueprint_csrf_protected(self): | ||||||
|  |         return True | ||||||
|  |  | ||||||
|  |     def route_hook(self, server_routes, *args, **kwargs): | ||||||
|  |         return [ | ||||||
|  |             (r"/download/timelapse/(.*)", LargeResponseHandler, | ||||||
|  |              {'path': self.get_plugin_data_folder(), 'as_attachment': True, 'path_validation': path_validation_factory( | ||||||
|  |                  lambda path: not is_hidden_path(path), status_code=404)}), | ||||||
|  |             (r"/download/thumbnail/(.*)", LargeResponseHandler, | ||||||
|  |              {'path': self.get_plugin_data_folder(), 'as_attachment': True, 'path_validation': path_validation_factory( | ||||||
|  |                  lambda path: not is_hidden_path(path), status_code=404)}) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|     def get_update_information(self): |     def get_update_information(self): | ||||||
|         return {'bambu_printer': {'displayName': "Bambu Printer", |         return {'bambu_printer': {'displayName': "Bambu Printer", | ||||||
|                                   'displayVersion': self._plugin_version, |                                   'displayVersion': self._plugin_version, | ||||||
| @@ -164,4 +266,6 @@ def __plugin_load__(): | |||||||
|         "octoprint.filemanager.extension_tree": __plugin_implementation__.support_3mf_files, |         "octoprint.filemanager.extension_tree": __plugin_implementation__.support_3mf_files, | ||||||
|         "octoprint.printer.sdcardupload": __plugin_implementation__.upload_to_sd, |         "octoprint.printer.sdcardupload": __plugin_implementation__.upload_to_sd, | ||||||
|         "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information, |         "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information, | ||||||
|  |         "octoprint.server.api.before_request": __plugin_implementation__._hook_octoprint_server_api_before_request, | ||||||
|  |         "octoprint.server.http.routes": __plugin_implementation__.route_hook | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -11,6 +11,9 @@ $(function () { | |||||||
|  |  | ||||||
|         self.settingsViewModel = parameters[0]; |         self.settingsViewModel = parameters[0]; | ||||||
|         self.filesViewModel = parameters[1]; |         self.filesViewModel = parameters[1]; | ||||||
|  |         self.loginStateViewModel = parameters[2]; | ||||||
|  |         self.accessViewModel = parameters[3]; | ||||||
|  |         self.timelapseViewModel = parameters[4]; | ||||||
|  |  | ||||||
|         self.getAuthToken = function (data) { |         self.getAuthToken = function (data) { | ||||||
|             self.settingsViewModel.settings.plugins.bambu_printer.auth_token(""); |             self.settingsViewModel.settings.plugins.bambu_printer.auth_token(""); | ||||||
| @@ -27,6 +30,59 @@ $(function () { | |||||||
|                 }); |                 }); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |                 // initialize list helper | ||||||
|  |         self.listHelper = new ItemListHelper( | ||||||
|  |             "timelapseFiles", | ||||||
|  |             { | ||||||
|  |                 name: function (a, b) { | ||||||
|  |                     // sorts ascending | ||||||
|  |                     if (a["name"].toLocaleLowerCase() < b["name"].toLocaleLowerCase()) | ||||||
|  |                         return -1; | ||||||
|  |                     if (a["name"].toLocaleLowerCase() > b["name"].toLocaleLowerCase()) | ||||||
|  |                         return 1; | ||||||
|  |                     return 0; | ||||||
|  |                 }, | ||||||
|  |                 date: function (a, b) { | ||||||
|  |                     // sorts descending | ||||||
|  |                     if (a["date"] > b["date"]) return -1; | ||||||
|  |                     if (a["date"] < b["date"]) return 1; | ||||||
|  |                     return 0; | ||||||
|  |                 }, | ||||||
|  |                 size: function (a, b) { | ||||||
|  |                     // sorts descending | ||||||
|  |                     if (a["bytes"] > b["bytes"]) return -1; | ||||||
|  |                     if (a["bytes"] < b["bytes"]) return 1; | ||||||
|  |                     return 0; | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             {}, | ||||||
|  |             "name", | ||||||
|  |             [], | ||||||
|  |             [], | ||||||
|  |             CONFIG_TIMELAPSEFILESPERPAGE | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         self.onDataUpdaterPluginMessage = function(plugin, data) { | ||||||
|  |             if (plugin != "bambu_printer") { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (data.files !== undefined) { | ||||||
|  |                 console.log(data.files); | ||||||
|  |                 self.listHelper.updateItems(data.files); | ||||||
|  |                 self.listHelper.resetPage(); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         self.onBeforeBinding = function () { | ||||||
|  |             $('#bambu_timelapse').appendTo("#timelapse"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         self.showTimelapseThumbnail = function(data) { | ||||||
|  |             $("#bambu_printer_timelapse_thumbnail").attr("src", data.thumbnail); | ||||||
|  |             $("#bambu_printer_timelapse_preview").modal('show'); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         /*$('#files div.upload-buttons > span.fileinput-button:first, #files div.folder-button').remove(); |         /*$('#files div.upload-buttons > span.fileinput-button:first, #files div.folder-button').remove(); | ||||||
|         $('#files div.upload-buttons > span.fileinput-button:first').removeClass('span6').addClass('input-block-level'); |         $('#files div.upload-buttons > span.fileinput-button:first').removeClass('span6').addClass('input-block-level'); | ||||||
|  |  | ||||||
| @@ -85,8 +141,8 @@ $(function () { | |||||||
|     OCTOPRINT_VIEWMODELS.push({ |     OCTOPRINT_VIEWMODELS.push({ | ||||||
|         construct: Bambu_printerViewModel, |         construct: Bambu_printerViewModel, | ||||||
|         // ViewModels your plugin depends on, e.g. loginStateViewModel, settingsViewModel, ... |         // ViewModels your plugin depends on, e.g. loginStateViewModel, settingsViewModel, ... | ||||||
|         dependencies: ["settingsViewModel", "filesViewModel"], |         dependencies: ["settingsViewModel", "filesViewModel", "loginStateViewModel", "accessViewModel", "timelapseViewModel"], | ||||||
|         // Elements to bind to, e.g. #settings_plugin_bambu_printer, #tab_plugin_bambu_printer, ... |         // Elements to bind to, e.g. #settings_plugin_bambu_printer, #tab_plugin_bambu_printer, ... | ||||||
|         elements: ["#bambu_printer_print_options", "#settings_plugin_bambu_printer"] |         elements: ["#bambu_printer_print_options", "#settings_plugin_bambu_printer", "#bambu_timelapse"] | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user