|
@@ -1,8 +1,9 @@
|
|
|
# Copyright (c) 2015-2022 Ultimaker B.V.
|
|
|
# Cura is released under the terms of the LGPLv3 or higher.
|
|
|
import json
|
|
|
+import re
|
|
|
|
|
|
-from typing import Optional, cast, List, Dict
|
|
|
+from typing import Optional, cast, List, Dict, Pattern, Set
|
|
|
|
|
|
from UM.Mesh.MeshWriter import MeshWriter
|
|
|
from UM.Math.Vector import Vector
|
|
@@ -17,6 +18,7 @@ from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer
|
|
|
|
|
|
from cura.CuraApplication import CuraApplication
|
|
|
from cura.CuraPackageManager import CuraPackageManager
|
|
|
+from cura.Settings import CuraContainerStack
|
|
|
from cura.Utils.Threading import call_on_qt_thread
|
|
|
from cura.Snapshot import Snapshot
|
|
|
|
|
@@ -175,13 +177,15 @@ class ThreeMFWriter(MeshWriter):
|
|
|
archive.writestr(thumbnail_file, thumbnail_buffer.data())
|
|
|
|
|
|
# Add PNG to content types file
|
|
|
- thumbnail_type = ET.SubElement(content_types, "Default", Extension = "png", ContentType = "image/png")
|
|
|
+ thumbnail_type = ET.SubElement(content_types, "Default", Extension="png", ContentType="image/png")
|
|
|
# Add thumbnail relation to _rels/.rels file
|
|
|
- thumbnail_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/" + THUMBNAIL_PATH, Id = "rel1", Type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
|
|
|
+ thumbnail_relation_element = ET.SubElement(relations_element, "Relationship",
|
|
|
+ Target="/" + THUMBNAIL_PATH, Id="rel1",
|
|
|
+ Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
|
|
|
|
|
|
# Write material metadata
|
|
|
- material_metadata = self._getMaterialPackageMetadata()
|
|
|
- self._storeMetadataJson({"packages": material_metadata}, archive, PACKAGE_METADATA_PATH)
|
|
|
+ packages_metadata = self._getMaterialPackageMetadata() + self._getPluginPackageMetadata()
|
|
|
+ self._storeMetadataJson({"packages": packages_metadata}, archive, PACKAGE_METADATA_PATH)
|
|
|
|
|
|
savitar_scene = Savitar.Scene()
|
|
|
|
|
@@ -253,7 +257,64 @@ class ThreeMFWriter(MeshWriter):
|
|
|
metadata_file = zipfile.ZipInfo(path)
|
|
|
# We have to set the compress type of each file as well (it doesn't keep the type of the entire archive)
|
|
|
metadata_file.compress_type = zipfile.ZIP_DEFLATED
|
|
|
- archive.writestr(metadata_file, json.dumps(metadata, separators=(", ", ": "), indent=4, skipkeys=True, ensure_ascii=False))
|
|
|
+ archive.writestr(metadata_file,
|
|
|
+ json.dumps(metadata, separators=(", ", ": "), indent=4, skipkeys=True, ensure_ascii=False))
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def _getPluginPackageMetadata() -> List[Dict[str, str]]:
|
|
|
+ """Get metadata for all backend plugins that are used in the project.
|
|
|
+
|
|
|
+ :return: List of material metadata dictionaries.
|
|
|
+ """
|
|
|
+
|
|
|
+ backend_plugin_enum_value_regex = re.compile(
|
|
|
+ r"PLUGIN::(?P<plugin_id>\w+)@(?P<version>\d+.\d+.\d+)::(?P<value>\w+)")
|
|
|
+ # This regex parses enum values to find if they contain custom
|
|
|
+ # backend engine values. These custom enum values are in the format
|
|
|
+ # PLUGIN::<plugin_id>@<version>::<value>
|
|
|
+ # where
|
|
|
+ # - plugin_id is the id of the plugin
|
|
|
+ # - version is in the semver format
|
|
|
+ # - value is the value of the enum
|
|
|
+
|
|
|
+ plugin_ids = set()
|
|
|
+
|
|
|
+ def add_plugin_ids_in_stack(stack: CuraContainerStack):
|
|
|
+ for key in stack.getAllKeys():
|
|
|
+ value = str(stack.getProperty(key, "value"))
|
|
|
+ for plugin_id, _version, _value in backend_plugin_enum_value_regex.findall(value):
|
|
|
+ plugin_ids.add(plugin_id)
|
|
|
+
|
|
|
+ # go through all stacks and find all the plugin id contained in the project
|
|
|
+ global_stack = Application.getInstance().getMachineManager().activeMachine
|
|
|
+ add_plugin_ids_in_stack(global_stack)
|
|
|
+
|
|
|
+ for container in global_stack.getContainers():
|
|
|
+ add_plugin_ids_in_stack(container)
|
|
|
+
|
|
|
+ for extruder_stack in global_stack.extruderList:
|
|
|
+ add_plugin_ids_in_stack(extruder_stack)
|
|
|
+
|
|
|
+ for container in extruder_stack.getContainers():
|
|
|
+ add_plugin_ids_in_stack(container)
|
|
|
+
|
|
|
+ metadata = {}
|
|
|
+
|
|
|
+ package_manager = cast(CuraPackageManager, CuraApplication.getInstance().getPackageManager())
|
|
|
+ for plugin_id in plugin_ids:
|
|
|
+ package_data = package_manager.getInstalledPackageInfo(plugin_id)
|
|
|
+
|
|
|
+ metadata[plugin_id] = {
|
|
|
+ "id": plugin_id,
|
|
|
+ "display_name": package_data.get("display_name") if package_data.get("display_name") else "",
|
|
|
+ "package_version": package_data.get("package_version") if package_data.get("package_version") else "",
|
|
|
+ "sdk_version_semver": package_data.get("sdk_version_semver") if package_data.get(
|
|
|
+ "sdk_version_semver") else "",
|
|
|
+ "type": "backend_plugin",
|
|
|
+ }
|
|
|
+
|
|
|
+ # Storing in a dict and fetching values to avoid duplicates
|
|
|
+ return list(metadata.values())
|
|
|
|
|
|
@staticmethod
|
|
|
def _getMaterialPackageMetadata() -> List[Dict[str, str]]:
|
|
@@ -278,7 +339,8 @@ class ThreeMFWriter(MeshWriter):
|
|
|
# Don't export bundled materials
|
|
|
continue
|
|
|
|
|
|
- package_id = package_manager.getMaterialFilePackageId(extruder.material.getFileName(), extruder.material.getMetaDataEntry("GUID"))
|
|
|
+ package_id = package_manager.getMaterialFilePackageId(extruder.material.getFileName(),
|
|
|
+ extruder.material.getMetaDataEntry("GUID"))
|
|
|
package_data = package_manager.getInstalledPackageInfo(package_id)
|
|
|
|
|
|
# We failed to find the package for this material
|
|
@@ -286,10 +348,14 @@ class ThreeMFWriter(MeshWriter):
|
|
|
Logger.info(f"Could not find package for material in extruder {extruder.id}, skipping.")
|
|
|
continue
|
|
|
|
|
|
- material_metadata = {"id": package_id,
|
|
|
- "display_name": package_data.get("display_name") if package_data.get("display_name") else "",
|
|
|
- "package_version": package_data.get("package_version") if package_data.get("package_version") else "",
|
|
|
- "sdk_version_semver": package_data.get("sdk_version_semver") if package_data.get("sdk_version_semver") else ""}
|
|
|
+ material_metadata = {
|
|
|
+ "id": package_id,
|
|
|
+ "display_name": package_data.get("display_name") if package_data.get("display_name") else "",
|
|
|
+ "package_version": package_data.get("package_version") if package_data.get("package_version") else "",
|
|
|
+ "sdk_version_semver": package_data.get("sdk_version_semver") if package_data.get(
|
|
|
+ "sdk_version_semver") else "",
|
|
|
+ "type": "material",
|
|
|
+ }
|
|
|
|
|
|
metadata[package_id] = material_metadata
|
|
|
|