From 8ae92351a7e3e1758cca4a62e4170749ed93a914 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 16 Sep 2022 21:27:00 +0800 Subject: [PATCH 01/83] Import Reference during Publish --- openpype/hosts/maya/plugins/create/create_look.py | 3 +++ openpype/hosts/maya/plugins/publish/extract_look.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/openpype/hosts/maya/plugins/create/create_look.py b/openpype/hosts/maya/plugins/create/create_look.py index 44e439fe1f..cecdf9f54d 100644 --- a/openpype/hosts/maya/plugins/create/create_look.py +++ b/openpype/hosts/maya/plugins/create/create_look.py @@ -21,6 +21,9 @@ class CreateLook(plugin.Creator): # Whether to automatically convert the textures to .tx upon publish. self.data["maketx"] = self.make_tx + # Enable users to import reference + self.data["importReference"] = False + # Enable users to force a copy. # - on Windows is "forceCopy" always changed to `True` because of # windows implementation of hardlinks diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 91b0da75c6..845af0d32d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -255,6 +255,14 @@ class ExtractLook(publish.Extractor): hashes = results["fileHashes"] remap = results["attrRemap"] + # Import Reference if the option is enabled + ref_import = instance.data.get("importReference", True) + if ref_import: + reference_node = cmds.ls(type="reference") + for r in reference_node: + rFile = cmds.referenceQuery(r, f=True) + cmds.file(rFile, importReference=True) + # Extract in correct render layer layer = instance.data.get("renderlayer", "defaultRenderLayer") with lib.renderlayer(layer): From 25279d276d8ef1035aea327c67b49a44ab29d647 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 17:59:39 +0800 Subject: [PATCH 02/83] Import Reference during Publish --- .../hosts/maya/plugins/create/create_look.py | 3 - .../publish/extract_import_reference.py | 122 ++++++++++++++++++ .../maya/plugins/publish/extract_look.py | 8 -- .../defaults/project_settings/maya.json | 3 + .../schemas/schema_maya_publish.json | 16 ++- 5 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 openpype/hosts/maya/plugins/publish/extract_import_reference.py diff --git a/openpype/hosts/maya/plugins/create/create_look.py b/openpype/hosts/maya/plugins/create/create_look.py index cecdf9f54d..44e439fe1f 100644 --- a/openpype/hosts/maya/plugins/create/create_look.py +++ b/openpype/hosts/maya/plugins/create/create_look.py @@ -21,9 +21,6 @@ class CreateLook(plugin.Creator): # Whether to automatically convert the textures to .tx upon publish. self.data["maketx"] = self.make_tx - # Enable users to import reference - self.data["importReference"] = False - # Enable users to force a copy. # - on Windows is "forceCopy" always changed to `True` because of # windows implementation of hardlinks diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py new file mode 100644 index 0000000000..3e44addf6c --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -0,0 +1,122 @@ +import os +import pyblish.api +from openpype.pipeline import publish, legacy_io +from openpype.settings import get_project_settings + + +def _get_project_setting(): + project_name = legacy_io.active_project() + project_setting = get_project_settings(project_name) + maya_enabled = ( + project_setting["maya"]["publish"]["ImportReference"]["enabled"] + ) + use_published = ( + project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] + ) + if maya_enabled != use_published: + return False + else: + return use_published + + +class ImportReference(publish.Extractor): + """ + + Extract the scene with imported reference. + The temp scene with imported reference is + published for rendering if this extractor is activated + + """ + + label = "Import Reference" + order = pyblish.api.ExtractorOrder - 0.48 + hosts = ["maya"] + families = ["renderlayer", "workfile"] + active = _get_project_setting() + optional = True + tmp_format = "_tmp" + + def process(self, instance): + from maya import cmds + + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) + if ext_mapping: + self.log.info("Looking in settings for scene type ...") + # use extension mapping for first family found + for family in self.families: + try: + self.scene_type = ext_mapping[family] + self.log.info( + "Using {} as scene type".format(self.scene_type)) + break + except KeyError: + # no preset found + pass + + _scene_type = ("mayaAscii" + if self.scene_type == "ma" + else "mayaBinary") + + dir_path = self.staging_dir(instance) + # named the file with imported reference + tmp_name = instance.name + self.tmp_format + m_ref_fname = "{0}.{1}".format(tmp_name, self.scene_type) + + m_ref_path = os.path.join(dir_path, m_ref_fname) + + self.log.info("Performing extraction..") + current = cmds.file(query=True, sceneName=True) + cmds.file(save=True, force=True + + self.log.info("Performing extraction..") + + # create temp scene with imported + # reference for rendering + reference_node = cmds.ls(type="reference") + for r in reference_node: + rFile = cmds.referenceQuery(r, f=True) + if r == "sharedReferenceNode": + cmds.file(rFile, removeReference = True, referenceNode=r) + cmds.file(rFile, importReference=True) + + if current.endswith(self.scene_type): + current_path = os.path.dirname(current) + tmp_path_name = os.path.join(current_path, tmp_name) + cmds.file(rename=tmp_path_name) + cmds.file(save=True, force=True) + + with lib.maintained_selection(): + cmds.select(all=True, noExpand=True) + cmds.file(m_ref_path, + force=True, + typ = _scene_type, + exportSelected=True, + channels=True, + constraints=True, + shader=True, + expressions=True, + constructionHistory=True + ) + + if "files" not in instance.data: + instance.data["files"] = [] + + instance.data["files"].append(m_ref_path) + + if instance.data.get("representations") is None: + instance.data["representations"] = [] + + ref_representation = { + "name": self.scene_type, + "ext": self.scene_type, + "files": os.path.basename(m_ref_fname), + "stagingDir": dir_path + } + instance.data["representations"].append(ref_representation) + + self.log.info("Extracted instance '%s' to : '%s'" % (tmp_name, + m_ref_path)) + + cmds.file(current, open=True) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 845af0d32d..91b0da75c6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -255,14 +255,6 @@ class ExtractLook(publish.Extractor): hashes = results["fileHashes"] remap = results["attrRemap"] - # Import Reference if the option is enabled - ref_import = instance.data.get("importReference", True) - if ref_import: - reference_node = cmds.ls(type="reference") - for r in reference_node: - rFile = cmds.referenceQuery(r, f=True) - cmds.file(rFile, importReference=True) - # Extract in correct render layer layer = instance.data.get("renderlayer", "defaultRenderLayer") with lib.renderlayer(layer): diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 8643297f02..76be3c393e 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -762,6 +762,9 @@ } } }, + "ImportReference": { + "enabled": false + }, "ExtractMayaSceneRaw": { "enabled": true, "add_for_families": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 53247f6bd4..4cf84795b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -62,7 +62,7 @@ } ] }, - { + { "type": "dict", "collapsible": true, "key": "ValidateFrameRange", @@ -807,6 +807,20 @@ "type": "schema", "name": "schema_maya_capture" }, + { + "type": "dict", + "collapsible": true, + "key": "ImportReference", + "label": "Extract Scenes with Imported Reference", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, { "type": "dict", "collapsible": true, From e5cf620575d535eb5415f8f0b22858c954fb5eb2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:06:50 +0800 Subject: [PATCH 03/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 3e44addf6c..ac425ee083 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,4 +1,5 @@ import os + import pyblish.api from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings @@ -11,8 +12,8 @@ def _get_project_setting(): project_setting["maya"]["publish"]["ImportReference"]["enabled"] ) use_published = ( - project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] - ) + project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] # noqa + ) if maya_enabled != use_published: return False else: From 06b982fd435c2549266cf400e47cbc3c3bb404f0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:09:21 +0800 Subject: [PATCH 04/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index ac425ee083..feacf072c3 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,5 +1,5 @@ -import os +import os import pyblish.api from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings @@ -10,7 +10,7 @@ def _get_project_setting(): project_setting = get_project_settings(project_name) maya_enabled = ( project_setting["maya"]["publish"]["ImportReference"]["enabled"] - ) + ) use_published = ( project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] # noqa ) From 89b7699c21f9c0b55e5a4cdc2755bc242b7e12e9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:10:48 +0800 Subject: [PATCH 05/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index feacf072c3..eb225127f0 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,6 +1,7 @@ - import os + import pyblish.api + from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings From 2dc186f1911c2b5eebfa98e513a89831a51389ec Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:16:05 +0800 Subject: [PATCH 06/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index eb225127f0..56b757f2b4 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,5 +1,3 @@ -import os - import pyblish.api from openpype.pipeline import publish, legacy_io @@ -39,6 +37,7 @@ class ImportReference(publish.Extractor): tmp_format = "_tmp" def process(self, instance): + import os from maya import cmds ext_mapping = ( From bf7cca8586f485fb4507eef0cd050f6cd4cf481e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:17:25 +0800 Subject: [PATCH 07/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 56b757f2b4..eb225127f0 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,3 +1,5 @@ +import os + import pyblish.api from openpype.pipeline import publish, legacy_io @@ -37,7 +39,6 @@ class ImportReference(publish.Extractor): tmp_format = "_tmp" def process(self, instance): - import os from maya import cmds ext_mapping = ( From 93c9ec8d16404bd17fd8de7c25c3bc5829402b20 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:22:52 +0800 Subject: [PATCH 08/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index eb225127f0..8be37c91e3 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,5 +1,7 @@ import os +from maya import cmds + import pyblish.api from openpype.pipeline import publish, legacy_io @@ -39,8 +41,6 @@ class ImportReference(publish.Extractor): tmp_format = "_tmp" def process(self, instance): - from maya import cmds - ext_mapping = ( instance.context.data["project_settings"]["maya"]["ext_mapping"] ) From 3ce41cbae8c7e0bb5b00951f8e7a9927df472da3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:33:57 +0800 Subject: [PATCH 09/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 8be37c91e3..2f9e8516d7 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -70,7 +70,7 @@ class ImportReference(publish.Extractor): self.log.info("Performing extraction..") current = cmds.file(query=True, sceneName=True) - cmds.file(save=True, force=True + cmds.file(save=True, force=True) self.log.info("Performing extraction..") From 79e78beb61ef1c5f5bae9042a6b07db419fb79f8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:36:24 +0800 Subject: [PATCH 10/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 2f9e8516d7..1ed109d720 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -6,6 +6,7 @@ import pyblish.api from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings +from openpype.hosts.maya.api import lib def _get_project_setting(): @@ -80,7 +81,7 @@ class ImportReference(publish.Extractor): for r in reference_node: rFile = cmds.referenceQuery(r, f=True) if r == "sharedReferenceNode": - cmds.file(rFile, removeReference = True, referenceNode=r) + cmds.file(rFile, removeReference=True, referenceNode=r) cmds.file(rFile, importReference=True) if current.endswith(self.scene_type): @@ -93,14 +94,14 @@ class ImportReference(publish.Extractor): cmds.select(all=True, noExpand=True) cmds.file(m_ref_path, force=True, - typ = _scene_type, + typ=_scene_type, exportSelected=True, channels=True, constraints=True, shader=True, expressions=True, constructionHistory=True - ) + ) if "files" not in instance.data: instance.data["files"] = [] From 92a80eb24a4e9a849e58ef5ca57d885f9efa396a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:38:08 +0800 Subject: [PATCH 11/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 1ed109d720..4035e8a9e6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -101,7 +101,7 @@ class ImportReference(publish.Extractor): shader=True, expressions=True, constructionHistory=True - ) + ) if "files" not in instance.data: instance.data["files"] = [] From ab7ed6becbd100c6e9f8960065bdeffc8f609b5b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:39:22 +0800 Subject: [PATCH 12/83] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 4035e8a9e6..cde8f67789 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -100,8 +100,7 @@ class ImportReference(publish.Extractor): constraints=True, shader=True, expressions=True, - constructionHistory=True - ) + constructionHistory=True) if "files" not in instance.data: instance.data["files"] = [] From ee78e9e67036308e651a9a0005b72a4d16ecce17 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 15:45:17 +0800 Subject: [PATCH 13/83] Import Reference during Publish --- .../publish/extract_import_reference.py | 58 +++++++++---------- .../deadline/abstract_submit_deadline.py | 7 ++- .../defaults/project_settings/deadline.json | 1 + .../defaults/project_settings/maya.json | 3 - .../schema_project_deadline.json | 5 ++ .../schemas/schema_maya_publish.json | 14 ----- 6 files changed, 37 insertions(+), 51 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index cde8f67789..4cbc963cf3 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -9,22 +9,16 @@ from openpype.settings import get_project_settings from openpype.hosts.maya.api import lib -def _get_project_setting(): +def _import_reference(): project_name = legacy_io.active_project() project_setting = get_project_settings(project_name) - maya_enabled = ( - project_setting["maya"]["publish"]["ImportReference"]["enabled"] + import_reference = ( + project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa ) - use_published = ( - project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] # noqa - ) - if maya_enabled != use_published: - return False - else: - return use_published + return import_reference -class ImportReference(publish.Extractor): +class ExtractImportReference(publish.Extractor): """ Extract the scene with imported reference. @@ -33,12 +27,11 @@ class ImportReference(publish.Extractor): """ - label = "Import Reference" + label = "Extract Import Reference" order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = _get_project_setting() - optional = True + active = _import_reference() tmp_format = "_tmp" def process(self, instance): @@ -54,9 +47,10 @@ class ImportReference(publish.Extractor): self.log.info( "Using {} as scene type".format(self.scene_type)) break + except KeyError: - # no preset found - pass + # set scene type to ma + self.scene_type = "ma" _scene_type = ("mayaAscii" if self.scene_type == "ma" @@ -64,6 +58,8 @@ class ImportReference(publish.Extractor): dir_path = self.staging_dir(instance) # named the file with imported reference + if instance.name == "Main": + return tmp_name = instance.name + self.tmp_format m_ref_fname = "{0}.{1}".format(tmp_name, self.scene_type) @@ -72,23 +68,20 @@ class ImportReference(publish.Extractor): self.log.info("Performing extraction..") current = cmds.file(query=True, sceneName=True) cmds.file(save=True, force=True) - - self.log.info("Performing extraction..") - # create temp scene with imported # reference for rendering reference_node = cmds.ls(type="reference") for r in reference_node: - rFile = cmds.referenceQuery(r, f=True) + ref_file = cmds.referenceQuery(r, f=True) if r == "sharedReferenceNode": - cmds.file(rFile, removeReference=True, referenceNode=r) - cmds.file(rFile, importReference=True) + cmds.file(ref_file, removeReference=True, referenceNode=r) + return + cmds.file(ref_file, importReference=True) - if current.endswith(self.scene_type): - current_path = os.path.dirname(current) - tmp_path_name = os.path.join(current_path, tmp_name) - cmds.file(rename=tmp_path_name) - cmds.file(save=True, force=True) + cmds.file(rename=m_ref_fname) + cmds.file(save=True, force=True) + tmp_filepath = cmds.file(query=True, sceneName=True) + instance.context.data["currentFile"] = tmp_filepath with lib.maintained_selection(): cmds.select(all=True, noExpand=True) @@ -104,8 +97,7 @@ class ImportReference(publish.Extractor): if "files" not in instance.data: instance.data["files"] = [] - - instance.data["files"].append(m_ref_path) + instance.data["files"].append(m_ref_fname) if instance.data.get("representations") is None: instance.data["representations"] = [] @@ -113,12 +105,14 @@ class ImportReference(publish.Extractor): ref_representation = { "name": self.scene_type, "ext": self.scene_type, - "files": os.path.basename(m_ref_fname), - "stagingDir": dir_path + "files": m_ref_fname, + "stagingDir": os.path.dirname(tmp_filepath) } + instance.data["representations"].append(ref_representation) - self.log.info("Extracted instance '%s' to : '%s'" % (tmp_name, + self.log.info("Extracted instance '%s' to : '%s'" % (m_ref_fname, m_ref_path)) + #re-open the previous scene cmds.file(current, open=True) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 512ff800ee..909a5871e3 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -400,6 +400,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): label = "Submit to Deadline" order = pyblish.api.IntegratorOrder + 0.1 + import_reference = False use_published = True asset_dependencies = False @@ -516,7 +517,6 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): published. """ - instance = self._instance workfile_instance = self._get_workfile_instance(instance.context) if workfile_instance is None: @@ -524,7 +524,10 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): # determine published path from Anatomy. template_data = workfile_instance.data.get("anatomyData") - rep = workfile_instance.data.get("representations")[0] + if self.import_reference: + rep = workfile_instance.data.get("representations")[1] + else: + rep = workfile_instance.data.get("representations")[0] template_data["representation"] = rep.get("name") template_data["ext"] = rep.get("ext") template_data["comment"] = None diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index a6e7b4a94a..5f0731fb0c 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -25,6 +25,7 @@ "active": true, "tile_assembler_plugin": "OpenPypeTileAssembler", "use_published": true, + "import_reference": false, "asset_dependencies": true, "priority": 50, "tile_priority": 50, diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 76be3c393e..8643297f02 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -762,9 +762,6 @@ } } }, - "ImportReference": { - "enabled": false - }, "ExtractMayaSceneRaw": { "enabled": true, "add_for_families": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index cd1741ba8b..be95c682c4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -130,6 +130,11 @@ "key": "use_published", "label": "Use Published scene" }, + { + "type": "boolean", + "key": "import_reference", + "label": "Use Scene with Imported Reference" + }, { "type": "boolean", "key": "asset_dependencies", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 4cf84795b5..07d8f6aea0 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -807,20 +807,6 @@ "type": "schema", "name": "schema_maya_capture" }, - { - "type": "dict", - "collapsible": true, - "key": "ImportReference", - "label": "Extract Scenes with Imported Reference", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, { "type": "dict", "collapsible": true, From 6f914a001de08931f84bf1753f0d3ad3bf903593 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 15:46:12 +0800 Subject: [PATCH 14/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 4cbc963cf3..fd2687995b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -114,5 +114,5 @@ class ExtractImportReference(publish.Extractor): self.log.info("Extracted instance '%s' to : '%s'" % (m_ref_fname, m_ref_path)) - #re-open the previous scene + # re-open the previous scene cmds.file(current, open=True) From df937a8e9ee028142ce4f56ea17c58e166b800ed Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 16:11:26 +0800 Subject: [PATCH 15/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index fd2687995b..92426e97dd 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -32,6 +32,7 @@ class ExtractImportReference(publish.Extractor): hosts = ["maya"] families = ["renderlayer", "workfile"] active = _import_reference() + optional= True tmp_format = "_tmp" def process(self, instance): From dfd30d54601939ac31f72fa417a8bb00f67c5881 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 16:13:21 +0800 Subject: [PATCH 16/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 92426e97dd..193048e9dc 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -32,7 +32,7 @@ class ExtractImportReference(publish.Extractor): hosts = ["maya"] families = ["renderlayer", "workfile"] active = _import_reference() - optional= True + optional = True tmp_format = "_tmp" def process(self, instance): From 590496814df787ba33a7bb366654fe14d8da76e3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 7 Oct 2022 22:07:15 +0800 Subject: [PATCH 17/83] Import Reference during Publish --- .../publish/extract_import_reference.py | 70 ++++++++++++------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 193048e9dc..7448cf9966 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,9 +1,11 @@ import os +import sys from maya import cmds import pyblish.api +from openpype.lib import run_subprocess from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings from openpype.hosts.maya.api import lib @@ -31,7 +33,7 @@ class ExtractImportReference(publish.Extractor): order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = _import_reference() + active = True optional = True tmp_format = "_tmp" @@ -62,31 +64,48 @@ class ExtractImportReference(publish.Extractor): if instance.name == "Main": return tmp_name = instance.name + self.tmp_format - m_ref_fname = "{0}.{1}".format(tmp_name, self.scene_type) + current_name = cmds.file(query=True, sceneName=True) + ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) - m_ref_path = os.path.join(dir_path, m_ref_fname) + reference_path = os.path.join(dir_path, ref_scene_name) self.log.info("Performing extraction..") - current = cmds.file(query=True, sceneName=True) - cmds.file(save=True, force=True) - # create temp scene with imported - # reference for rendering - reference_node = cmds.ls(type="reference") - for r in reference_node: - ref_file = cmds.referenceQuery(r, f=True) - if r == "sharedReferenceNode": - cmds.file(ref_file, removeReference=True, referenceNode=r) - return - cmds.file(ref_file, importReference=True) + script = ("import maya.standalone\nmaya.standalone.initialize()\n" + "cmds.file('{current_name}', open=True, force=True)\n" + "reference_node = cmds.ls(type='reference')\n" + "for ref in reference_node:\n" + "\tref_file = cmds.referenceQuery(ref, f=True)\n" + "\tif ref == 'sharedReferenceNode':\n" + "\t\tcmds.file(ref_file, removeReference=True, referenceNode=ref)\n" + "\telse:\n" + "\t\tcmds.file(ref_file, importReference=True)\n" + "try:\n" + "\tcmds.file(rename='{ref_scene_name}')\n" + "except SyntaxError:\n" + "\tcmds.file(rename='{ref_scene_name}')\n" + "cmds.file(save=True, force=True)\n") - cmds.file(rename=m_ref_fname) - cmds.file(save=True, force=True) - tmp_filepath = cmds.file(query=True, sceneName=True) + mayapy_exe = os.path.join(os.getenv("MAYA_LOCATION"), "bin", "mayapy") + if sys.platform == "windows": + mayapy_exe = mayapy_exe + ".exe" + + subprocess_args = [ + mayapy_exe, + "-c", + script.replace("\n", ";") + ] + try: + out = run_subprocess(subprocess_args) + except Exception: + self.log.error("Import reference failed", exc_info=True) + raise + + proj_file_dir = os.path.dirname(current_name) + tmp_filepath = os.path.join(proj_file_dir, ref_scene_name) instance.context.data["currentFile"] = tmp_filepath - with lib.maintained_selection(): cmds.select(all=True, noExpand=True) - cmds.file(m_ref_path, + cmds.file(reference_path, force=True, typ=_scene_type, exportSelected=True, @@ -98,7 +117,7 @@ class ExtractImportReference(publish.Extractor): if "files" not in instance.data: instance.data["files"] = [] - instance.data["files"].append(m_ref_fname) + instance.data["files"].append(ref_scene_name) if instance.data.get("representations") is None: instance.data["representations"] = [] @@ -106,14 +125,11 @@ class ExtractImportReference(publish.Extractor): ref_representation = { "name": self.scene_type, "ext": self.scene_type, - "files": m_ref_fname, - "stagingDir": os.path.dirname(tmp_filepath) + "files": ref_scene_name, + "stagingDir": proj_file_dir } instance.data["representations"].append(ref_representation) - self.log.info("Extracted instance '%s' to : '%s'" % (m_ref_fname, - m_ref_path)) - - # re-open the previous scene - cmds.file(current, open=True) + self.log.info("Extracted instance '%s' to : '%s'" % (ref_scene_name, + reference_path)) From 34b7ba9d3abead3437f7c53ea5d4d3abe3cd5720 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 12 Oct 2022 15:14:14 +0800 Subject: [PATCH 18/83] Import Reference during Publish --- .../publish/extract_import_reference.py | 38 ++++++++++--------- .../deadline/abstract_submit_deadline.py | 2 + .../defaults/project_anatomy/templates.json | 2 +- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 7448cf9966..4ad3c7756d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -70,20 +70,24 @@ class ExtractImportReference(publish.Extractor): reference_path = os.path.join(dir_path, ref_scene_name) self.log.info("Performing extraction..") - script = ("import maya.standalone\nmaya.standalone.initialize()\n" - "cmds.file('{current_name}', open=True, force=True)\n" - "reference_node = cmds.ls(type='reference')\n" - "for ref in reference_node:\n" - "\tref_file = cmds.referenceQuery(ref, f=True)\n" - "\tif ref == 'sharedReferenceNode':\n" - "\t\tcmds.file(ref_file, removeReference=True, referenceNode=ref)\n" - "\telse:\n" - "\t\tcmds.file(ref_file, importReference=True)\n" - "try:\n" - "\tcmds.file(rename='{ref_scene_name}')\n" - "except SyntaxError:\n" - "\tcmds.file(rename='{ref_scene_name}')\n" - "cmds.file(save=True, force=True)\n") + script = f""" + import maya.standalone + maya.standalone.initialize() + cmds.file('{current_name}', open=True, force=True) + reference_node = cmds.ls(type='reference') + for ref in reference_node: + ref_file = cmds.referenceQuery(ref, f=True) + if ref == 'sharedReferenceNode': + cmds.file(ref_file, removeReference=True, referenceNode=ref) + else: + cmds.file(ref_file, importReference=True) + try: + cmds.file(rename='{ref_scene_name}') + except SyntaxError: + cmds.file(rename='{ref_scene_name}') + + cmds.file(save=True, force=True) + """ mayapy_exe = os.path.join(os.getenv("MAYA_LOCATION"), "bin", "mayapy") if sys.platform == "windows": @@ -100,9 +104,7 @@ class ExtractImportReference(publish.Extractor): self.log.error("Import reference failed", exc_info=True) raise - proj_file_dir = os.path.dirname(current_name) - tmp_filepath = os.path.join(proj_file_dir, ref_scene_name) - instance.context.data["currentFile"] = tmp_filepath + instance.context.data["currentFile"] = ref_scene_name with lib.maintained_selection(): cmds.select(all=True, noExpand=True) cmds.file(reference_path, @@ -126,7 +128,7 @@ class ExtractImportReference(publish.Extractor): "name": self.scene_type, "ext": self.scene_type, "files": ref_scene_name, - "stagingDir": proj_file_dir + "stagingDir": os.path.dirname(current_name) } instance.data["representations"].append(ref_representation) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 909a5871e3..1f74b9b19b 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -526,6 +526,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): template_data = workfile_instance.data.get("anatomyData") if self.import_reference: rep = workfile_instance.data.get("representations")[1] + # template_data["workfiletype"] = rep.get("workfiletype") else: rep = workfile_instance.data.get("representations")[0] template_data["representation"] = rep.get("name") @@ -535,6 +536,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): anatomy = instance.context.data['anatomy'] anatomy_filled = anatomy.format(template_data) template_filled = anatomy_filled["publish"]["path"] + # template_filled = anatomy_filled["others"]["mayaWorkfile"]["path"] file_path = os.path.normpath(template_filled) self.log.info("Using published scene for render {}".format(file_path)) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index caf399a903..72d387335d 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -17,7 +17,7 @@ }, "publish": { "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}", + "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}><_{workfiletype}>.{ext}", "path": "{@folder}/{@file}", "thumbnail": "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}" }, From de73dd7de6368dbc65e825d325d2058af14633d0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 14 Oct 2022 21:27:10 +0800 Subject: [PATCH 19/83] Import Reference during Publish --- .../publish/extract_import_reference.py | 86 ++++++++++++------- .../deadline/abstract_submit_deadline.py | 2 - .../defaults/project_anatomy/templates.json | 2 +- 3 files changed, 56 insertions(+), 34 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 4ad3c7756d..6b935ffb73 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -4,6 +4,7 @@ import sys from maya import cmds import pyblish.api +import tempfile from openpype.lib import run_subprocess from openpype.pipeline import publish, legacy_io @@ -33,7 +34,7 @@ class ExtractImportReference(publish.Extractor): order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = True + active = _import_reference() optional = True tmp_format = "_tmp" @@ -68,43 +69,62 @@ class ExtractImportReference(publish.Extractor): ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) reference_path = os.path.join(dir_path, ref_scene_name) + tmp_path = os.path.dirname(current_name) + "/" + ref_scene_name self.log.info("Performing extraction..") - script = f""" - import maya.standalone - maya.standalone.initialize() - cmds.file('{current_name}', open=True, force=True) - reference_node = cmds.ls(type='reference') - for ref in reference_node: - ref_file = cmds.referenceQuery(ref, f=True) - if ref == 'sharedReferenceNode': - cmds.file(ref_file, removeReference=True, referenceNode=ref) - else: - cmds.file(ref_file, importReference=True) - try: - cmds.file(rename='{ref_scene_name}') - except SyntaxError: - cmds.file(rename='{ref_scene_name}') - cmds.file(save=True, force=True) - """ + # This generates script for mayapy to take care of reference + # importing outside current session. It is passing current scene + # name and destination scene name. + script = (""" +# -*- coding: utf-8 -*- +'''Script to import references to given scene.''' +import maya.standalone +maya.standalone.initialize() +# scene names filled by caller +current_name = "{current_name}" +ref_scene_name = "{ref_scene_name}" +print(">>> Opening {{}} ...".format(current_name)) +cmds.file(current_name, open=True, force=True) +reference_node = cmds.ls(type='reference') +print(">>> Processing references") +for ref in reference_node: + ref_file = cmds.referenceQuery(ref, f=True) + print("--- {{}}".format(ref)) + print("--> {{}}".format(ref_file)) + if ref == 'sharedReferenceNode': + cmds.file(ref_file, removeReference=True, referenceNode=ref) + else: + cmds.file(ref_file, importReference=True) +print(">>> Saving scene as {{}}".format(ref_scene_name)) +cmds.file(rename=ref_scene_name) +cmds.file(save=True, force=True) +print("*** Done") + """).format(current_name=current_name, ref_scene_name=tmp_path) mayapy_exe = os.path.join(os.getenv("MAYA_LOCATION"), "bin", "mayapy") if sys.platform == "windows": - mayapy_exe = mayapy_exe + ".exe" + mayapy_exe += ".exe" + mayapy_exe = os.path.normpath(mayapy_exe) + # can't use TemporaryNamedFile as that can't be opened in another + # process until handles are closed by context manager. + with tempfile.TemporaryDirectory() as tmp_dir_name: + tmp_file_name = os.path.join(tmp_dir_name, "import_ref.py") + tmp = open(tmp_file_name, "w+t") + subprocess_args = [ + mayapy_exe, + tmp_file_name + ] + self.log.info("Using temp file: {}".format(tmp.name)) + try: + tmp.write(script) + tmp.close() + run_subprocess(subprocess_args) + except Exception: + self.log.error("Import reference failed", exc_info=True) + raise - subprocess_args = [ - mayapy_exe, - "-c", - script.replace("\n", ";") - ] - try: - out = run_subprocess(subprocess_args) - except Exception: - self.log.error("Import reference failed", exc_info=True) - raise - instance.context.data["currentFile"] = ref_scene_name with lib.maintained_selection(): cmds.select(all=True, noExpand=True) cmds.file(reference_path, @@ -117,6 +137,8 @@ class ExtractImportReference(publish.Extractor): expressions=True, constructionHistory=True) + instance.context.data["currentFile"] = tmp_path + if "files" not in instance.data: instance.data["files"] = [] instance.data["files"].append(ref_scene_name) @@ -128,8 +150,10 @@ class ExtractImportReference(publish.Extractor): "name": self.scene_type, "ext": self.scene_type, "files": ref_scene_name, - "stagingDir": os.path.dirname(current_name) + "stagingDir": os.path.dirname(current_name), + "outputName": "imported" } + self.log.info("%s" % ref_representation) instance.data["representations"].append(ref_representation) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 1f74b9b19b..909a5871e3 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -526,7 +526,6 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): template_data = workfile_instance.data.get("anatomyData") if self.import_reference: rep = workfile_instance.data.get("representations")[1] - # template_data["workfiletype"] = rep.get("workfiletype") else: rep = workfile_instance.data.get("representations")[0] template_data["representation"] = rep.get("name") @@ -536,7 +535,6 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): anatomy = instance.context.data['anatomy'] anatomy_filled = anatomy.format(template_data) template_filled = anatomy_filled["publish"]["path"] - # template_filled = anatomy_filled["others"]["mayaWorkfile"]["path"] file_path = os.path.normpath(template_filled) self.log.info("Using published scene for render {}".format(file_path)) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 72d387335d..caf399a903 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -17,7 +17,7 @@ }, "publish": { "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}><_{workfiletype}>.{ext}", + "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}", "path": "{@folder}/{@file}", "thumbnail": "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}" }, From 76a70b70cf75c83c2be3beae8897c17c9ad80cb0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 14 Oct 2022 21:28:30 +0800 Subject: [PATCH 20/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 6b935ffb73..e70a27a6f6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -124,7 +124,6 @@ print("*** Done") self.log.error("Import reference failed", exc_info=True) raise - with lib.maintained_selection(): cmds.select(all=True, noExpand=True) cmds.file(reference_path, From b82c3ac7f97d74272c4d991e7ec9a230cda6a5b4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 14 Oct 2022 21:29:51 +0800 Subject: [PATCH 21/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index e70a27a6f6..3ec2f3bba4 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -66,7 +66,7 @@ class ExtractImportReference(publish.Extractor): return tmp_name = instance.name + self.tmp_format current_name = cmds.file(query=True, sceneName=True) - ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) + ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) reference_path = os.path.join(dir_path, ref_scene_name) tmp_path = os.path.dirname(current_name) + "/" + ref_scene_name From ab93c766ea425e6ef7b017465c6905cca014f712 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 25 Oct 2022 20:09:04 +0800 Subject: [PATCH 22/83] Import Reference during Publish --- .../plugins/publish/extract_import_reference.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 3ec2f3bba4..fa9e612dad 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -7,20 +7,10 @@ import pyblish.api import tempfile from openpype.lib import run_subprocess -from openpype.pipeline import publish, legacy_io -from openpype.settings import get_project_settings +from openpype.pipeline import publish from openpype.hosts.maya.api import lib -def _import_reference(): - project_name = legacy_io.active_project() - project_setting = get_project_settings(project_name) - import_reference = ( - project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa - ) - return import_reference - - class ExtractImportReference(publish.Extractor): """ @@ -34,10 +24,13 @@ class ExtractImportReference(publish.Extractor): order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = _import_reference() optional = True tmp_format = "_tmp" + @classmethod + def apply_settings(cls, project_setting, system_settings): #noqa + cls.active = project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa + def process(self, instance): ext_mapping = ( instance.context.data["project_settings"]["maya"]["ext_mapping"] From fbfdefd8d4b487a161db7843e6f6995c1c45b88d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 25 Oct 2022 20:09:38 +0800 Subject: [PATCH 23/83] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index fa9e612dad..b0b69304ef 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -28,7 +28,7 @@ class ExtractImportReference(publish.Extractor): tmp_format = "_tmp" @classmethod - def apply_settings(cls, project_setting, system_settings): #noqa + def apply_settings(cls, project_setting, system_settings): cls.active = project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa def process(self, instance): From ef924a2358b883c8118fca413770881e544cf8d2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 16 Nov 2022 21:00:17 +0800 Subject: [PATCH 24/83] Import Reference during Publish --- .../publish/extract_import_reference.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index b0b69304ef..8e0257dafb 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -79,16 +79,19 @@ current_name = "{current_name}" ref_scene_name = "{ref_scene_name}" print(">>> Opening {{}} ...".format(current_name)) cmds.file(current_name, open=True, force=True) -reference_node = cmds.ls(type='reference') print(">>> Processing references") -for ref in reference_node: - ref_file = cmds.referenceQuery(ref, f=True) - print("--- {{}}".format(ref)) - print("--> {{}}".format(ref_file)) - if ref == 'sharedReferenceNode': - cmds.file(ref_file, removeReference=True, referenceNode=ref) - else: - cmds.file(ref_file, importReference=True) +all_reference = cmds.file(q=True, reference=True) or [] +for ref in all_reference: + if cmds.referenceQuery(ref, f=True): + cmds.file(ref, importReference=True) + + nested_ref = cmds.file(q=True, reference=True) + if nested_ref: + for new_ref in nested_ref: + if new_ref not in all_reference: + all_reference.append(new_ref) + +print(">>> Finish importing references") print(">>> Saving scene as {{}}".format(ref_scene_name)) cmds.file(rename=ref_scene_name) From d068ff481401e4261f3447ddbd52a7bd0cdd06a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 24 Nov 2022 10:54:46 +0100 Subject: [PATCH 25/83] Don't use legacy_io session in global plugins --- openpype/plugins/publish/cleanup_farm.py | 6 ++---- .../plugins/publish/collect_anatomy_instance_data.py | 3 +-- .../plugins/publish/collect_scene_loaded_versions.py | 7 ++----- openpype/plugins/publish/integrate.py | 11 +++++------ .../plugins/publish/validate_editorial_asset_name.py | 6 +----- 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/openpype/plugins/publish/cleanup_farm.py b/openpype/plugins/publish/cleanup_farm.py index 2c6c1625bb..b87d4698a2 100644 --- a/openpype/plugins/publish/cleanup_farm.py +++ b/openpype/plugins/publish/cleanup_farm.py @@ -4,8 +4,6 @@ import os import shutil import pyblish.api -from openpype.pipeline import legacy_io - class CleanUpFarm(pyblish.api.ContextPlugin): """Cleans up the staging directory after a successful publish. @@ -23,8 +21,8 @@ class CleanUpFarm(pyblish.api.ContextPlugin): def process(self, context): # Get source host from which farm publishing was started - src_host_name = legacy_io.Session.get("AVALON_APP") - self.log.debug("Host name from session is {}".format(src_host_name)) + src_host_name = context.data["hostName"] + self.log.debug("Host name from context is {}".format(src_host_name)) # Skip process if is not in list of source hosts in which this # plugin should run if src_host_name not in self.allowed_hosts: diff --git a/openpype/plugins/publish/collect_anatomy_instance_data.py b/openpype/plugins/publish/collect_anatomy_instance_data.py index 909b49a07d..3858b4725e 100644 --- a/openpype/plugins/publish/collect_anatomy_instance_data.py +++ b/openpype/plugins/publish/collect_anatomy_instance_data.py @@ -32,7 +32,6 @@ from openpype.client import ( get_subsets, get_last_versions ) -from openpype.pipeline import legacy_io class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): @@ -49,7 +48,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): def process(self, context): self.log.info("Collecting anatomy data for all instances.") - project_name = legacy_io.active_project() + project_name = context.data["projectName"] self.fill_missing_asset_docs(context, project_name) self.fill_instance_data_from_asset(context) self.fill_latest_versions(context, project_name) diff --git a/openpype/plugins/publish/collect_scene_loaded_versions.py b/openpype/plugins/publish/collect_scene_loaded_versions.py index 5ff2b46e3b..627d451f58 100644 --- a/openpype/plugins/publish/collect_scene_loaded_versions.py +++ b/openpype/plugins/publish/collect_scene_loaded_versions.py @@ -1,10 +1,7 @@ import pyblish.api from openpype.client import get_representations -from openpype.pipeline import ( - registered_host, - legacy_io, -) +from openpype.pipeline import registered_host class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): @@ -44,7 +41,7 @@ class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): for container in containers } - project_name = legacy_io.active_project() + project_name = context.data["projectName"] repre_docs = get_representations( project_name, representation_ids=repre_ids, diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 401270a788..e19c3eee7c 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -25,7 +25,6 @@ from openpype.client import ( ) from openpype.lib import source_hash from openpype.lib.file_transaction import FileTransaction -from openpype.pipeline import legacy_io from openpype.pipeline.publish import ( KnownPublishError, get_publish_template_name, @@ -242,7 +241,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): return filtered_repres def register(self, instance, file_transactions, filtered_repres): - project_name = legacy_io.active_project() + project_name = instance.context["projectName"] instance_stagingdir = instance.data.get("stagingDir") if not instance_stagingdir: @@ -803,11 +802,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin): """Return anatomy template name to use for integration""" # Anatomy data is pre-filled by Collectors - - project_name = legacy_io.active_project() + context = instance.context + project_name = context.data["projectName"] # Task can be optional in anatomy data - host_name = instance.context.data["hostName"] + host_name = context.data["hostName"] anatomy_data = instance.data["anatomyData"] family = anatomy_data["family"] task_info = anatomy_data.get("task") or {} @@ -818,7 +817,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): family, task_name=task_info.get("name"), task_type=task_info.get("type"), - project_settings=instance.context.data["project_settings"], + project_settings=context.data["project_settings"], logger=self.log ) diff --git a/openpype/plugins/publish/validate_editorial_asset_name.py b/openpype/plugins/publish/validate_editorial_asset_name.py index 694788c414..4f8a1abf2e 100644 --- a/openpype/plugins/publish/validate_editorial_asset_name.py +++ b/openpype/plugins/publish/validate_editorial_asset_name.py @@ -2,7 +2,6 @@ from pprint import pformat import pyblish.api -from openpype.pipeline import legacy_io from openpype.client import get_assets @@ -28,10 +27,7 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): asset_and_parents = self.get_parents(context) self.log.debug("__ asset_and_parents: {}".format(asset_and_parents)) - if not legacy_io.Session: - legacy_io.install() - - project_name = legacy_io.active_project() + project_name = context.data["projectName"] db_assets = list(get_assets( project_name, fields=["name", "data.parents"] )) From 59b4f5823719e168bdf4d6f0ccb33a380e83405b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 12:59:49 +0100 Subject: [PATCH 26/83] OP-4504 - added originalBasename and originalDirname to instance Will be used to fill 'source' (and 'online') template. Source template is used to publish in-situ, eg. without copying possibly massive files (as pointcaches etc.) --- .../hosts/traypublisher/plugins/publish/collect_source.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_source.py b/openpype/hosts/traypublisher/plugins/publish/collect_source.py index 6ff22be13a..3d983a89ee 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_source.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_source.py @@ -1,3 +1,5 @@ +import os.path + import pyblish.api @@ -22,3 +24,9 @@ class CollectSource(pyblish.api.ContextPlugin): self.log.info(( "Source of instance \"{}\" was already set to \"{}\"" ).format(instance.data["name"], source)) + + if not instance.data.get("originalBasename"): + instance.data["originalBasename"] = os.path.basename(source) + + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = os.path.dirname(source) From 5aed3f9bd817b722f529d9289885a7af1cd155e4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 13:01:54 +0100 Subject: [PATCH 27/83] OP-4504 - added originalDirname to data filling template Will be used to fill 'source' (and 'online') template. --- openpype/plugins/publish/integrate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 401270a788..a5df678332 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -535,7 +535,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "resolutionHeight": "resolution_height", "fps": "fps", "outputName": "output", - "originalBasename": "originalBasename" + "originalBasename": "originalBasename", + "originalDirname": "originalDirname" }.items(): # Allow to take value from representation # if not found also consider instance.data From 07ee68eea1d13960450a170cc86e8285638d1552 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 13:02:49 +0100 Subject: [PATCH 28/83] OP-4504 - added checks for not overwriting same file Added check for publishing to project folder --- openpype/plugins/publish/integrate.py | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index a5df678332..08b59a3574 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -268,6 +268,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ) instance.data["versionEntity"] = version + anatomy = instance.context.data["anatomy"] + # Get existing representations (if any) existing_repres_by_name = { repre_doc["name"].lower(): repre_doc @@ -291,6 +293,19 @@ class IntegrateAsset(pyblish.api.InstancePlugin): instance) for src, dst in prepared["transfers"]: + if src == dst: + self.log.info( + "Source '{}' same as destination '{}'. Skipping." + .format(src, dst)) + continue + + if not self._is_path_in_project_roots(anatomy.all_root_paths, + dst): + self.log.warning( + "Destination '{}' is not in project folder. Skipping" + .format(dst)) + continue + # todo: add support for hardlink transfers file_transactions.add(src, dst) @@ -340,7 +355,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Compute the resource file infos once (files belonging to the # version instance instead of an individual representation) so # we can re-use those file infos per representation - anatomy = instance.context.data["anatomy"] resource_file_infos = self.get_files_info(resource_destinations, sites=sites, anatomy=anatomy) @@ -889,3 +903,22 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "hash": source_hash(path), "sites": sites } + + def _is_path_in_project_roots(self, roots, file_path): + """Checks if 'file_path' starts with any of the roots. + + Used to check that published path belongs to project, eg. we are not + trying to publish to local only folder. + Args: + roots (list of RootItem): {ROOT_NAME: ROOT_PATH} + file_path (str) + Returns: + (bool) + """ + file_path = str(file_path).replace("\\", "/") + for root_item in roots.values(): + if file_path.startswith(root_item.clean_value): + return True + + return False + From 0aa0080f1122fdafde739258f127056ab37a7620 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 15:36:33 +0100 Subject: [PATCH 29/83] OP-4504 - fixed check file in project folder --- openpype/plugins/publish/integrate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 08b59a3574..44cee664b5 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -299,7 +299,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): .format(src, dst)) continue - if not self._is_path_in_project_roots(anatomy.all_root_paths, + if not self._is_path_in_project_roots(anatomy.all_root_paths(), dst): self.log.warning( "Destination '{}' is not in project folder. Skipping" @@ -915,9 +915,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): Returns: (bool) """ - file_path = str(file_path).replace("\\", "/") - for root_item in roots.values(): - if file_path.startswith(root_item.clean_value): + file_path = str(file_path).replace("\\", "/").lower() + for root_item in roots: + if file_path.startswith(root_item.lower()): return True return False From 232743854e06ad68eff562272372177437d4059e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 15:58:01 +0100 Subject: [PATCH 30/83] OP-4504 - moved path parsing to global plugin CollectSources should run on all hosts whenever some output is expected. It seems to be best location to put parsing of source file, unless we want to create completely new separate plugin. --- openpype/hosts/traypublisher/api/plugin.py | 7 +++++- .../plugins/publish/collect_source.py | 6 ----- .../plugins/publish/collect_resources_path.py | 22 ++++++++++++++++++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/traypublisher/api/plugin.py b/openpype/hosts/traypublisher/api/plugin.py index 75930f0f31..d559853fd1 100644 --- a/openpype/hosts/traypublisher/api/plugin.py +++ b/openpype/hosts/traypublisher/api/plugin.py @@ -1,4 +1,4 @@ -from openpype.lib.attribute_definitions import FileDef +from openpype.lib.attribute_definitions import FileDef, BoolDef from openpype.lib.transcoding import IMAGE_EXTENSIONS, VIDEO_EXTENSIONS from openpype.pipeline.create import ( Creator, @@ -129,6 +129,11 @@ class SettingsCreator(TrayPublishCreator): single_item=True, label="Reviewable representations", extensions_label="Single reviewable item" + ), + BoolDef( + "publish_prepared", + default=False, + label="Just publish already prepared" ) ] diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_source.py b/openpype/hosts/traypublisher/plugins/publish/collect_source.py index 3d983a89ee..5121452ca8 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_source.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_source.py @@ -24,9 +24,3 @@ class CollectSource(pyblish.api.ContextPlugin): self.log.info(( "Source of instance \"{}\" was already set to \"{}\"" ).format(instance.data["name"], source)) - - if not instance.data.get("originalBasename"): - instance.data["originalBasename"] = os.path.basename(source) - - if not instance.data.get("originalDirname"): - instance.data["originalDirname"] = os.path.dirname(source) diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index 00f65b8b67..383cea4a25 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -15,7 +15,11 @@ import pyblish.api class CollectResourcesPath(pyblish.api.InstancePlugin): - """Generate directory path where the files and resources will be stored""" + """Generate directory path where the files and resources will be stored. + + Collects folder name and file name from files, if exists, for in-situ + publishing. + """ label = "Collect Resources Path" order = pyblish.api.CollectorOrder + 0.495 @@ -100,3 +104,19 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): self.log.debug("publishDir: \"{}\"".format(publish_folder)) self.log.debug("resourcesDir: \"{}\"".format(resources_folder)) + + # parse folder name and file name for online and source templates + # currentFile comes from hosts workfiles + # source comes from Publisher + current_file = instance.data.get("currentFile") + source = instance.data.get("source") + source_file = current_file or source + if os.path.exists(source_file): + self.log.debug("Parsing paths for {}".format(source_file)) + if not instance.data.get("originalBasename"): + instance.data["originalBasename"] = \ + os.path.basename(source_file) + + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = \ + os.path.dirname(source_file) From 499c32110cbf498234032c389585fb02d4aa9d5e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 16:02:47 +0100 Subject: [PATCH 31/83] OP-4504 - revert of unwanted change --- openpype/hosts/traypublisher/api/plugin.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/hosts/traypublisher/api/plugin.py b/openpype/hosts/traypublisher/api/plugin.py index d559853fd1..75930f0f31 100644 --- a/openpype/hosts/traypublisher/api/plugin.py +++ b/openpype/hosts/traypublisher/api/plugin.py @@ -1,4 +1,4 @@ -from openpype.lib.attribute_definitions import FileDef, BoolDef +from openpype.lib.attribute_definitions import FileDef from openpype.lib.transcoding import IMAGE_EXTENSIONS, VIDEO_EXTENSIONS from openpype.pipeline.create import ( Creator, @@ -129,11 +129,6 @@ class SettingsCreator(TrayPublishCreator): single_item=True, label="Reviewable representations", extensions_label="Single reviewable item" - ), - BoolDef( - "publish_prepared", - default=False, - label="Just publish already prepared" ) ] From 9c381b4b51e9e017bb4366eb14e647d5f61389a6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 18:37:47 +0100 Subject: [PATCH 32/83] OP-4504 - added logic for originalDirname into integrate Adding originalDirname to all hosts could be an ordeal, adding logic here is simpler, but might not be best solution. --- openpype/plugins/publish/integrate.py | 35 +++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 44cee664b5..b48020860b 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -541,8 +541,25 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_data["representation"] = repre["name"] template_data["ext"] = repre["ext"] + stagingdir = repre.get("stagingDir") + if not stagingdir: + # Fall back to instance staging dir if not explicitly + # set for representation in the instance + self.log.debug(( + "Representation uses instance staging dir: {}" + ).format(instance_stagingdir)) + stagingdir = instance_stagingdir + + if not stagingdir: + raise KnownPublishError( + "No staging directory set for representation: {}".format(repre) + ) + # optionals # retrieve additional anatomy data from representation if exists + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = stagingdir + for key, anatomy_key in { # Representation Key: Anatomy data key "resolutionWidth": "resolution_width", @@ -561,20 +578,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if value is not None: template_data[anatomy_key] = value - stagingdir = repre.get("stagingDir") - if not stagingdir: - # Fall back to instance staging dir if not explicitly - # set for representation in the instance - self.log.debug(( - "Representation uses instance staging dir: {}" - ).format(instance_stagingdir)) - stagingdir = instance_stagingdir - - if not stagingdir: - raise KnownPublishError( - "No staging directory set for representation: {}".format(repre) - ) - self.log.debug("Anatomy template name: {}".format(template_name)) anatomy = instance.context.data["anatomy"] publish_template_category = anatomy.templates[template_name] @@ -600,6 +603,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): )) src_collection = src_collections[0] + template_data["originalBasename"] = src_collection.head[:-1] destination_indexes = list(src_collection.indexes) # Use last frame for minimum padding # - that should cover both 'udim' and 'frame' minimum padding @@ -684,7 +688,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): raise KnownPublishError( "This is a bug. Representation file name is full path" ) - + if not template_data.get("originalBasename"): + template_data["originalBasename"] = fname # Manage anatomy template data template_data.pop("frame", None) if is_udim: From 1106f8cf5ad6d23051643c83fd894846127ec600 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 18:38:24 +0100 Subject: [PATCH 33/83] OP-4504 - check for None --- openpype/plugins/publish/collect_resources_path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index 383cea4a25..0f55e65c9e 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -111,7 +111,7 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): current_file = instance.data.get("currentFile") source = instance.data.get("source") source_file = current_file or source - if os.path.exists(source_file): + if source_file and os.path.exists(source_file): self.log.debug("Parsing paths for {}".format(source_file)) if not instance.data.get("originalBasename"): instance.data["originalBasename"] = \ From b67c8e28a9115c16970596dfceaf2ada1e425ea0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 19:16:39 +0100 Subject: [PATCH 34/83] OP-4504 - cleanup of logic --- openpype/plugins/publish/integrate.py | 43 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index b48020860b..dbad90af93 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -293,17 +293,12 @@ class IntegrateAsset(pyblish.api.InstancePlugin): instance) for src, dst in prepared["transfers"]: - if src == dst: - self.log.info( - "Source '{}' same as destination '{}'. Skipping." - .format(src, dst)) + + if self._are_paths_same(src, dst): continue if not self._is_path_in_project_roots(anatomy.all_root_paths(), dst): - self.log.warning( - "Destination '{}' is not in project folder. Skipping" - .format(dst)) continue # todo: add support for hardlink transfers @@ -316,13 +311,20 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # .ma representation. Those destination paths are pre-defined, etc. # todo: should we move or simplify this logic? resource_destinations = set() - for src, dst in instance.data.get("transfers", []): - file_transactions.add(src, dst, mode=FileTransaction.MODE_COPY) - resource_destinations.add(os.path.abspath(dst)) - for src, dst in instance.data.get("hardlinks", []): - file_transactions.add(src, dst, mode=FileTransaction.MODE_HARDLINK) - resource_destinations.add(os.path.abspath(dst)) + file_copy_modes = [ + ("transfers", FileTransaction.MODE_COPY), + ("hardlinks", FileTransaction.MODE_HARDLINK) + ] + for files_type, copy_mode in zip(*file_copy_modes): # unpack + for src, dst in instance.data.get(files_type, []): + if self._are_paths_same(src, dst): + continue + if not self._is_path_in_project_roots(anatomy.all_root_paths(), + dst): + continue + file_transactions.add(src, dst, mode=copy_mode) + resource_destinations.add(os.path.abspath(dst)) # Bulk write to the database # We write the subset and version to the database before the File @@ -924,6 +926,19 @@ class IntegrateAsset(pyblish.api.InstancePlugin): for root_item in roots: if file_path.startswith(root_item.lower()): return True - + self.log.warning( + "Destination '{}' is not in project folder. Skipping" + .format(file_path)) return False + def _are_paths_same(self, src, dst): + src = str(src).replace("\\", "/").lower() + dst = str(dst).replace("\\", "/").lower() + + same = src == dst + if same: + self.log.info( + "Source '{}' same as destination '{}'. Skipping." + .format(src, dst)) + return same + From ddfaae8a798c9ba7ea12b56c4547f2e3214c8c6c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 2 Dec 2022 14:28:26 +0100 Subject: [PATCH 35/83] OP-4504 - added source template to defaults Source template is used in-situ publishing, eg. use files at their location, don't copy them anywhere. --- openpype/settings/defaults/project_anatomy/templates.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 0ac56a4dad..e4814257bc 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -53,11 +53,17 @@ "file": "{originalBasename}<.{@frame}><_{udim}>.{ext}", "path": "{@folder}/{@file}" }, + "source": { + "folder": "{originalBasename}<.{@frame}><_{udim}>.{ext}", + "file": "{originalDirname}", + "path": "{@folder}/{@file}" + }, "__dynamic_keys_labels__": { "maya2unreal": "Maya to Unreal", "simpleUnrealTextureHero": "Simple Unreal Texture - Hero", "simpleUnrealTexture": "Simple Unreal Texture", - "online": "online" + "online": "online", + "source": "source" } } } \ No newline at end of file From af3ebebb264ae559d98fa296c18da534d3590c66 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 2 Dec 2022 14:47:01 +0100 Subject: [PATCH 36/83] OP-4504 - Hound --- openpype/hosts/traypublisher/plugins/publish/collect_source.py | 2 -- openpype/plugins/publish/integrate.py | 1 - 2 files changed, 3 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_source.py b/openpype/hosts/traypublisher/plugins/publish/collect_source.py index 5121452ca8..6ff22be13a 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_source.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_source.py @@ -1,5 +1,3 @@ -import os.path - import pyblish.api diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index dbad90af93..4c26f28862 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -941,4 +941,3 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "Source '{}' same as destination '{}'. Skipping." .format(src, dst)) return same - From d1ee451b9a547d3ff8d480022efe4d020d9f7bc7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 6 Dec 2022 15:06:21 +0100 Subject: [PATCH 37/83] OP-4504 - update logging --- openpype/plugins/publish/extract_thumbnail_from_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail_from_source.py b/openpype/plugins/publish/extract_thumbnail_from_source.py index 8da1213807..1165c80318 100644 --- a/openpype/plugins/publish/extract_thumbnail_from_source.py +++ b/openpype/plugins/publish/extract_thumbnail_from_source.py @@ -76,7 +76,7 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin): def _create_thumbnail(self, context, thumbnail_source): if not thumbnail_source: - self.log.debug("Thumbnail source not filled. Skipping.") + self.log.debug("Thumbnail source on context not filled. Skipping.") return if not os.path.exists(thumbnail_source): From c9496bcfe3271631124d557af9166c0a2c3879cc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:22:53 +0100 Subject: [PATCH 38/83] OP-4504 - change boolean test to validation with exception It actually shouldn't allow to publish into non project folder (like artists own c:/ drive). --- openpype/plugins/publish/integrate.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index f3683c4214..5e76521550 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -293,11 +293,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): instance) for src, dst in prepared["transfers"]: - if self._are_paths_same(src, dst): - continue + self._validate_path_in_project_roots(anatomy.all_root_paths(), + dst) - if not self._is_path_in_project_roots(anatomy.all_root_paths(), - dst): + if self._are_paths_same(src, dst): continue # todo: add support for hardlink transfers @@ -317,11 +316,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ] for files_type, copy_mode in zip(*file_copy_modes): # unpack for src, dst in instance.data.get(files_type, []): + self._validate_path_in_project_roots(anatomy.all_root_paths(), + dst) if self._are_paths_same(src, dst): continue - if not self._is_path_in_project_roots(anatomy.all_root_paths(), - dst): - continue file_transactions.add(src, dst, mode=copy_mode) resource_destinations.add(os.path.abspath(dst)) @@ -910,7 +908,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "sites": sites } - def _is_path_in_project_roots(self, roots, file_path): + def _validate_path_in_project_roots(self, roots, file_path): """Checks if 'file_path' starts with any of the roots. Used to check that published path belongs to project, eg. we are not @@ -918,17 +916,17 @@ class IntegrateAsset(pyblish.api.InstancePlugin): Args: roots (list of RootItem): {ROOT_NAME: ROOT_PATH} file_path (str) - Returns: - (bool) + Raises + (KnownPublishError) """ file_path = str(file_path).replace("\\", "/").lower() for root_item in roots: if file_path.startswith(root_item.lower()): return True - self.log.warning( - "Destination '{}' is not in project folder. Skipping" - .format(file_path)) - return False + raise KnownPublishError(( + "Destination path {} ".format(file_path) + + "must be in project dir" + )) def _are_paths_same(self, src, dst): src = str(src).replace("\\", "/").lower() From 3fabd516ea02b762505377e4d60853e6ebc60c9e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:25:16 +0100 Subject: [PATCH 39/83] OP-4504 - weird unpacking not necessary --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 5e76521550..6b359af1d2 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -314,7 +314,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ("transfers", FileTransaction.MODE_COPY), ("hardlinks", FileTransaction.MODE_HARDLINK) ] - for files_type, copy_mode in zip(*file_copy_modes): # unpack + for files_type, copy_mode in file_copy_modes: for src, dst in instance.data.get(files_type, []): self._validate_path_in_project_roots(anatomy.all_root_paths(), dst) From 0a12a42460cef6770b4761d935cce38a2f6fc1cb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:29:44 +0100 Subject: [PATCH 40/83] OP-4504 - use existing method to check if path in project --- openpype/plugins/publish/integrate.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 6b359af1d2..ce31831f1e 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -293,8 +293,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): instance) for src, dst in prepared["transfers"]: - self._validate_path_in_project_roots(anatomy.all_root_paths(), - dst) + self._validate_path_in_project_roots(anatomy, dst) if self._are_paths_same(src, dst): continue @@ -316,8 +315,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ] for files_type, copy_mode in file_copy_modes: for src, dst in instance.data.get(files_type, []): - self._validate_path_in_project_roots(anatomy.all_root_paths(), - dst) + self._validate_path_in_project_roots(anatomy, dst) if self._are_paths_same(src, dst): continue file_transactions.add(src, dst, mode=copy_mode) @@ -908,25 +906,23 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "sites": sites } - def _validate_path_in_project_roots(self, roots, file_path): + def _validate_path_in_project_roots(self, anatomy, file_path): """Checks if 'file_path' starts with any of the roots. Used to check that published path belongs to project, eg. we are not trying to publish to local only folder. Args: - roots (list of RootItem): {ROOT_NAME: ROOT_PATH} + anatomy (Anatomy) file_path (str) Raises (KnownPublishError) """ - file_path = str(file_path).replace("\\", "/").lower() - for root_item in roots: - if file_path.startswith(root_item.lower()): - return True - raise KnownPublishError(( - "Destination path {} ".format(file_path) + - "must be in project dir" - )) + found, _ = anatomy.find_root_template_from_path(file_path) + if not found: + raise KnownPublishError(( + "Destination path {} ".format(file_path) + + "must be in project dir" + )) def _are_paths_same(self, src, dst): src = str(src).replace("\\", "/").lower() From cccd9c61ddac217d59480c8fafee4eba8b569412 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:34:26 +0100 Subject: [PATCH 41/83] OP-4504 - logging message is wrong as it is called on instances also --- openpype/plugins/publish/extract_thumbnail_from_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail_from_source.py b/openpype/plugins/publish/extract_thumbnail_from_source.py index 084815915f..03df1455e2 100644 --- a/openpype/plugins/publish/extract_thumbnail_from_source.py +++ b/openpype/plugins/publish/extract_thumbnail_from_source.py @@ -77,7 +77,7 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin): def _create_thumbnail(self, context, thumbnail_source): if not thumbnail_source: - self.log.debug("Thumbnail source on context not filled. Skipping.") + self.log.debug("Thumbnail source not filled. Skipping.") return if not os.path.exists(thumbnail_source): From 30eca6f6ca3487bee2b758e85cce6dbeb0d26354 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 14:10:52 +0100 Subject: [PATCH 42/83] OP-4504 - fix default source template --- openpype/settings/defaults/project_anatomy/templates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index e4814257bc..32230e0625 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -54,8 +54,8 @@ "path": "{@folder}/{@file}" }, "source": { - "folder": "{originalBasename}<.{@frame}><_{udim}>.{ext}", - "file": "{originalDirname}", + "folder": "{root[work]}/{originalDirname}", + "file": "{originalBasename}<.{@frame}><_{udim}>.{ext}", "path": "{@folder}/{@file}" }, "__dynamic_keys_labels__": { From 4c53a77a83fd4d79825f11339d7561c23f3797f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 17:48:59 +0100 Subject: [PATCH 43/83] OP-4504 - make root comparison case insensitive for windows find_root_template_from_path tries to find root in passed path with case sensitivity, on Windows it doesn't make sense C:// == c://. Keep all other paths case sensitive. --- openpype/pipeline/anatomy.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index 908dc2b187..a50f8f67bb 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -1106,17 +1106,21 @@ class RootItem(FormatObject): result = False output = str(path) - root_paths = list(self.cleaned_data.values()) mod_path = self.clean_path(path) - for root_path in root_paths: + for root_os, root_path in self.cleaned_data.items(): # Skip empty paths if not root_path: continue - if mod_path.startswith(root_path): + _mod_path = mod_path # reset to original cleaned value + if root_os == "windows": + root_path = root_path.lower() + _mod_path = _mod_path.lower() + + if _mod_path.startswith(root_path): result = True replacement = "{" + self.full_key() + "}" - output = replacement + mod_path[len(root_path):] + output = replacement + _mod_path[len(root_path):] break return (result, output) @@ -1206,6 +1210,7 @@ class Roots: Raises: ValueError: When roots are not entered and can't be loaded. """ + print("!roots::{}".format(roots)) if roots is None: log.debug( "Looking for matching root in path \"{}\".".format(path) @@ -1216,10 +1221,12 @@ class Roots: raise ValueError("Roots are not set. Can't find path.") if isinstance(roots, RootItem): + print("here") return roots.find_root_template_from_path(path) for root_name, _root in roots.items(): - success, result = self.find_root_template_from_path(path, _root) + print("root::{}".format(_root)) + success, result = self.find_root_template_from_path(path.lower(), _root) if success: log.info("Found match in root \"{}\".".format(root_name)) return success, result From 726c8f2cc12f5362fc255f2b269c5b356794b0e6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 17:51:41 +0100 Subject: [PATCH 44/83] OP-4504 - fix resolving of originalDirname If instance has originalDirname collected, all repres should use this as a target folder (that allows copying transient items from temporary folders into folder where source item comes from). --- openpype/plugins/publish/integrate.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index ce31831f1e..45710c8f41 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -554,17 +554,13 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # optionals # retrieve additional anatomy data from representation if exists - if not instance.data.get("originalDirname"): - instance.data["originalDirname"] = stagingdir - for key, anatomy_key in { # Representation Key: Anatomy data key "resolutionWidth": "resolution_width", "resolutionHeight": "resolution_height", "fps": "fps", "outputName": "output", - "originalBasename": "originalBasename", - "originalDirname": "originalDirname" + "originalBasename": "originalBasename" }.items(): # Allow to take value from representation # if not found also consider instance.data @@ -582,6 +578,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): is_udim = bool(repre.get("udim")) + # store as originalDirname only original value without project root + # if instance collected originalDirname it should be used for all repre + # useful to storing transient items, eg. thumbnails, from temp to final + original_directory = instance.data.get("originalDirname") or stagingdir + _rootless = self.get_rootless_path(anatomy, original_directory) + without_root = _rootless[_rootless.rfind('}')+2:] + template_data["originalDirname"] = without_root + is_sequence_representation = isinstance(files, (list, tuple)) if is_sequence_representation: # Collection of files (sequence) @@ -685,8 +689,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): raise KnownPublishError( "This is a bug. Representation file name is full path" ) - if not template_data.get("originalBasename"): - template_data["originalBasename"] = fname + template_data["originalBasename"] = fname # Manage anatomy template data template_data.pop("frame", None) if is_udim: @@ -917,8 +920,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): Raises (KnownPublishError) """ - found, _ = anatomy.find_root_template_from_path(file_path) - if not found: + path = self.get_rootless_path(anatomy, file_path) + if not path: raise KnownPublishError(( "Destination path {} ".format(file_path) + "must be in project dir" From a1f85d3978e0ad87c13c8f415b9b91a375429a67 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 17:57:10 +0100 Subject: [PATCH 45/83] OP-4504 - use always stagingDir from instance instead of repre Representation stagingDir might be in temporary folders (for thumbnails etc.), use value from instance as a backup instead. --- openpype/plugins/publish/integrate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 45710c8f41..1b79b5b858 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -581,7 +581,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # store as originalDirname only original value without project root # if instance collected originalDirname it should be used for all repre # useful to storing transient items, eg. thumbnails, from temp to final - original_directory = instance.data.get("originalDirname") or stagingdir + original_directory = ( + instance.data.get("originalDirname") or instance_stagingdir) _rootless = self.get_rootless_path(anatomy, original_directory) without_root = _rootless[_rootless.rfind('}')+2:] template_data["originalDirname"] = without_root @@ -694,7 +695,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_data.pop("frame", None) if is_udim: template_data["udim"] = repre["udim"][0] - # Construct destination filepath from template anatomy_filled = anatomy.format(template_data) template_filled = anatomy_filled[template_name]["path"] From d8ed8998b2274699760a88f99bb4d09a97fd1d51 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 18:00:24 +0100 Subject: [PATCH 46/83] OP-4504 - removed unwanted lower Removed logging --- openpype/pipeline/anatomy.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index a50f8f67bb..969e1570fb 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -1210,7 +1210,6 @@ class Roots: Raises: ValueError: When roots are not entered and can't be loaded. """ - print("!roots::{}".format(roots)) if roots is None: log.debug( "Looking for matching root in path \"{}\".".format(path) @@ -1221,12 +1220,10 @@ class Roots: raise ValueError("Roots are not set. Can't find path.") if isinstance(roots, RootItem): - print("here") return roots.find_root_template_from_path(path) for root_name, _root in roots.items(): - print("root::{}".format(_root)) - success, result = self.find_root_template_from_path(path.lower(), _root) + success, result = self.find_root_template_from_path(path, _root) if success: log.info("Found match in root \"{}\".".format(root_name)) return success, result From 98f45c24a6a697ae533a48c17cc5ebc9b7930dbf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 18:02:54 +0100 Subject: [PATCH 47/83] OP-4504 - Hound --- openpype/plugins/publish/integrate.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 1b79b5b858..7ef279d787 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -584,7 +584,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): original_directory = ( instance.data.get("originalDirname") or instance_stagingdir) _rootless = self.get_rootless_path(anatomy, original_directory) - without_root = _rootless[_rootless.rfind('}')+2:] + relative_path_start = _rootless.rfind('}') + 2 + without_root = _rootless[relative_path_start:] template_data["originalDirname"] = without_root is_sequence_representation = isinstance(files, (list, tuple)) @@ -923,8 +924,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): path = self.get_rootless_path(anatomy, file_path) if not path: raise KnownPublishError(( - "Destination path {} ".format(file_path) + - "must be in project dir" + "Destination path {} ".format(file_path) + + "must be in project dir" )) def _are_paths_same(self, src, dst): From fc10b26ea0610dc5d32ed7c2ce285dac8fdef9eb Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 11:20:24 +0000 Subject: [PATCH 48/83] Implemented creator and extractor for uasset --- .../unreal/plugins/create/create_uasset.py | 61 +++++++++++++++++++ .../unreal/plugins/publish/extract_uasset.py | 45 ++++++++++++++ openpype/plugins/publish/integrate.py | 3 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/unreal/plugins/create/create_uasset.py create mode 100644 openpype/hosts/unreal/plugins/publish/extract_uasset.py diff --git a/openpype/hosts/unreal/plugins/create/create_uasset.py b/openpype/hosts/unreal/plugins/create/create_uasset.py new file mode 100644 index 0000000000..ee584ac00c --- /dev/null +++ b/openpype/hosts/unreal/plugins/create/create_uasset.py @@ -0,0 +1,61 @@ +"""Create UAsset.""" +from pathlib import Path + +import unreal + +from openpype.hosts.unreal.api import pipeline +from openpype.pipeline import LegacyCreator + + +class CreateUAsset(LegacyCreator): + """UAsset.""" + + name = "UAsset" + label = "UAsset" + family = "uasset" + icon = "cube" + + root = "/Game/OpenPype" + suffix = "_INS" + + def __init__(self, *args, **kwargs): + super(CreateUAsset, self).__init__(*args, **kwargs) + + def process(self): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + subset = self.data["subset"] + path = f"{self.root}/PublishInstances/" + + unreal.EditorAssetLibrary.make_directory(path) + + selection = [] + if (self.options or {}).get("useSelection"): + sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + selection = [a.get_path_name() for a in sel_objects] + + if len(selection) != 1: + raise RuntimeError("Please select only one object.") + + obj = selection[0] + + asset = ar.get_asset_by_object_path(obj).get_asset() + sys_path = unreal.SystemLibrary.get_system_path(asset) + + if not sys_path: + raise RuntimeError( + f"{Path(obj).name} is not on the disk. Likely it needs to" + "be saved first.") + + if Path(sys_path).suffix != ".uasset": + raise RuntimeError(f"{Path(sys_path).name} is not a UAsset.") + + unreal.log("selection: {}".format(selection)) + container_name = f"{subset}{self.suffix}" + pipeline.create_publish_instance( + instance=container_name, path=path) + + data = self.data.copy() + data["members"] = selection + + pipeline.imprint(f"{path}/{container_name}", data) diff --git a/openpype/hosts/unreal/plugins/publish/extract_uasset.py b/openpype/hosts/unreal/plugins/publish/extract_uasset.py new file mode 100644 index 0000000000..99279e38a1 --- /dev/null +++ b/openpype/hosts/unreal/plugins/publish/extract_uasset.py @@ -0,0 +1,45 @@ +from pathlib import Path +import shutil + +import unreal +from unreal import EditorLevelLibrary as ell +from unreal import EditorAssetLibrary as eal + +from openpype.client import get_representation_by_name +from openpype.pipeline import legacy_io, publish + + +class ExtractUAsset(publish.Extractor): + """Extract a UAsset.""" + + label = "Extract UAsset" + hosts = ["unreal"] + families = ["uasset"] + optional = True + + def process(self, instance): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + self.log.info("Performing extraction..") + + staging_dir = self.staging_dir(instance) + filename = "{}.uasset".format(instance.name) + + obj = instance[0] + + asset = ar.get_asset_by_object_path(obj).get_asset() + sys_path = unreal.SystemLibrary.get_system_path(asset) + filename = Path(sys_path).name + + shutil.copy(sys_path, staging_dir) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'uasset', + 'ext': 'uasset', + 'files': filename, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 6a85a87129..6efff8440c 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -130,7 +130,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "mvUsdComposition", "mvUsdOverride", "simpleUnrealTexture", - "online" + "online", + "uasset" ] default_template_name = "publish" From 2b566bb594229d452dde7cb871239e07fac9366c Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 11:20:47 +0000 Subject: [PATCH 49/83] Implemented balidator to check if the uasset has any dependency --- .../publish/validate_no_dependencies.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py diff --git a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py new file mode 100644 index 0000000000..b7f42a772b --- /dev/null +++ b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py @@ -0,0 +1,40 @@ +import unreal + +import pyblish.api + + +class ValidateNoDependencies(pyblish.api.InstancePlugin): + """Ensure that the uasset has no dependencies + + The uasset is checked for dependencies. If there are any, the instance + cannot be published. + """ + + order = pyblish.api.ValidatorOrder + label = "Check no dependencies" + families = ["uasset"] + hosts = ["unreal"] + optional = True + + def process(self, instance): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + all_dependencies = [] + + for obj in instance[:]: + asset = ar.get_asset_by_object_path(obj) + dependencies = ar.get_dependencies( + asset.package_name, + unreal.AssetRegistryDependencyOptions( + include_soft_package_references=True, + include_hard_package_references=True, + include_searchable_names=False, + include_soft_management_references=False, + include_hard_management_references=False + )) + if dependencies: + for dep in dependencies: + all_dependencies.append(str(dep)) + + if all_dependencies: + raise RuntimeError( + f"Dependencies found: {all_dependencies}") From ee3d88756cbb75deb966fab37c8369d8a4d9df6d Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 13:00:31 +0000 Subject: [PATCH 50/83] Implemented loading --- .../hosts/unreal/plugins/load/load_uasset.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 openpype/hosts/unreal/plugins/load/load_uasset.py diff --git a/openpype/hosts/unreal/plugins/load/load_uasset.py b/openpype/hosts/unreal/plugins/load/load_uasset.py new file mode 100644 index 0000000000..e3f967c43d --- /dev/null +++ b/openpype/hosts/unreal/plugins/load/load_uasset.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +"""Load UAsset.""" +from pathlib import Path +import shutil + +from openpype.pipeline import ( + get_representation_path, + AVALON_CONTAINER_ID +) +from openpype.hosts.unreal.api import plugin +from openpype.hosts.unreal.api import pipeline as unreal_pipeline +import unreal # noqa + + +class UAssetLoader(plugin.Loader): + """Load UAsset.""" + + families = ["uasset"] + label = "Load UAsset" + representations = ["uasset"] + icon = "cube" + color = "orange" + + def load(self, context, name, namespace, options): + """Load and containerise representation into Content Browser. + + Args: + context (dict): application context + name (str): subset name + namespace (str): in Unreal this is basically path to container. + This is not passed here, so namespace is set + by `containerise()` because only then we know + real path. + options (dict): Those would be data to be imprinted. This is not + used now, data are imprinted by `containerise()`. + + Returns: + list(str): list of container content + """ + + # Create directory for asset and OpenPype container + root = "/Game/OpenPype/Assets" + if options and options.get("asset_dir"): + root = options["asset_dir"] + asset = context.get('asset').get('name') + suffix = "_CON" + if asset: + asset_name = "{}_{}".format(asset, name) + else: + asset_name = "{}".format(name) + + tools = unreal.AssetToolsHelpers().get_asset_tools() + asset_dir, container_name = tools.create_unique_asset_name( + "{}/{}/{}".format(root, asset, name), suffix="") + + container_name += suffix + + unreal.EditorAssetLibrary.make_directory(asset_dir) + + # Create Asset Container + container = unreal_pipeline.create_container( + container=container_name, path=asset_dir) + + container_path = unreal.SystemLibrary.get_system_path(container) + destination_path = Path(container_path).parent.as_posix() + + shutil.copy(self.fname, destination_path) + + data = { + "schema": "openpype:container-2.0", + "id": AVALON_CONTAINER_ID, + "asset": asset, + "namespace": asset_dir, + "container_name": container_name, + "asset_name": asset_name, + "loader": str(self.__class__.__name__), + "representation": context["representation"]["_id"], + "parent": context["representation"]["parent"], + "family": context["representation"]["context"]["family"] + } + unreal_pipeline.imprint( + "{}/{}".format(asset_dir, container_name), data) + + asset_content = unreal.EditorAssetLibrary.list_assets( + asset_dir, recursive=True, include_folder=True + ) + + for a in asset_content: + unreal.EditorAssetLibrary.save_asset(a) + + return asset_content From a847626aac962d0f954b14a8de0e341793092d69 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 16:12:48 +0000 Subject: [PATCH 51/83] Don't use container path to get destination folder --- .../hosts/unreal/plugins/load/load_uasset.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/unreal/plugins/load/load_uasset.py b/openpype/hosts/unreal/plugins/load/load_uasset.py index e3f967c43d..76c4de1fbe 100644 --- a/openpype/hosts/unreal/plugins/load/load_uasset.py +++ b/openpype/hosts/unreal/plugins/load/load_uasset.py @@ -40,8 +40,6 @@ class UAssetLoader(plugin.Loader): # Create directory for asset and OpenPype container root = "/Game/OpenPype/Assets" - if options and options.get("asset_dir"): - root = options["asset_dir"] asset = context.get('asset').get('name') suffix = "_CON" if asset: @@ -57,15 +55,17 @@ class UAssetLoader(plugin.Loader): unreal.EditorAssetLibrary.make_directory(asset_dir) - # Create Asset Container - container = unreal_pipeline.create_container( - container=container_name, path=asset_dir) - - container_path = unreal.SystemLibrary.get_system_path(container) - destination_path = Path(container_path).parent.as_posix() + destination_path = asset_dir.replace( + "/Game", + Path(unreal.Paths.project_content_dir()).as_posix(), + 1) shutil.copy(self.fname, destination_path) + # Create Asset Container + unreal_pipeline.create_container( + container=container_name, path=asset_dir) + data = { "schema": "openpype:container-2.0", "id": AVALON_CONTAINER_ID, From bc574e5e5e8174c69528e3834465de677b857115 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 17:23:10 +0000 Subject: [PATCH 52/83] Implemented update and remove --- .../hosts/unreal/plugins/load/load_uasset.py | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/load/load_uasset.py b/openpype/hosts/unreal/plugins/load/load_uasset.py index 76c4de1fbe..eccfc7b445 100644 --- a/openpype/hosts/unreal/plugins/load/load_uasset.py +++ b/openpype/hosts/unreal/plugins/load/load_uasset.py @@ -60,7 +60,7 @@ class UAssetLoader(plugin.Loader): Path(unreal.Paths.project_content_dir()).as_posix(), 1) - shutil.copy(self.fname, destination_path) + shutil.copy(self.fname, f"{destination_path}/{name}.uasset") # Create Asset Container unreal_pipeline.create_container( @@ -89,3 +89,57 @@ class UAssetLoader(plugin.Loader): unreal.EditorAssetLibrary.save_asset(a) return asset_content + + def update(self, container, representation): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + asset_dir = container["namespace"] + name = representation["context"]["subset"] + + destination_path = asset_dir.replace( + "/Game", + Path(unreal.Paths.project_content_dir()).as_posix(), + 1) + + asset_content = unreal.EditorAssetLibrary.list_assets( + asset_dir, recursive=False, include_folder=True + ) + + for asset in asset_content: + obj = ar.get_asset_by_object_path(asset).get_asset() + if not obj.get_class().get_name() == 'AssetContainer': + unreal.EditorAssetLibrary.delete_asset(asset) + + update_filepath = get_representation_path(representation) + + shutil.copy(update_filepath, f"{destination_path}/{name}.uasset") + + container_path = "{}/{}".format(container["namespace"], + container["objectName"]) + # update metadata + unreal_pipeline.imprint( + container_path, + { + "representation": str(representation["_id"]), + "parent": str(representation["parent"]) + }) + + asset_content = unreal.EditorAssetLibrary.list_assets( + asset_dir, recursive=True, include_folder=True + ) + + for a in asset_content: + unreal.EditorAssetLibrary.save_asset(a) + + def remove(self, container): + path = container["namespace"] + parent_path = Path(path).parent.as_posix() + + unreal.EditorAssetLibrary.delete_directory(path) + + asset_content = unreal.EditorAssetLibrary.list_assets( + parent_path, recursive=False + ) + + if len(asset_content) == 0: + unreal.EditorAssetLibrary.delete_directory(parent_path) From 976ba9b6ce5396da651d08b032fab052856fd3f4 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 17:28:26 +0000 Subject: [PATCH 53/83] Hound fixes --- openpype/hosts/unreal/plugins/publish/extract_uasset.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/hosts/unreal/plugins/publish/extract_uasset.py b/openpype/hosts/unreal/plugins/publish/extract_uasset.py index 99279e38a1..89d779d368 100644 --- a/openpype/hosts/unreal/plugins/publish/extract_uasset.py +++ b/openpype/hosts/unreal/plugins/publish/extract_uasset.py @@ -2,11 +2,8 @@ from pathlib import Path import shutil import unreal -from unreal import EditorLevelLibrary as ell -from unreal import EditorAssetLibrary as eal -from openpype.client import get_representation_by_name -from openpype.pipeline import legacy_io, publish +from openpype.pipeline import publish class ExtractUAsset(publish.Extractor): From fe0336c4359c519dd787aea5f5683e4fa871c171 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 12 Dec 2022 10:34:01 +0100 Subject: [PATCH 54/83] OP-4504 - removed path comparison function Obsolete as it is part of file_transaction file --- openpype/plugins/publish/integrate.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 0a885733bd..041f7b1b19 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -297,9 +297,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): for src, dst in prepared["transfers"]: self._validate_path_in_project_roots(anatomy, dst) - if self._are_paths_same(src, dst): - continue - # todo: add support for hardlink transfers file_transactions.add(src, dst) @@ -318,8 +315,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): for files_type, copy_mode in file_copy_modes: for src, dst in instance.data.get(files_type, []): self._validate_path_in_project_roots(anatomy, dst) - if self._are_paths_same(src, dst): - continue + file_transactions.add(src, dst, mode=copy_mode) resource_destinations.add(os.path.abspath(dst)) @@ -929,14 +925,3 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "Destination path {} ".format(file_path) + "must be in project dir" )) - - def _are_paths_same(self, src, dst): - src = str(src).replace("\\", "/").lower() - dst = str(dst).replace("\\", "/").lower() - - same = src == dst - if same: - self.log.info( - "Source '{}' same as destination '{}'. Skipping." - .format(src, dst)) - return same From d18fc94c01bb2044ef209658f4cab32f8cd87ee8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 12 Dec 2022 12:41:26 +0100 Subject: [PATCH 55/83] OP-4504 - fix for deadline publishing --- openpype/plugins/publish/integrate.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 041f7b1b19..ce37a53c65 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -581,10 +581,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # useful to storing transient items, eg. thumbnails, from temp to final original_directory = ( instance.data.get("originalDirname") or instance_stagingdir) - _rootless = self.get_rootless_path(anatomy, original_directory) - relative_path_start = _rootless.rfind('}') + 2 - without_root = _rootless[relative_path_start:] - template_data["originalDirname"] = without_root + if original_directory: + _rootless = self.get_rootless_path(anatomy, original_directory) + relative_path_start = _rootless.rfind('}') + 2 + without_root = _rootless[relative_path_start:] + template_data["originalDirname"] = without_root is_sequence_representation = isinstance(files, (list, tuple)) if is_sequence_representation: From 8a267f6c340bc31d4c15dd5d90bc7c534d1a03af Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 12 Dec 2022 16:18:25 +0100 Subject: [PATCH 56/83] OP-4504 - handle originalDirname only if in template --- openpype/plugins/publish/integrate.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index ce37a53c65..4692cefe4d 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -295,8 +295,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): instance) for src, dst in prepared["transfers"]: - self._validate_path_in_project_roots(anatomy, dst) - # todo: add support for hardlink transfers file_transactions.add(src, dst) @@ -576,13 +574,21 @@ class IntegrateAsset(pyblish.api.InstancePlugin): is_udim = bool(repre.get("udim")) - # store as originalDirname only original value without project root - # if instance collected originalDirname it should be used for all repre - # useful to storing transient items, eg. thumbnails, from temp to final - original_directory = ( - instance.data.get("originalDirname") or instance_stagingdir) - if original_directory: + # handle publish in place + if "originalDirname" in template: + # store as originalDirname only original value without project root + # if instance collected originalDirname is present, it should be + # used for all represe + # from temp to final + original_directory = ( + instance.data.get("originalDirname") or instance_stagingdir) + _rootless = self.get_rootless_path(anatomy, original_directory) + if _rootless == original_directory: + raise KnownPublishError(( + "Destination path '{}' ".format(original_directory) + + "must be in project dir" + )) relative_path_start = _rootless.rfind('}') + 2 without_root = _rootless[relative_path_start:] template_data["originalDirname"] = without_root @@ -923,6 +929,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): path = self.get_rootless_path(anatomy, file_path) if not path: raise KnownPublishError(( - "Destination path {} ".format(file_path) + + "Destination path '{}' ".format(file_path) + "must be in project dir" )) From d9e6bedf3b7fbf3c30825df4aff91a4965bd7d7c Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Dec 2022 10:35:15 +0000 Subject: [PATCH 57/83] Do not check soft references --- .../hosts/unreal/plugins/publish/validate_no_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py index b7f42a772b..79d54306c4 100644 --- a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py +++ b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py @@ -25,7 +25,7 @@ class ValidateNoDependencies(pyblish.api.InstancePlugin): dependencies = ar.get_dependencies( asset.package_name, unreal.AssetRegistryDependencyOptions( - include_soft_package_references=True, + include_soft_package_references=False, include_hard_package_references=True, include_searchable_names=False, include_soft_management_references=False, From bf10a77fb6f1dcde764020de3588e54391df9f4a Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Dec 2022 10:50:38 +0000 Subject: [PATCH 58/83] Check only dependencies that are in the Content folder We ignore native dependencies and dependencies from plugins --- .../hosts/unreal/plugins/publish/validate_no_dependencies.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py index 79d54306c4..c760129550 100644 --- a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py +++ b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py @@ -33,7 +33,8 @@ class ValidateNoDependencies(pyblish.api.InstancePlugin): )) if dependencies: for dep in dependencies: - all_dependencies.append(str(dep)) + if str(dep).startswith("/Game/"): + all_dependencies.append(str(dep)) if all_dependencies: raise RuntimeError( From 9bf00f9cfebb4c5b0ec6586672308d1a74cd6376 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 14 Dec 2022 14:41:52 +0100 Subject: [PATCH 59/83] OP-4504 - added collector for originalDirname --- .../plugins/publish/collect_resources_path.py | 16 ------- .../publish/collect_source_for_source.py | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 openpype/plugins/publish/collect_source_for_source.py diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index ea86ab93b4..dcd80fbbdf 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -106,19 +106,3 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): self.log.debug("publishDir: \"{}\"".format(publish_folder)) self.log.debug("resourcesDir: \"{}\"".format(resources_folder)) - - # parse folder name and file name for online and source templates - # currentFile comes from hosts workfiles - # source comes from Publisher - current_file = instance.data.get("currentFile") - source = instance.data.get("source") - source_file = current_file or source - if source_file and os.path.exists(source_file): - self.log.debug("Parsing paths for {}".format(source_file)) - if not instance.data.get("originalBasename"): - instance.data["originalBasename"] = \ - os.path.basename(source_file) - - if not instance.data.get("originalDirname"): - instance.data["originalDirname"] = \ - os.path.dirname(source_file) diff --git a/openpype/plugins/publish/collect_source_for_source.py b/openpype/plugins/publish/collect_source_for_source.py new file mode 100644 index 0000000000..345daa6fe8 --- /dev/null +++ b/openpype/plugins/publish/collect_source_for_source.py @@ -0,0 +1,43 @@ +""" +Requires: + instance -> currentFile + instance -> source + +Provides: + instance -> originalBasename + instance -> originalDirname +""" + +import os +import copy + +import pyblish.api + + +class CollectSourceForSource(pyblish.api.InstancePlugin): + """Collects source location of file for instance. + + Used for 'source' template name which handles in place publishing. + For this kind of publishing files are present with correct file name + pattern and correct location. + """ + + label = "Collect Source" + order = pyblish.api.CollectorOrder + 0.495 + + def process(self, instance): + # parse folder name and file name for online and source templates + # currentFile comes from hosts workfiles + # source comes from Publisher + current_file = instance.data.get("currentFile") + source = instance.data.get("source") + source_file = current_file or source + if source_file and os.path.exists(source_file): + self.log.debug("Parsing paths for {}".format(source_file)) + if not instance.data.get("originalBasename"): + instance.data["originalBasename"] = \ + os.path.basename(source_file) + + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = \ + os.path.dirname(source_file) From 3cf3fd65b1fe61ee339caf5fc69352e3c9ab1851 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 14 Dec 2022 14:42:51 +0100 Subject: [PATCH 60/83] OP-4504 - added validator for source template Check is output template is 'source' if originalDirname is collected and if it is inside of project --- .../plugins/publish/validate_publish_dir.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 openpype/plugins/publish/validate_publish_dir.py diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py new file mode 100644 index 0000000000..03fc47347d --- /dev/null +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -0,0 +1,69 @@ +import pyblish.api +from openpype.pipeline.publish import ValidateContentsOrder +from openpype.pipeline.publish import ( + KnownPublishError, + get_publish_template_name, +) + + +class ValidatePublishDir(pyblish.api.InstancePlugin): + """Validates if 'publishDir' is a project directory + + 'publishDir' is collected based on publish templates. In specific cases + ('source' template) source folder of items is used as a 'publishDir', this + validates if it is inside any project dir for the project. + (eg. files are not published from local folder, unaccessible for studio' + + """ + + order = ValidateContentsOrder + label = "Validate publish dir" + + checked_template_names = ["source"] + # validate instances might have interim family, needs to be mapped to final + family_mapping = { + "renderLayer": "render", + "renderLocal": "render" + } + + def process(self, instance): + + template_name = self._get_template_name_from_instance(instance) + + if template_name not in self.checked_template_names: + return + + original_dirname = instance.data.get("originalDirname") + if not original_dirname: + raise KnownPublishError("Instance meant for in place publishing." + " Its 'originalDirname' must be collected." + " Contact OP developer to modify collector" + ) + + anatomy = instance.context.data["anatomy"] + + success, _ = anatomy.find_root_template_from_path(original_dirname) + self.log.info(_) + if not success: + raise KnownPublishError( + "Path '{}' not in project folder.".format(original_dirname) + + " Please publish from inside of project folder." + ) + + def _get_template_name_from_instance(self, instance): + project_name = instance.context.data["projectName"] + host_name = instance.context.data["hostName"] + anatomy_data = instance.data["anatomyData"] + family = anatomy_data["family"] + family = self.family_mapping.get("family") or family + task_info = anatomy_data.get("task") or {} + + return get_publish_template_name( + project_name, + host_name, + family, + task_name=task_info.get("name"), + task_type=task_info.get("type"), + project_settings=instance.context.data["project_settings"], + logger=self.log + ) From d9a7d5cb802d1aec7ab71db7f576b2dde09c5f15 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 14 Dec 2022 14:46:01 +0100 Subject: [PATCH 61/83] OP-4504 - Hound --- openpype/plugins/publish/collect_source_for_source.py | 1 - openpype/plugins/publish/validate_publish_dir.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/collect_source_for_source.py b/openpype/plugins/publish/collect_source_for_source.py index 345daa6fe8..fd08d28c74 100644 --- a/openpype/plugins/publish/collect_source_for_source.py +++ b/openpype/plugins/publish/collect_source_for_source.py @@ -9,7 +9,6 @@ Provides: """ import os -import copy import pyblish.api diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py index 03fc47347d..eabf4810f3 100644 --- a/openpype/plugins/publish/validate_publish_dir.py +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -35,10 +35,10 @@ class ValidatePublishDir(pyblish.api.InstancePlugin): original_dirname = instance.data.get("originalDirname") if not original_dirname: - raise KnownPublishError("Instance meant for in place publishing." - " Its 'originalDirname' must be collected." - " Contact OP developer to modify collector" - ) + raise KnownPublishError( + "Instance meant for in place publishing." + " Its 'originalDirname' must be collected." + " Contact OP developer to modify collector.") anatomy = instance.context.data["anatomy"] From a3969f8d1a6c901cfa2abb6dd8871527004724cf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 12:51:11 +0100 Subject: [PATCH 62/83] Update openpype/plugins/publish/validate_publish_dir.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- openpype/plugins/publish/validate_publish_dir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py index eabf4810f3..e375fadf49 100644 --- a/openpype/plugins/publish/validate_publish_dir.py +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -45,7 +45,7 @@ class ValidatePublishDir(pyblish.api.InstancePlugin): success, _ = anatomy.find_root_template_from_path(original_dirname) self.log.info(_) if not success: - raise KnownPublishError( + raise PublishValidationError( "Path '{}' not in project folder.".format(original_dirname) + " Please publish from inside of project folder." ) From ee58c0ce48dc7230a18a575e7089aeaf20616eaf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 13:15:14 +0100 Subject: [PATCH 63/83] OP-4504 - changed to XMLPublishError --- .../publish/help/validate_publish_dir.xml | 31 +++++++++++++++++++ .../plugins/publish/validate_publish_dir.py | 21 ++++++++----- 2 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 openpype/plugins/publish/help/validate_publish_dir.xml diff --git a/openpype/plugins/publish/help/validate_publish_dir.xml b/openpype/plugins/publish/help/validate_publish_dir.xml new file mode 100644 index 0000000000..9f62b264bf --- /dev/null +++ b/openpype/plugins/publish/help/validate_publish_dir.xml @@ -0,0 +1,31 @@ + + + +Source directory not collected + +## Source directory not collected + +Instance is marked for in place publishing. Its 'originalDirname' must be collected. Contact OP developer to modify collector. + + + +### __Detailed Info__ (optional) + +In place publishing uses source directory and file name in resulting path and file name of published item. For this instance + all required metadata weren't filled. This is not recoverable error, unless instance itself is removed. + Collector for this instance must be updated for instance to be published. + + + +Source file not in project dir + +## Source file not in project dir + +Path '{original_dirname}' not in project folder. Please publish from inside of project folder. + +### How to repair? + +Restart publish after you moved source file into project directory. + + + \ No newline at end of file diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py index e375fadf49..2f41127548 100644 --- a/openpype/plugins/publish/validate_publish_dir.py +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -1,7 +1,7 @@ import pyblish.api from openpype.pipeline.publish import ValidateContentsOrder from openpype.pipeline.publish import ( - KnownPublishError, + PublishXmlValidationError, get_publish_template_name, ) @@ -35,20 +35,25 @@ class ValidatePublishDir(pyblish.api.InstancePlugin): original_dirname = instance.data.get("originalDirname") if not original_dirname: - raise KnownPublishError( + raise PublishXmlValidationError( + self, "Instance meant for in place publishing." " Its 'originalDirname' must be collected." - " Contact OP developer to modify collector.") + " Contact OP developer to modify collector." + ) anatomy = instance.context.data["anatomy"] success, _ = anatomy.find_root_template_from_path(original_dirname) - self.log.info(_) + + formatting_data = { + "original_dirname": original_dirname, + } + msg = "Path '{}' not in project folder.".format(original_dirname) + \ + " Please publish from inside of project folder." if not success: - raise PublishValidationError( - "Path '{}' not in project folder.".format(original_dirname) + - " Please publish from inside of project folder." - ) + raise PublishXmlValidationError(self, msg, key="not_in_dir", + formatting_data=formatting_data) def _get_template_name_from_instance(self, instance): project_name = instance.context.data["projectName"] From da274a7c8db1df524efd29a8a00cd54d2f30022a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:28:49 +0100 Subject: [PATCH 64/83] OP-4504 - safer comparison of two paths --- openpype/lib/file_transaction.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/openpype/lib/file_transaction.py b/openpype/lib/file_transaction.py index f265b8815c..4ebede0174 100644 --- a/openpype/lib/file_transaction.py +++ b/openpype/lib/file_transaction.py @@ -92,7 +92,9 @@ class FileTransaction(object): def process(self): # Backup any existing files for dst, (src, _) in self._transfers.items(): - if dst == src or not os.path.exists(dst): + self.log.debug("Checking file ... {} -> {}".format(src, dst)) + path_same = self._same_paths(src, dst) + if path_same or not os.path.exists(dst): continue # Backup original file @@ -105,7 +107,8 @@ class FileTransaction(object): # Copy the files to transfer for dst, (src, opts) in self._transfers.items(): - if dst == src: + path_same = self._same_paths(src, dst) + if path_same: self.log.debug( "Source and destionation are same files {} -> {}".format( src, dst)) @@ -182,3 +185,10 @@ class FileTransaction(object): else: self.log.critical("An unexpected error occurred.") six.reraise(*sys.exc_info()) + + def _same_paths(self, src, dst): + # handles same paths but with C:/project vs c:/project + if os.path.exists(src) and os.path.exists(dst): + return os.path.samefile(src, dst) + + return False \ No newline at end of file From b6873a063eaac8f8e3d1463cd0f82681ae3c7f6e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:29:56 +0100 Subject: [PATCH 65/83] OP-4504 - added '_thumb' suffix to thumbnail Without it thumbnail would overwrite source file --- openpype/plugins/publish/extract_thumbnail.py | 2 +- openpype/plugins/publish/extract_thumbnail_from_source.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 14b43beae8..a3c428fe97 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -91,7 +91,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): full_input_path = os.path.join(src_staging, input_file) self.log.info("input {}".format(full_input_path)) filename = os.path.splitext(input_file)[0] - jpeg_file = filename + ".jpg" + jpeg_file = filename + "_thumb.jpg" full_output_path = os.path.join(dst_staging, jpeg_file) if oiio_supported: diff --git a/openpype/plugins/publish/extract_thumbnail_from_source.py b/openpype/plugins/publish/extract_thumbnail_from_source.py index 03df1455e2..a92f762cde 100644 --- a/openpype/plugins/publish/extract_thumbnail_from_source.py +++ b/openpype/plugins/publish/extract_thumbnail_from_source.py @@ -100,7 +100,7 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin): self.log.info("Thumbnail source: {}".format(thumbnail_source)) src_basename = os.path.basename(thumbnail_source) - dst_filename = os.path.splitext(src_basename)[0] + ".jpg" + dst_filename = os.path.splitext(src_basename)[0] + "_thumb.jpg" full_output_path = os.path.join(dst_staging, dst_filename) if oiio_supported: From e3866dff5abd2022d37d90f0da4eb634e35c995e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:37:45 +0100 Subject: [PATCH 66/83] OP-4504 - remove check for existence For sequences source contains `%d` placeholder --- openpype/plugins/publish/collect_source_for_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_source_for_source.py b/openpype/plugins/publish/collect_source_for_source.py index fd08d28c74..aa94238b4f 100644 --- a/openpype/plugins/publish/collect_source_for_source.py +++ b/openpype/plugins/publish/collect_source_for_source.py @@ -31,7 +31,7 @@ class CollectSourceForSource(pyblish.api.InstancePlugin): current_file = instance.data.get("currentFile") source = instance.data.get("source") source_file = current_file or source - if source_file and os.path.exists(source_file): + if source_file: self.log.debug("Parsing paths for {}".format(source_file)) if not instance.data.get("originalBasename"): instance.data["originalBasename"] = \ From 3c09dfc80e003021b930b28288664eedff82002e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:38:19 +0100 Subject: [PATCH 67/83] OP-4504 - remove extension from originalBasename It would produce weird concatenation of extensions. --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 4692cefe4d..94789bb778 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -696,7 +696,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): raise KnownPublishError( "This is a bug. Representation file name is full path" ) - template_data["originalBasename"] = fname + template_data["originalBasename"], _ = os.path.splitext(fname) # Manage anatomy template data template_data.pop("frame", None) if is_udim: From 5d8ddb6e55cca8a3bc7bb058bc87080f329fc156 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 19 Dec 2022 12:15:09 +0100 Subject: [PATCH 68/83] OP-4504 - added explicit check Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/lib/file_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/file_transaction.py b/openpype/lib/file_transaction.py index 4ebede0174..cba361a8d4 100644 --- a/openpype/lib/file_transaction.py +++ b/openpype/lib/file_transaction.py @@ -191,4 +191,4 @@ class FileTransaction(object): if os.path.exists(src) and os.path.exists(dst): return os.path.samefile(src, dst) - return False \ No newline at end of file + return src == dst From b1c834245072e99126a16d9e714dba8903dc5b95 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:13:19 +0100 Subject: [PATCH 69/83] fix access to 'projectName' --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 5da4c76539..b8f5c4eedb 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -243,7 +243,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): return filtered_repres def register(self, instance, file_transactions, filtered_repres): - project_name = instance.context["projectName"] + project_name = instance.context.data["projectName"] instance_stagingdir = instance.data.get("stagingDir") if not instance_stagingdir: From 3d2bfbf5a7447d7894338c12a9b2d41c9c3a363c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 20 Dec 2022 12:08:29 +0100 Subject: [PATCH 70/83] traypublisher: multiple sequences enhancement --- .../plugins/create/create_editorial.py | 73 +++++++++++-------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 28a115629e..205403d33e 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -239,35 +239,38 @@ or updating already created. Publishing will create OTIO file. sequence_path_data = pre_create_data["sequence_filepath_data"] media_path_data = pre_create_data["media_filepaths_data"] - sequence_path = self._get_path_from_file_data(sequence_path_data) + sequence_paths = self._get_path_from_file_data( + sequence_path_data, multi=True) media_path = self._get_path_from_file_data(media_path_data) - # get otio timeline - otio_timeline = self._create_otio_timeline( - sequence_path, fps) + for index, seq_path in enumerate(sequence_paths): + # get otio timeline + otio_timeline = self._create_otio_timeline( + seq_path, fps) - # Create all clip instances - clip_instance_properties.update({ - "fps": fps, - "parent_asset_name": asset_name, - "variant": instance_data["variant"] - }) + # Create all clip instances + clip_instance_properties.update({ + "fps": fps, + "parent_asset_name": asset_name, + "variant": instance_data["variant"] + }) - # create clip instances - self._get_clip_instances( - otio_timeline, - media_path, - clip_instance_properties, - family_presets=allowed_family_presets + # create clip instances + self._get_clip_instances( + otio_timeline, + media_path, + clip_instance_properties, + family_presets=allowed_family_presets - ) + ) - # create otio editorial instance - self._create_otio_instance( - subset_name, instance_data, - sequence_path, media_path, - otio_timeline - ) + # create otio editorial instance + self._create_otio_instance( + subset_name + str(index), + instance_data, + seq_path, media_path, + otio_timeline + ) def _create_otio_instance( self, @@ -320,11 +323,12 @@ or updating already created. Publishing will create OTIO file. self.log.info(f"kwargs: {kwargs}") return otio.adapters.read_from_file(sequence_path, **kwargs) - def _get_path_from_file_data(self, file_path_data): + def _get_path_from_file_data(self, file_path_data, multi=False): """Converting creator path data to single path string Args: file_path_data (FileDefItem): creator path data inputs + multi (bool): switch to multiple files mode Raises: FileExistsError: in case nothing had been set @@ -332,16 +336,23 @@ or updating already created. Publishing will create OTIO file. Returns: str: path string """ - # TODO: just temporarly solving only one media file - if isinstance(file_path_data, list): - file_path_data = file_path_data.pop() + return_path_list = [] - if len(file_path_data["filenames"]) == 0: + self.log.debug(f"type: {type(file_path_data)}") + self.log.debug(f"file_path_data: {file_path_data}") + + if isinstance(file_path_data, list): + return_path_list = [ + os.path.join(f["directory"], f["filenames"][0]) + for f in file_path_data + ] + self.log.debug(f"return_path_list: {return_path_list}") + + if not return_path_list: raise FileExistsError( f"File path was not added: {file_path_data}") - return os.path.join( - file_path_data["directory"], file_path_data["filenames"][0]) + return return_path_list if multi else return_path_list[0] def _get_clip_instances( self, @@ -833,7 +844,7 @@ or updating already created. Publishing will create OTIO file. ".fcpxml" ], allow_sequences=False, - single_item=True, + single_item=False, label="Sequence file", ), FileDef( From 409db0fb831fc7447c8acfeb565ce06d57f5a162 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 11:48:27 +0100 Subject: [PATCH 71/83] use qtpy in resolve instead of Qt.py --- openpype/hosts/resolve/api/menu.py | 2 +- openpype/hosts/resolve/api/plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index 86b292105a..eeb9e65dec 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -1,7 +1,7 @@ import os import sys -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.tools.utils import host_tools diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 0ed7beee59..77e30149fd 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -2,7 +2,7 @@ import re import uuid import qargparse -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.settings import get_current_project_settings from openpype.pipeline.context_tools import get_current_project_asset From c86ebf1e93257ad29647797de57111bb1544fe08 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 23 Dec 2022 10:05:51 +0800 Subject: [PATCH 72/83] only allows the loaded reference to be imported reference --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 8e0257dafb..b77740ae13 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -82,7 +82,7 @@ cmds.file(current_name, open=True, force=True) print(">>> Processing references") all_reference = cmds.file(q=True, reference=True) or [] for ref in all_reference: - if cmds.referenceQuery(ref, f=True): + if cmds.referenceQuery(ref, f=True, il=True): cmds.file(ref, importReference=True) nested_ref = cmds.file(q=True, reference=True) From df62f315cc60f72ff401992b2853b8fc47e1cd31 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 3 Jan 2023 23:45:51 +0800 Subject: [PATCH 73/83] drop out the force flag in referenceQuery --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index b77740ae13..ce284d16fb 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -82,7 +82,7 @@ cmds.file(current_name, open=True, force=True) print(">>> Processing references") all_reference = cmds.file(q=True, reference=True) or [] for ref in all_reference: - if cmds.referenceQuery(ref, f=True, il=True): + if cmds.referenceQuery(ref, il=True): cmds.file(ref, importReference=True) nested_ref = cmds.file(q=True, reference=True) From 0fd51e8a5b5f2068ea2e2970617e4f79fd58b5b0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Jan 2023 14:21:58 +0100 Subject: [PATCH 74/83] traypublisher: adding single file subset name condition --- .../hosts/traypublisher/plugins/create/create_editorial.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 205403d33e..614cf9dbca 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -264,9 +264,14 @@ or updating already created. Publishing will create OTIO file. ) + # alter subset name if multiple files + subset_name_edit = subset_name + if len(sequence_paths) > 1: + subset_name_edit = subset_name + str(index) + # create otio editorial instance self._create_otio_instance( - subset_name + str(index), + subset_name_edit, instance_data, seq_path, media_path, otio_timeline From ddb6ae8a5a38e2a408a701c47bc5484c485dbbd7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 5 Jan 2023 12:25:55 +0800 Subject: [PATCH 75/83] import reference during publish --- .../plugins/publish/extract_import_reference.py | 16 ++++++---------- .../modules/deadline/abstract_submit_deadline.py | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index ce284d16fb..51c82dde92 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -105,17 +105,13 @@ print("*** Done") # can't use TemporaryNamedFile as that can't be opened in another # process until handles are closed by context manager. with tempfile.TemporaryDirectory() as tmp_dir_name: - tmp_file_name = os.path.join(tmp_dir_name, "import_ref.py") - tmp = open(tmp_file_name, "w+t") - subprocess_args = [ - mayapy_exe, - tmp_file_name - ] - self.log.info("Using temp file: {}".format(tmp.name)) - try: + tmp_script_path = os.path.join(tmp_dir_name, "import_ref.py") + self.log.info("Using script file: {}".format(tmp_script_path)) + with open(tmp_script_path, "wt") as tmp: tmp.write(script) - tmp.close() - run_subprocess(subprocess_args) + + try: + run_subprocess([mayapy_exe, tmp_script_path]) except Exception: self.log.error("Import reference failed", exc_info=True) raise diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 909a5871e3..155a647ff6 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -525,9 +525,9 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): # determine published path from Anatomy. template_data = workfile_instance.data.get("anatomyData") if self.import_reference: - rep = workfile_instance.data.get("representations")[1] + rep = workfile_instance.data["representations"][1] else: - rep = workfile_instance.data.get("representations")[0] + rep = workfile_instance.data["representations"][0] template_data["representation"] = rep.get("name") template_data["ext"] = rep.get("ext") template_data["comment"] = None From 4cc66395c5a60c82cd862165c849591a112180f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Jan 2023 06:31:59 +0000 Subject: [PATCH 76/83] Bump json5 from 1.0.1 to 1.0.2 in /website Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- website/yarn.lock | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index 220a489dfa..9af21c7500 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -4740,9 +4740,9 @@ json-schema-traverse@^1.0.0: integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" @@ -5154,16 +5154,11 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0: +minimist@^1.2.0, minimist@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== -minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" From 2cf9b1ee6371d740c178451d1d65c6e7637df03c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 10 Jan 2023 00:28:10 +0800 Subject: [PATCH 77/83] use current file for the scene rendering if the reference is imported --- openpype/modules/deadline/abstract_submit_deadline.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 155a647ff6..f6750bc0f2 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -425,7 +425,11 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): file_path = None if self.use_published: - file_path = self.from_published_scene() + if not self.import_reference: + file_path = self.from_published_scene() + else: + self.log.info("use the scene with imported reference for rendering") # noqa + file_path = context.data["currentFile"] # fallback if nothing was set if not file_path: From 9301bf03fac16278f13eb094bc9116e8916328f4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 10 Jan 2023 00:30:18 +0800 Subject: [PATCH 78/83] use current file for the scene rendering if the reference is imported --- openpype/modules/deadline/abstract_submit_deadline.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index f6750bc0f2..648eb77007 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -528,10 +528,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): # determine published path from Anatomy. template_data = workfile_instance.data.get("anatomyData") - if self.import_reference: - rep = workfile_instance.data["representations"][1] - else: - rep = workfile_instance.data["representations"][0] + rep = workfile_instance.data["representations"][0] template_data["representation"] = rep.get("name") template_data["ext"] = rep.get("ext") template_data["comment"] = None From 59f051d06596f63cbb861938b1a7c26d2d992e05 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 18:30:10 +0100 Subject: [PATCH 79/83] :bug: fix instance collection --- openpype/hosts/unreal/plugins/publish/collect_instances.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/unreal/plugins/publish/collect_instances.py b/openpype/hosts/unreal/plugins/publish/collect_instances.py index 1f25cbde7d..6696eacb6a 100644 --- a/openpype/hosts/unreal/plugins/publish/collect_instances.py +++ b/openpype/hosts/unreal/plugins/publish/collect_instances.py @@ -26,8 +26,8 @@ class CollectInstances(pyblish.api.ContextPlugin): ar = unreal.AssetRegistryHelpers.get_asset_registry() class_name = ["/Script/OpenPype", - "AssetContainer"] if UNREAL_VERSION.major == 5 and \ - UNREAL_VERSION.minor > 0 else "OpenPypePublishInstance" # noqa + "OpenPypePublishInstance"] if UNREAL_VERSION.major == 5 and \ + UNREAL_VERSION.minor > 0 else "OpenPypePublishInstance" # noqa instance_containers = ar.get_assets_by_class(class_name, True) for container_data in instance_containers: From ea65afdbc176e3daf4ffcf1fb95939d39ed1e0b5 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 18:36:14 +0100 Subject: [PATCH 80/83] :rotating_light: hound fix --- .../hosts/unreal/plugins/publish/collect_instances.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/unreal/plugins/publish/collect_instances.py b/openpype/hosts/unreal/plugins/publish/collect_instances.py index 6696eacb6a..27b711cad6 100644 --- a/openpype/hosts/unreal/plugins/publish/collect_instances.py +++ b/openpype/hosts/unreal/plugins/publish/collect_instances.py @@ -25,9 +25,13 @@ class CollectInstances(pyblish.api.ContextPlugin): def process(self, context): ar = unreal.AssetRegistryHelpers.get_asset_registry() - class_name = ["/Script/OpenPype", - "OpenPypePublishInstance"] if UNREAL_VERSION.major == 5 and \ - UNREAL_VERSION.minor > 0 else "OpenPypePublishInstance" # noqa + class_name = [ + "/Script/OpenPype", + "OpenPypePublishInstance" + ] if ( + UNREAL_VERSION.major == 5 + and UNREAL_VERSION.minor > 0 + ) else "OpenPypePublishInstance" # noqa instance_containers = ar.get_assets_by_class(class_name, True) for container_data in instance_containers: From b539ba029990c25318cc98f5776de75eda7ccaf4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 9 Jan 2023 22:16:16 +0100 Subject: [PATCH 81/83] traypublisher: multiple edl workflow with multilayer otio timeline --- openpype/hosts/traypublisher/api/editorial.py | 5 -- .../plugins/create/create_editorial.py | 67 +++++++------------ .../plugins/publish/collect_shot_instances.py | 5 -- 3 files changed, 23 insertions(+), 54 deletions(-) diff --git a/openpype/hosts/traypublisher/api/editorial.py b/openpype/hosts/traypublisher/api/editorial.py index 7c392ef508..293db542a9 100644 --- a/openpype/hosts/traypublisher/api/editorial.py +++ b/openpype/hosts/traypublisher/api/editorial.py @@ -171,7 +171,6 @@ class ShotMetadataSolver: _index == 0 and parents[-1]["entity_name"] == parent_name ): - self.log.debug(f" skipping : {parent_name}") continue # in case first parent is project then start parents from start @@ -179,7 +178,6 @@ class ShotMetadataSolver: _index == 0 and parent_token_type == "Project" ): - self.log.debug("rebuilding parents from scratch") project_parent = parents[0] parents = [project_parent] continue @@ -189,8 +187,6 @@ class ShotMetadataSolver: "entity_name": parent_name }) - self.log.debug(f"__ parents: {parents}") - return parents def _create_hierarchy_path(self, parents): @@ -297,7 +293,6 @@ class ShotMetadataSolver: Returns: (str, dict): shot name and hierarchy data """ - self.log.info(f"_ source_data: {source_data}") tasks = {} asset_doc = source_data["selected_asset_doc"] diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 614cf9dbca..d1086a1ff3 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -1,6 +1,5 @@ import os from copy import deepcopy -from pprint import pformat import opentimelineio as otio from openpype.client import ( get_asset_by_name, @@ -13,9 +12,7 @@ from openpype.hosts.traypublisher.api.plugin import ( from openpype.hosts.traypublisher.api.editorial import ( ShotMetadataSolver ) - from openpype.pipeline import CreatedInstance - from openpype.lib import ( get_ffprobe_data, convert_ffprobe_fps_value, @@ -70,14 +67,12 @@ class EditorialClipInstanceCreatorBase(HiddenTrayPublishCreator): host_name = "traypublisher" def create(self, instance_data, source_data=None): - self.log.info(f"instance_data: {instance_data}") subset_name = instance_data["subset"] # Create new instance new_instance = CreatedInstance( self.family, subset_name, instance_data, self ) - self.log.info(f"instance_data: {pformat(new_instance.data)}") self._store_new_instance(new_instance) @@ -223,8 +218,6 @@ or updating already created. Publishing will create OTIO file. asset_name = instance_data["asset"] asset_doc = get_asset_by_name(self.project_name, asset_name) - self.log.info(pre_create_data["fps"]) - if pre_create_data["fps"] == "from_selection": # get asset doc data attributes fps = asset_doc["data"]["fps"] @@ -243,7 +236,8 @@ or updating already created. Publishing will create OTIO file. sequence_path_data, multi=True) media_path = self._get_path_from_file_data(media_path_data) - for index, seq_path in enumerate(sequence_paths): + first_otio_timeline = None + for seq_path in sequence_paths: # get otio timeline otio_timeline = self._create_otio_timeline( seq_path, fps) @@ -260,22 +254,22 @@ or updating already created. Publishing will create OTIO file. otio_timeline, media_path, clip_instance_properties, - family_presets=allowed_family_presets - + allowed_family_presets, + os.path.basename(seq_path), + first_otio_timeline ) - # alter subset name if multiple files - subset_name_edit = subset_name - if len(sequence_paths) > 1: - subset_name_edit = subset_name + str(index) + if not first_otio_timeline: + # assing otio timeline for multi file to layer + first_otio_timeline = otio_timeline - # create otio editorial instance - self._create_otio_instance( - subset_name_edit, - instance_data, - seq_path, media_path, - otio_timeline - ) + # create otio editorial instance + self._create_otio_instance( + subset_name, + instance_data, + seq_path, media_path, + first_otio_timeline + ) def _create_otio_instance( self, @@ -325,7 +319,6 @@ or updating already created. Publishing will create OTIO file. kwargs["rate"] = fps kwargs["ignore_timecode_mismatch"] = True - self.log.info(f"kwargs: {kwargs}") return otio.adapters.read_from_file(sequence_path, **kwargs) def _get_path_from_file_data(self, file_path_data, multi=False): @@ -343,15 +336,12 @@ or updating already created. Publishing will create OTIO file. """ return_path_list = [] - self.log.debug(f"type: {type(file_path_data)}") - self.log.debug(f"file_path_data: {file_path_data}") if isinstance(file_path_data, list): return_path_list = [ os.path.join(f["directory"], f["filenames"][0]) for f in file_path_data ] - self.log.debug(f"return_path_list: {return_path_list}") if not return_path_list: raise FileExistsError( @@ -364,7 +354,9 @@ or updating already created. Publishing will create OTIO file. otio_timeline, media_path, instance_data, - family_presets + family_presets, + sequence_file_name, + first_otio_timeline=None ): """Helping function fro creating clip instance @@ -384,17 +376,15 @@ or updating already created. Publishing will create OTIO file. media_data = self._get_media_source_metadata(media_path) for track in tracks: - self.log.debug(f"track.name: {track.name}") + track.name = f"{sequence_file_name} - {otio_timeline.name}" try: track_start_frame = ( abs(track.source_range.start_time.value) ) - self.log.debug(f"track_start_frame: {track_start_frame}") track_start_frame -= self.timeline_frame_start except AttributeError: track_start_frame = 0 - self.log.debug(f"track_start_frame: {track_start_frame}") for clip in track.each_child(): if not self._validate_clip_for_processing(clip): @@ -416,10 +406,6 @@ or updating already created. Publishing will create OTIO file. "instance_label": None, "instance_id": None } - self.log.info(( - "Creating subsets from presets: \n" - f"{pformat(family_presets)}" - )) for _fpreset in family_presets: # exclude audio family if no audio stream @@ -435,7 +421,10 @@ or updating already created. Publishing will create OTIO file. deepcopy(base_instance_data), parenting_data ) - self.log.debug(f"{pformat(dict(instance.data))}") + + # add track to first otioTimeline if it is in input args + if first_otio_timeline: + first_otio_timeline.tracks.append(deepcopy(track)) def _restore_otio_source_range(self, otio_clip): """Infusing source range. @@ -476,7 +465,6 @@ or updating already created. Publishing will create OTIO file. target_url=media_path, available_range=available_range ) - otio_clip.media_reference = media_reference def _get_media_source_metadata(self, path): @@ -497,7 +485,6 @@ or updating already created. Publishing will create OTIO file. media_data = get_ffprobe_data( path, self.log ) - self.log.debug(f"__ media_data: {pformat(media_data)}") # get video stream data video_stream = media_data["streams"][0] @@ -605,9 +592,6 @@ or updating already created. Publishing will create OTIO file. # get variant name from preset or from inharitance _variant_name = preset.get("variant") or variant_name - self.log.debug(f"__ family: {family}") - self.log.debug(f"__ preset: {preset}") - # subset name subset_name = "{}{}".format( family, _variant_name.capitalize() @@ -738,17 +722,13 @@ or updating already created. Publishing will create OTIO file. clip_in += track_start_frame clip_out = otio_clip.range_in_parent().end_time_inclusive().value clip_out += track_start_frame - self.log.info(f"clip_in: {clip_in} | clip_out: {clip_out}") # add offset in case there is any - self.log.debug(f"__ timeline_offset: {timeline_offset}") if timeline_offset: clip_in += timeline_offset clip_out += timeline_offset clip_duration = otio_clip.duration().value - self.log.info(f"clip duration: {clip_duration}") - source_in = otio_clip.trimmed_range().start_time.value source_out = source_in + clip_duration @@ -778,7 +758,6 @@ or updating already created. Publishing will create OTIO file. Returns: list: lit of dict with preset items """ - self.log.debug(f"__ pre_create_data: {pre_create_data}") return [ {"family": "shot"}, *[ diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py index 716f73022e..78c1f14e4e 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -33,8 +33,6 @@ class CollectShotInstance(pyblish.api.InstancePlugin): ] def process(self, instance): - self.log.debug(pformat(instance.data)) - creator_identifier = instance.data["creator_identifier"] if "editorial" not in creator_identifier: return @@ -82,7 +80,6 @@ class CollectShotInstance(pyblish.api.InstancePlugin): ] otio_clip = clips.pop() - self.log.debug(f"__ otioclip.parent: {otio_clip.parent}") return otio_clip @@ -172,7 +169,6 @@ class CollectShotInstance(pyblish.api.InstancePlugin): } parents = instance.data.get('parents', []) - self.log.debug(f"parents: {pformat(parents)}") actual = {name: in_info} @@ -190,7 +186,6 @@ class CollectShotInstance(pyblish.api.InstancePlugin): # adding hierarchy context to instance context.data["hierarchyContext"] = final_context - self.log.debug(pformat(final_context)) def _update_dict(self, ex_dict, new_dict): """ Recursion function From fb886125393d93b1216ece77e987823998401afa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 10 Jan 2023 10:44:52 +0100 Subject: [PATCH 82/83] added 'install' method with docstring to 'HostBase' --- openpype/host/host.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openpype/host/host.py b/openpype/host/host.py index 99f7868727..94416bb39a 100644 --- a/openpype/host/host.py +++ b/openpype/host/host.py @@ -76,6 +76,18 @@ class HostBase(object): pass + def install(self): + """Install host specific functionality. + + This is where should be added menu with tools, registered callbacks + and other host integration initialization. + + It is called automatically when 'openpype.pipeline.install_host' is + triggered. + """ + + pass + @property def log(self): if self._log is None: From 4d0f954785ed03012247263805bff0715ec8d536 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 10 Jan 2023 16:29:51 +0100 Subject: [PATCH 83/83] :bug: fix missing maintained_selection call --- openpype/hosts/unreal/api/__init__.py | 2 ++ openpype/hosts/unreal/api/pipeline.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/openpype/hosts/unreal/api/__init__.py b/openpype/hosts/unreal/api/__init__.py index 3f96d8ac6f..ca9db259e6 100644 --- a/openpype/hosts/unreal/api/__init__.py +++ b/openpype/hosts/unreal/api/__init__.py @@ -18,6 +18,7 @@ from .pipeline import ( show_tools_popup, instantiate, UnrealHost, + maintained_selection ) __all__ = [ @@ -36,4 +37,5 @@ __all__ = [ "show_tools_popup", "instantiate", "UnrealHost", + "maintained_selection" ] diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index ca5a42cd82..2081c8fd13 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -2,6 +2,7 @@ import os import logging from typing import List +from contextlib import contextmanager import semver import pyblish.api @@ -447,3 +448,16 @@ def get_subsequences(sequence: unreal.LevelSequence): if subscene_track is not None and subscene_track.get_sections(): return subscene_track.get_sections() return [] + + +@contextmanager +def maintained_selection(): + """Stub to be either implemented or replaced. + + This is needed for old publisher implementation, but + it is not supported (yet) in UE. + """ + try: + yield + finally: + pass