From 1e911e09a10edf8775b7d106b9f1b0f38dced0f1 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 13 Feb 2024 15:01:47 +0200 Subject: [PATCH 001/130] expose RS_outputMultilayerMode in creator tab --- .../plugins/create/create_redshift_rop.py | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py index 8e88c690b9..c234ef8399 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py @@ -14,6 +14,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): family = "redshift_rop" icon = "magic" ext = "exr" + multi_layered_mode = "No Multi-Layered EXR File" # Default to split export and render jobs split_render = True @@ -54,22 +55,33 @@ class CreateRedshiftROP(plugin.HoudiniCreator): # Set the linked rop to the Redshift ROP ipr_rop.parm("linked_rop").set(instance_node.path()) - ext = pre_create_data.get("image_format") - filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( - renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, - fmt="${aov}.$F4.{ext}".format(aov="AOV", ext=ext) - ) + multi_layered_mode = pre_create_data.get("multi_layered_mode") ext_format_index = {"exr": 0, "tif": 1, "jpg": 2, "png": 3} + multilayer_mode_index = {"No Multi-Layered EXR File": "1", + "Full Multi-Layered EXR File": "2" } + + if multilayer_mode_index[multi_layered_mode] == "1": + filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( + renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), + subset_name=subset_name, + fmt="${aov}.$F4.{ext}".format(aov="AOV", ext=ext) + ) + + elif multilayer_mode_index[multi_layered_mode] == "2": + filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format( + renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), + subset_name=subset_name, + ext=ext + ) parms = { # Render frame range "trange": 1, # Redshift ROP settings "RS_outputFileNamePrefix": filepath, - "RS_outputMultilayerMode": "1", # no multi-layered exr + "RS_outputMultilayerMode": multilayer_mode_index[multi_layered_mode], "RS_outputBeautyAOVSuffix": "beauty", "RS_outputFileFormat": ext_format_index[ext], } @@ -110,6 +122,11 @@ class CreateRedshiftROP(plugin.HoudiniCreator): image_format_enum = [ "exr", "tif", "jpg", "png", ] + multi_layered_mode = [ + "No Multi-Layered EXR File", + "Full Multi-Layered EXR File" + ] + return attrs + [ BoolDef("farm", @@ -121,5 +138,9 @@ class CreateRedshiftROP(plugin.HoudiniCreator): EnumDef("image_format", image_format_enum, default=self.ext, - label="Image Format Options") + label="Image Format Options"), + EnumDef("multi_layered_mode", + multi_layered_mode, + default=self.multi_layered_mode, + label="Multi-Layered EXR") ] From 9385a0ffd73cefca338374d0e32ca1411c0e7a17 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 13 Feb 2024 15:03:20 +0200 Subject: [PATCH 002/130] avoid adding custom aov path to representation data if full mode enabled --- .../houdini/plugins/publish/collect_redshift_rop.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py index 26dd942559..c0927601f5 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py @@ -70,6 +70,7 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): beauty_product)} num_aovs = rop.evalParm("RS_aov") + full_exr_mode = (rop.evalParm("RS_outputMultilayerMode") == "2") for index in range(num_aovs): i = index + 1 @@ -82,11 +83,12 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): if not aov_prefix: aov_prefix = default_prefix - aov_product = self.get_render_product_name(aov_prefix, aov_suffix) - render_products.append(aov_product) + if not full_exr_mode: + aov_product = self.get_render_product_name(aov_prefix, aov_suffix) + render_products.append(aov_product) - files_by_aov[aov_suffix] = self.generate_expected_files(instance, - aov_product) # noqa + files_by_aov[aov_suffix] = self.generate_expected_files(instance, + aov_product) # noqa for product in render_products: self.log.debug("Found render product: %s" % product) From 1d85ef2466f48e91ffcdcd5543f231b5f993b26a Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 14 Feb 2024 14:12:59 +0200 Subject: [PATCH 003/130] Add multipart parm --- .../hosts/houdini/plugins/create/create_redshift_rop.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py index c234ef8399..62f7ae4a3c 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py @@ -68,6 +68,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): subset_name=subset_name, fmt="${aov}.$F4.{ext}".format(aov="AOV", ext=ext) ) + multipart = False elif multilayer_mode_index[multi_layered_mode] == "2": filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format( @@ -75,6 +76,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): subset_name=subset_name, ext=ext ) + multipart = True parms = { # Render frame range @@ -82,6 +84,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): # Redshift ROP settings "RS_outputFileNamePrefix": filepath, "RS_outputMultilayerMode": multilayer_mode_index[multi_layered_mode], + "RS_aovMultipart" : multipart, "RS_outputBeautyAOVSuffix": "beauty", "RS_outputFileFormat": ext_format_index[ext], } From a2266e2455aafcfc494f91e7ed6ad10db955f14e Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 14 Feb 2024 14:16:34 +0200 Subject: [PATCH 004/130] Kayla's comment - update parms when ext is EXR --- .../hosts/houdini/plugins/create/create_redshift_rop.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py index 62f7ae4a3c..e02c2066c0 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py @@ -83,11 +83,12 @@ class CreateRedshiftROP(plugin.HoudiniCreator): "trange": 1, # Redshift ROP settings "RS_outputFileNamePrefix": filepath, - "RS_outputMultilayerMode": multilayer_mode_index[multi_layered_mode], - "RS_aovMultipart" : multipart, "RS_outputBeautyAOVSuffix": "beauty", "RS_outputFileFormat": ext_format_index[ext], } + if ext == "exr": + parms["RS_outputMultilayerMode"] = multilayer_mode_index[multi_layered_mode] + parms["RS_aovMultipart"] = multipart if self.selected_nodes: # set up the render camera from the selected node From f32641e8326e3cd6678a331b9f151c90ad0c7575 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 15 Feb 2024 20:19:01 +0200 Subject: [PATCH 005/130] Add $AOV token back --- .../houdini/plugins/create/create_redshift_rop.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py index e02c2066c0..724996faa3 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py @@ -62,20 +62,16 @@ class CreateRedshiftROP(plugin.HoudiniCreator): multilayer_mode_index = {"No Multi-Layered EXR File": "1", "Full Multi-Layered EXR File": "2" } - if multilayer_mode_index[multi_layered_mode] == "1": - filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( + filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), subset_name=subset_name, fmt="${aov}.$F4.{ext}".format(aov="AOV", ext=ext) ) + + if multilayer_mode_index[multi_layered_mode] == "1": multipart = False elif multilayer_mode_index[multi_layered_mode] == "2": - filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format( - renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), - subset_name=subset_name, - ext=ext - ) multipart = True parms = { From 431fa3167898e4d62cdb85cffdf61f53db61b8ca Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 16 Feb 2024 20:51:16 +0800 Subject: [PATCH 006/130] use %AOV% instead of for correct expectedFile name --- .../hosts/houdini/plugins/create/create_redshift_rop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py index 724996faa3..5ecd5d08ed 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py @@ -65,7 +65,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), subset_name=subset_name, - fmt="${aov}.$F4.{ext}".format(aov="AOV", ext=ext) + fmt="%{aov}%.$F4.{ext}".format(aov="AOV", ext=ext) ) if multilayer_mode_index[multi_layered_mode] == "1": From 5981c09ba7cbe8be49ffeb2131a2a5742becb7b4 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 19 Feb 2024 14:23:14 +0200 Subject: [PATCH 007/130] Add Cryptomatte to expected files by default if existed --- .../hosts/houdini/plugins/publish/collect_redshift_rop.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py index c0927601f5..198f0fa960 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py @@ -83,7 +83,9 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): if not aov_prefix: aov_prefix = default_prefix - if not full_exr_mode: + if rop.parm(f"RS_aovID_{i}").evalAsString() == "CRYPTOMATTE" or \ + not full_exr_mode: + aov_product = self.get_render_product_name(aov_prefix, aov_suffix) render_products.append(aov_product) From 0e6e63e4631ab0ccae1535a9f71ae73a0edabf48 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 22 Feb 2024 17:28:54 +0200 Subject: [PATCH 008/130] use $AOV token instead %AOV% --- .../hosts/houdini/plugins/create/create_redshift_rop.py | 2 +- .../hosts/houdini/plugins/publish/collect_redshift_rop.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py index 5ecd5d08ed..a36f530820 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py @@ -65,7 +65,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): filepath = "{renders_dir}{subset_name}/{subset_name}.{fmt}".format( renders_dir=hou.text.expandString("$HIP/pyblish/renders/"), subset_name=subset_name, - fmt="%{aov}%.$F4.{ext}".format(aov="AOV", ext=ext) + fmt="$AOV.$F4.{ext}".format(ext=ext) ) if multilayer_mode_index[multi_layered_mode] == "1": diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py index 198f0fa960..8a704aa47c 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py @@ -118,7 +118,7 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): # When AOV is explicitly defined in prefix we just swap it out # directly with the AOV suffix to embed it. - # Note: ${AOV} seems to be evaluated in the parameter as %AOV% + # Note: '$AOV' seems to be evaluated in the parameter as '%AOV%' has_aov_in_prefix = "%AOV%" in prefix if has_aov_in_prefix: # It seems that when some special separator characters are present From b5108045175ff85523ec4395049b347960549ee8 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 22 Feb 2024 17:33:37 +0200 Subject: [PATCH 009/130] Ignore beauty suffix when full mode is enabled in the RS ROP node --- .../houdini/plugins/publish/collect_redshift_rop.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py index 8a704aa47c..ef4ca2ee85 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py @@ -60,17 +60,22 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): instance.data["ifdFile"] = beauty_export_product instance.data["exportFiles"] = list(export_products) - # Default beauty AOV + full_exr_mode = (rop.evalParm("RS_outputMultilayerMode") == "2") + if full_exr_mode: + # Ignore beauty suffix if full mode is enabled + # As this is what the rop does. + beauty_suffix = "" + + # Default beauty/main layer AOV beauty_product = self.get_render_product_name( prefix=default_prefix, suffix=beauty_suffix ) render_products = [beauty_product] files_by_aov = { - "_": self.generate_expected_files(instance, + beauty_suffix: self.generate_expected_files(instance, beauty_product)} num_aovs = rop.evalParm("RS_aov") - full_exr_mode = (rop.evalParm("RS_outputMultilayerMode") == "2") for index in range(num_aovs): i = index + 1 From bb3fa9ceeda426d2696be8eb6e0aacdae4baf7b8 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 23 Feb 2024 16:48:18 +0200 Subject: [PATCH 010/130] fix code style --- .../hosts/houdini/plugins/publish/collect_redshift_rop.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py index ef4ca2ee85..dab4b07e2f 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py @@ -73,7 +73,8 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): render_products = [beauty_product] files_by_aov = { beauty_suffix: self.generate_expected_files(instance, - beauty_product)} + beauty_product) + } num_aovs = rop.evalParm("RS_aov") for index in range(num_aovs): From 81073b4cafab3dc337ed597b05a696d1975a893b Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 15 Mar 2024 09:55:45 +0000 Subject: [PATCH 011/130] If knob name exists only change value. --- client/ayon_core/hosts/nuke/api/lib.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index e304b33dc7..32e9ace377 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -397,7 +397,13 @@ def imprint(node, data, tab=None): """ for knob in create_knobs(data, tab): - node.addKnob(knob) + # If knob name exists we set the value. Technically there could be + # multiple knobs with the same name, but the intent is not to have + # duplicated knobs so we do not account for that. + if knob.name() in node.knobs().keys(): + node[knob.name()].setValue(knob.value()) + else: + node.addKnob(knob) @deprecated From 70f89e234bd03423863a7d7ebaa25dda4ba4ff28 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 15:06:38 +0100 Subject: [PATCH 012/130] renamed key 'custom_attributes' to 'attributes' --- .../traypublisher/plugins/publish/collect_shot_instances.py | 2 +- client/ayon_core/plugins/publish/collect_hierarchy.py | 2 +- client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py index edcbb27cb3..f1e5e83f0a 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -155,7 +155,7 @@ class CollectShotInstance(pyblish.api.InstancePlugin): in_info = { "entity_type": "Shot", - "custom_attributes": { + "attributes": { "handleStart": handle_start, "handleEnd": handle_end, "frameStart": instance.data["frameStart"], diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 7387b1865b..d7f0777ba4 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -46,7 +46,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): shot_data['tasks'] = instance.data.get("tasks") or {} shot_data["comments"] = instance.data.get("comments", []) - shot_data['custom_attributes'] = { + shot_data['attributes'] = { "handleStart": instance.data["handleStart"], "handleEnd": instance.data["handleEnd"], "frameStart": instance.data["frameStart"], diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index 59a15af299..50b2bf7c0b 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -241,7 +241,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): project_item["name"] = key project_item["tasks"] = [] project_item["attributes"] = project_item.pop( - "custom_attributes", {} + "attributes", {} ) project_item["children"] = [] @@ -280,7 +280,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): task_info["name"] = task_name task_items.append(task_info) new_item["tasks"] = task_items - new_item["attributes"] = new_item.pop("custom_attributes", {}) + new_item["attributes"] = new_item.pop("attributes", {}) items_by_id[item_id] = new_item parent_id_by_item_id[item_id] = parent_id From e22eff05b7884f10af133115952c13bf5923e519 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 15:09:01 +0100 Subject: [PATCH 013/130] renamed 'childs' key to 'children' --- .../plugins/publish/collect_shot_instances.py | 2 +- .../plugins/publish/collect_anatomy_instance_data.py | 8 ++++++-- client/ayon_core/plugins/publish/collect_hierarchy.py | 7 +++---- .../plugins/publish/extract_hierarchy_to_ayon.py | 6 +++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py index f1e5e83f0a..e3f8bfebba 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -177,7 +177,7 @@ class CollectShotInstance(pyblish.api.InstancePlugin): next_dict = { parent_name: { "entity_type": parent["entity_type"], - "childs": actual + "children": actual } } actual = next_dict diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index e3b27a0db5..f8cc81e718 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -465,7 +465,11 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): current_data = hierarchy_context.get(project_name, {}) for key in folder_path.split("/"): if key: - current_data = current_data.get("childs", {}).get(key, {}) + current_data = ( + current_data + .get("children", {}) + .get(key, {}) + ) tasks_info = current_data.get("tasks", {}) task_info = tasks_info.get(task_name, {}) @@ -529,5 +533,5 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): return item[folder_name].get("tasks") or {} for subitem in item.values(): - hierarchy_queue.extend(subitem.get("childs") or []) + hierarchy_queue.extend(subitem.get("children") or []) return {} diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index d7f0777ba4..c74378bbd3 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -68,7 +68,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): next_dict[parent_name] = {} next_dict[parent_name]["entity_type"] = parent[ "entity_type"].capitalize() - next_dict[parent_name]["childs"] = actual + next_dict[parent_name]["children"] = actual actual = next_dict temp_context = self._update_dict(temp_context, actual) @@ -77,7 +77,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): if not temp_context: return - final_context[project_name]['childs'] = temp_context + final_context[project_name]['children'] = temp_context # adding hierarchy context to context context.data["hierarchyContext"] = final_context @@ -85,8 +85,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): context.data["hierarchyContext"])) def _update_dict(self, parent_dict, child_dict): - """ - Nesting each children into its parent. + """Nesting each child into its parent. Args: parent_dict (dict): parent dict wich should be nested with children diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index 50b2bf7c0b..d43dcab28c 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -237,7 +237,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): hierarchy_context = copy.deepcopy(context.data["hierarchyContext"]) for key, value in hierarchy_context.items(): project_item = copy.deepcopy(value) - project_children_context = project_item.pop("childs", None) + project_children_context = project_item.pop("children", None) project_item["name"] = key project_item["tasks"] = [] project_item["attributes"] = project_item.pop( @@ -265,7 +265,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): folder_path = "{}/{}".format(parent_path, folder_name) if ( folder_path not in active_folder_paths - and not folder_info.get("childs") + and not folder_info.get("children") ): continue @@ -273,7 +273,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): new_item = copy.deepcopy(folder_info) new_item["name"] = folder_name new_item["children"] = [] - new_children_context = new_item.pop("childs", None) + new_children_context = new_item.pop("children", None) tasks = new_item.pop("tasks", {}) task_items = [] for task_name, task_info in tasks.items(): From a29809a8138e91d0a7640854a8b4fac9b2941b96 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 15:11:31 +0100 Subject: [PATCH 014/130] add 'folder_type' to data next to 'entity_type' --- .../plugins/publish/collect_shot_instances.py | 15 ++++++++------- .../plugins/publish/collect_hierarchy.py | 6 ++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py index e3f8bfebba..5a2f5cbc20 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -154,7 +154,8 @@ class CollectShotInstance(pyblish.api.InstancePlugin): handle_end = int(instance.data["handleEnd"]) in_info = { - "entity_type": "Shot", + "entity_type": "folder", + "folder_type": "Shot", "attributes": { "handleStart": handle_start, "handleEnd": handle_end, @@ -174,13 +175,13 @@ class CollectShotInstance(pyblish.api.InstancePlugin): for parent in reversed(parents): parent_name = parent["entity_name"] - next_dict = { - parent_name: { - "entity_type": parent["entity_type"], - "children": actual - } + parent_info = { + "entity_type": parent["entity_type"], + "children": actual, } - actual = next_dict + if parent_info["entity_type"] == "folder": + parent_info["folder_type"] = parent["folder_type"] + actual = {parent_name: parent_info} final_context = self._update_dict(final_context, actual) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index c74378bbd3..a16ca27e5c 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -41,8 +41,9 @@ class CollectHierarchy(pyblish.api.ContextPlugin): if not instance.data.get("heroTrack"): continue + shot_data["entity_type"] = "folder" # suppose that all instances are Shots - shot_data['entity_type'] = 'Shot' + shot_data["folder_type"] = "Shot" shot_data['tasks'] = instance.data.get("tasks") or {} shot_data["comments"] = instance.data.get("comments", []) @@ -66,7 +67,8 @@ class CollectHierarchy(pyblish.api.ContextPlugin): next_dict = {} parent_name = parent["entity_name"] next_dict[parent_name] = {} - next_dict[parent_name]["entity_type"] = parent[ + next_dict[parent_name]["entity_type"] = "folder" + next_dict[parent_name]["folder_type"] = parent[ "entity_type"].capitalize() next_dict[parent_name]["children"] = actual actual = next_dict From a0dc2d7bffdab00925dde7dbbdaedb244731cb8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 15:11:58 +0100 Subject: [PATCH 015/130] refactored how data structure in collect hierarchy is created --- .../plugins/publish/collect_hierarchy.py | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index a16ca27e5c..0751bf305b 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -17,17 +17,19 @@ class CollectHierarchy(pyblish.api.ContextPlugin): hosts = ["resolve", "hiero", "flame"] def process(self, context): - temp_context = {} project_name = context.data["projectName"] - final_context = {} - final_context[project_name] = {} - final_context[project_name]["entity_type"] = "project" + temp_context = {} + final_context = { + project_name: { + "entity_type": "project", + "children": temp_context + }, + } for instance in context: self.log.debug("Processing instance: `{}` ...".format(instance)) # shot data dict - shot_data = {} product_type = instance.data["productType"] families = instance.data["families"] @@ -41,23 +43,25 @@ class CollectHierarchy(pyblish.api.ContextPlugin): if not instance.data.get("heroTrack"): continue - shot_data["entity_type"] = "folder" - # suppose that all instances are Shots - shot_data["folder_type"] = "Shot" - shot_data['tasks'] = instance.data.get("tasks") or {} - shot_data["comments"] = instance.data.get("comments", []) - - shot_data['attributes'] = { - "handleStart": instance.data["handleStart"], - "handleEnd": instance.data["handleEnd"], - "frameStart": instance.data["frameStart"], - "frameEnd": instance.data["frameEnd"], - "clipIn": instance.data["clipIn"], - "clipOut": instance.data["clipOut"], - "fps": instance.data["fps"], - "resolutionWidth": instance.data["resolutionWidth"], - "resolutionHeight": instance.data["resolutionHeight"], - "pixelAspect": instance.data["pixelAspect"] + shot_data = { + "entity_type": "folder", + # WARNING Default folder type is hardcoded + # suppose that all instances are Shots + "folder_type": "Shot", + "tasks": instance.data.get("tasks") or {}, + "comments": instance.data.get("comments", []), + "attributes": { + "handleStart": instance.data["handleStart"], + "handleEnd": instance.data["handleEnd"], + "frameStart": instance.data["frameStart"], + "frameEnd": instance.data["frameEnd"], + "clipIn": instance.data["clipIn"], + "clipOut": instance.data["clipOut"], + "fps": instance.data["fps"], + "resolutionWidth": instance.data["resolutionWidth"], + "resolutionHeight": instance.data["resolutionHeight"], + "pixelAspect": instance.data["pixelAspect"], + }, } # Split by '/' for AYON where asset is a path name = instance.data["folderPath"].split("/")[-1] @@ -71,6 +75,16 @@ class CollectHierarchy(pyblish.api.ContextPlugin): next_dict[parent_name]["folder_type"] = parent[ "entity_type"].capitalize() next_dict[parent_name]["children"] = actual + + for parent in reversed(instance.data["parents"]): + parent_name = parent["entity_name"] + next_dict = { + parent_name: { + "entity_type": "folder", + "folder_type": parent["entity_type"].capitalize(), + "children": actual + } + } actual = next_dict temp_context = self._update_dict(temp_context, actual) @@ -79,8 +93,6 @@ class CollectHierarchy(pyblish.api.ContextPlugin): if not temp_context: return - final_context[project_name]['children'] = temp_context - # adding hierarchy context to context context.data["hierarchyContext"] = final_context self.log.debug("context.data[hierarchyContext] is: {}".format( From 52e9993b4a58b77b1b5951095910ab30ef56b0ff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 16:03:37 +0100 Subject: [PATCH 016/130] change create and publish logic in hosts to set 'folder_type' correctly --- client/ayon_core/hosts/flame/api/plugin.py | 10 +++++----- .../plugins/publish/collect_timeline_instances.py | 6 ++++++ client/ayon_core/hosts/hiero/api/plugin.py | 10 +++++----- .../hiero/plugins/publish/precollect_instances.py | 5 +++++ client/ayon_core/hosts/resolve/api/plugin.py | 6 +++--- .../resolve/plugins/publish/precollect_instances.py | 5 +++++ client/ayon_core/hosts/traypublisher/api/editorial.py | 6 ++++-- 7 files changed, 33 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/hosts/flame/api/plugin.py b/client/ayon_core/hosts/flame/api/plugin.py index c5667eb75a..20edba2d6b 100644 --- a/client/ayon_core/hosts/flame/api/plugin.py +++ b/client/ayon_core/hosts/flame/api/plugin.py @@ -644,13 +644,13 @@ class PublishableClip: "families": [self.base_product_type, self.product_type] } - def _convert_to_entity(self, type, template): + def _convert_to_entity(self, src_type, template): """ Converting input key to key with type. """ # convert to entity type - entity_type = self.types.get(type, None) + folder_type = self.types.get(src_type, None) - assert entity_type, "Missing entity type for `{}`".format( - type + assert folder_type, "Missing folder type for `{}`".format( + src_type ) # first collect formatting data to use for formatting template @@ -661,7 +661,7 @@ class PublishableClip: formatting_data[_k] = value return { - "entity_type": entity_type, + "folder_type": folder_type, "entity_name": template.format( **formatting_data ) diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py index 9d6560023c..47632ecf3d 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -100,6 +100,12 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): marker_data["handleEnd"] = min( marker_data["handleEnd"], tail) + # Backward compatibility fix of 'entity_type' > 'folder_type' + if "parents" in marker_data: + for parent in marker_data["parents"]: + if "entity_type" in parent: + parent["folder_type"] = parent.pop("entity_type") + workfile_start = self._set_workfile_start(marker_data) with_audio = bool(marker_data.pop("audio")) diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index 6a665dc9c5..721ade09b2 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -909,13 +909,13 @@ class PublishClip: "families": [self.product_type, self.data["family"]] } - def _convert_to_entity(self, type, template): + def _convert_to_entity(self, src_type, template): """ Converting input key to key with type. """ # convert to entity type - entity_type = self.types.get(type, None) + folder_type = self.types.get(src_type, None) - assert entity_type, "Missing entity type for `{}`".format( - type + assert folder_type, "Missing folder type for `{}`".format( + src_type ) # first collect formatting data to use for formatting template @@ -926,7 +926,7 @@ class PublishClip: formatting_data[_k] = value return { - "entity_type": entity_type, + "folder_type": folder_type, "entity_name": template.format( **formatting_data ) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index d921f37934..2d3689f001 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -84,6 +84,11 @@ class PrecollectInstances(pyblish.api.ContextPlugin): k: v for k, v in tag_data.items() if k not in ("id", "applieswhole", "label") }) + # Backward compatibility fix of 'entity_type' > 'folder_type' + if "parents" in data: + for parent in data["parents"]: + if "entity_type" in parent: + parent["folder_type"] = parent.pop("entity_type") asset, asset_name = self._get_folder_data(tag_data) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index 8c97df98b8..e3e2715bad 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -873,14 +873,14 @@ class PublishClip: def _convert_to_entity(self, key): """ Converting input key to key with type. """ # convert to entity type - entity_type = self.types.get(key) + folder_type = self.types.get(key) - assert entity_type, "Missing entity type for `{}`".format( + assert folder_type, "Missing folder type for `{}`".format( key ) return { - "entity_type": entity_type, + "folder_type": folder_type, "entity_name": self.hierarchy_data[key]["value"].format( **self.timeline_item_default_data ) diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py index 72ecd3669d..caa79c85c0 100644 --- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py @@ -64,6 +64,11 @@ class PrecollectInstances(pyblish.api.ContextPlugin): }) folder_path = tag_data["folder_path"] + # Backward compatibility fix of 'entity_type' > 'folder_type' + if "parents" in data: + for parent in data["parents"]: + if "entity_type" in parent: + parent["folder_type"] = parent.pop("entity_type") # TODO: remove backward compatibility product_name = tag_data.get("productName") diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/client/ayon_core/hosts/traypublisher/api/editorial.py index 8dedec7398..92a7b315f8 100644 --- a/client/ayon_core/hosts/traypublisher/api/editorial.py +++ b/client/ayon_core/hosts/traypublisher/api/editorial.py @@ -193,7 +193,8 @@ class ShotMetadataSolver: continue parents.append({ - "entity_type": parent_token_type, + "entity_type": "folder", + "folder_type": parent_token_type, "entity_name": parent_name }) @@ -264,7 +265,8 @@ class ShotMetadataSolver: }] for entity in folders_hierarchy: output.append({ - "entity_type": entity["folderType"], + "entity_type": "folder", + "folder_type": entity["folderType"], "entity_name": entity["name"] }) return output From b1f8a1c9b4649507b211715b44cad27cd1e7937f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 20 Mar 2024 00:19:41 +0100 Subject: [PATCH 017/130] Fix argument name --- .../ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py index eaa7ff1ae3..004b85f420 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py @@ -64,7 +64,7 @@ class LoadVDBtoArnold(load.LoaderPlugin): path = self.filepath_from_context(context) self._set_path(grid_node, path=path, - representation=context["representation"]) + repre_entity=context["representation"]) # Lock the shape node so the user can't delete the transform/shape # as if it was referenced @@ -94,7 +94,7 @@ class LoadVDBtoArnold(load.LoaderPlugin): assert len(grid_nodes) == 1, "This is a bug" # Update the VRayVolumeGrid - self._set_path(grid_nodes[0], path=path, representation=repre_entity) + self._set_path(grid_nodes[0], path=path, repre_entity=repre_entity) # Update container representation cmds.setAttr(container["objectName"] + ".representation", From 87a0c7555c823c29cc54af9cd8b747dc9b92e6a1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 20 Mar 2024 00:35:41 +0100 Subject: [PATCH 018/130] Fix indentations --- .../plugins/publish/collect_karma_rop.py | 28 +++--- .../plugins/publish/collect_mantra_rop.py | 92 +++++++++---------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_karma_rop.py index 85100bc2c6..78651b0c69 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_karma_rop.py @@ -41,23 +41,23 @@ class CollectKarmaROPRenderProducts(pyblish.api.InstancePlugin): instance.data["chunkSize"] = chunk_size self.log.debug("Chunk Size: %s" % chunk_size) - default_prefix = evalParmNoFrame(rop, "picture") - render_products = [] + default_prefix = evalParmNoFrame(rop, "picture") + render_products = [] - # Default beauty AOV - beauty_product = self.get_render_product_name( - prefix=default_prefix, suffix=None - ) - render_products.append(beauty_product) + # Default beauty AOV + beauty_product = self.get_render_product_name( + prefix=default_prefix, suffix=None + ) + render_products.append(beauty_product) - files_by_aov = { - "beauty": self.generate_expected_files(instance, - beauty_product) - } + files_by_aov = { + "beauty": self.generate_expected_files(instance, + beauty_product) + } - filenames = list(render_products) - instance.data["files"] = filenames - instance.data["renderProducts"] = colorspace.ARenderProduct() + filenames = list(render_products) + instance.data["files"] = filenames + instance.data["renderProducts"] = colorspace.ARenderProduct() for product in render_products: self.log.debug("Found render product: %s" % product) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_mantra_rop.py index d46476c2ce..df9acc4b61 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_mantra_rop.py @@ -41,57 +41,57 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): instance.data["chunkSize"] = chunk_size self.log.debug("Chunk Size: %s" % chunk_size) - default_prefix = evalParmNoFrame(rop, "vm_picture") - render_products = [] + default_prefix = evalParmNoFrame(rop, "vm_picture") + render_products = [] - # Store whether we are splitting the render job (export + render) - split_render = bool(rop.parm("soho_outputmode").eval()) - instance.data["splitRender"] = split_render - export_prefix = None - export_products = [] - if split_render: - export_prefix = evalParmNoFrame( - rop, "soho_diskfile", pad_character="0" - ) - beauty_export_product = self.get_render_product_name( - prefix=export_prefix, - suffix=None) - export_products.append(beauty_export_product) - self.log.debug( - "Found export product: {}".format(beauty_export_product) - ) - instance.data["ifdFile"] = beauty_export_product - instance.data["exportFiles"] = list(export_products) - - # Default beauty AOV - beauty_product = self.get_render_product_name( - prefix=default_prefix, suffix=None + # Store whether we are splitting the render job (export + render) + split_render = bool(rop.parm("soho_outputmode").eval()) + instance.data["splitRender"] = split_render + export_prefix = None + export_products = [] + if split_render: + export_prefix = evalParmNoFrame( + rop, "soho_diskfile", pad_character="0" ) - render_products.append(beauty_product) + beauty_export_product = self.get_render_product_name( + prefix=export_prefix, + suffix=None) + export_products.append(beauty_export_product) + self.log.debug( + "Found export product: {}".format(beauty_export_product) + ) + instance.data["ifdFile"] = beauty_export_product + instance.data["exportFiles"] = list(export_products) - files_by_aov = { - "beauty": self.generate_expected_files(instance, - beauty_product) - } + # Default beauty AOV + beauty_product = self.get_render_product_name( + prefix=default_prefix, suffix=None + ) + render_products.append(beauty_product) - aov_numbers = rop.evalParm("vm_numaux") - if aov_numbers > 0: - # get the filenames of the AOVs - for i in range(1, aov_numbers + 1): - var = rop.evalParm("vm_variable_plane%d" % i) - if var: - aov_name = "vm_filename_plane%d" % i - aov_boolean = "vm_usefile_plane%d" % i - aov_enabled = rop.evalParm(aov_boolean) - has_aov_path = rop.evalParm(aov_name) - if has_aov_path and aov_enabled == 1: - aov_prefix = evalParmNoFrame(rop, aov_name) - aov_product = self.get_render_product_name( - prefix=aov_prefix, suffix=None - ) - render_products.append(aov_product) + files_by_aov = { + "beauty": self.generate_expected_files(instance, + beauty_product) + } - files_by_aov[var] = self.generate_expected_files(instance, aov_product) # noqa + aov_numbers = rop.evalParm("vm_numaux") + if aov_numbers > 0: + # get the filenames of the AOVs + for i in range(1, aov_numbers + 1): + var = rop.evalParm("vm_variable_plane%d" % i) + if var: + aov_name = "vm_filename_plane%d" % i + aov_boolean = "vm_usefile_plane%d" % i + aov_enabled = rop.evalParm(aov_boolean) + has_aov_path = rop.evalParm(aov_name) + if has_aov_path and aov_enabled == 1: + aov_prefix = evalParmNoFrame(rop, aov_name) + aov_product = self.get_render_product_name( + prefix=aov_prefix, suffix=None + ) + render_products.append(aov_product) + + files_by_aov[var] = self.generate_expected_files(instance, aov_product) # noqa for product in render_products: self.log.debug("Found render product: %s" % product) From 5496484aab7c538cadc3be013c1408bf9c730444 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 10:23:10 +0100 Subject: [PATCH 019/130] Fix settings title (typo) + add description --- server_addon/maya/server/settings/publishers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 3a6de2eb44..fe5c10e93c 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -322,7 +322,9 @@ class ExtractCameraAlembicModel(BaseSettingsModel): optional: bool = SettingsField(title="Optional") active: bool = SettingsField(title="Active") bake_attributes: str = SettingsField( - "[]", title="Base Attributes", widget="textarea" + "[]", title="Bake Attributes", widget="textarea", + description="List of attributes that will be included in the alembic " + "export.", ) @validator("bake_attributes") From 404681e684892e63e4d26915364c24ed56f23c7d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 10:32:08 +0100 Subject: [PATCH 020/130] Add list of attributes to always be included in alembic export even when not specified in publisher UI by user. This matches the Extract Camera Alembic logic for `bake_attributes` --- .../hosts/maya/plugins/publish/extract_pointcache.py | 9 +++++++++ server_addon/maya/server/settings/publishers.py | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py index f2187063fc..f83d2679cb 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -1,4 +1,5 @@ import os +import json from maya import cmds @@ -25,6 +26,7 @@ class ExtractAlembic(publish.Extractor): hosts = ["maya"] families = ["pointcache", "model", "vrayproxy.alembic"] targets = ["local", "remote"] + bake_attributes = "[]" def process(self, instance): if instance.data.get("farm"): @@ -42,6 +44,13 @@ class ExtractAlembic(publish.Extractor): attrs += instance.data.get("userDefinedAttributes", []) attrs += ["cbId"] + # bake specified attributes in preset + bake_attributes = json.loads(self.bake_attributes) + assert isinstance(bake_attributes, list), ( + "Attributes to bake must be specified as a list" + ) + attrs += bake_attributes + attr_prefixes = instance.data.get("attrPrefix", "").split(";") attr_prefixes = [value for value in attr_prefixes if value.strip()] diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 3a6de2eb44..ca99d8c57f 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -299,6 +299,11 @@ class ExtractAlembicModel(BaseSettingsModel): families: list[str] = SettingsField( default_factory=list, title="Families") + bake_attributes: str = SettingsField( + "[]", title="Bake Attributes", widget="textarea", + description="List of attributes that will be included in the alembic " + "export.", + ) class ExtractObjModel(BaseSettingsModel): @@ -1193,7 +1198,8 @@ DEFAULT_PUBLISH_SETTINGS = { "pointcache", "model", "vrayproxy.alembic" - ] + ], + "bake_attributes": "[]" }, "ExtractObj": { "enabled": False, From 3d9cd57df01aacde07e3424a9d9c0af8a5f834ec Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 10:37:31 +0100 Subject: [PATCH 021/130] Expose bake attribute prefixes, plus make settings just a list of strings --- .../maya/plugins/publish/extract_pointcache.py | 15 ++++++--------- server_addon/maya/server/settings/publishers.py | 12 +++++++++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py index f83d2679cb..5de72f7674 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -1,5 +1,4 @@ import os -import json from maya import cmds @@ -26,7 +25,10 @@ class ExtractAlembic(publish.Extractor): hosts = ["maya"] families = ["pointcache", "model", "vrayproxy.alembic"] targets = ["local", "remote"] - bake_attributes = "[]" + + # From settings + bake_attributes = [] + bake_attribute_prefixes = [] def process(self, instance): if instance.data.get("farm"): @@ -42,17 +44,12 @@ class ExtractAlembic(publish.Extractor): attrs = instance.data.get("attr", "").split(";") attrs = [value for value in attrs if value.strip()] attrs += instance.data.get("userDefinedAttributes", []) + attrs += self.bake_attributes attrs += ["cbId"] - # bake specified attributes in preset - bake_attributes = json.loads(self.bake_attributes) - assert isinstance(bake_attributes, list), ( - "Attributes to bake must be specified as a list" - ) - attrs += bake_attributes - attr_prefixes = instance.data.get("attrPrefix", "").split(";") attr_prefixes = [value for value in attr_prefixes if value.strip()] + attr_prefixes += self.bake_attribute_prefixes self.log.debug("Extracting pointcache..") dirname = self.staging_dir(instance) diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index ca99d8c57f..7ba2522e60 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -299,10 +299,15 @@ class ExtractAlembicModel(BaseSettingsModel): families: list[str] = SettingsField( default_factory=list, title="Families") - bake_attributes: str = SettingsField( - "[]", title="Bake Attributes", widget="textarea", + bake_attributes: list[str] = SettingsField( + "", title="Bake Attributes", widget="textarea", description="List of attributes that will be included in the alembic " "export.", + ), + bake_attribute_prefixes: list[str] = SettingsField( + "", title="Bake Attribute Prefixes", widget="textarea", + description="List of attribute prefixes for attributes that will be " + "included in the alembic export.", ) @@ -1199,7 +1204,8 @@ DEFAULT_PUBLISH_SETTINGS = { "model", "vrayproxy.alembic" ], - "bake_attributes": "[]" + "bake_attributes": [], + "bake_attribute_prefixes": [], }, "ExtractObj": { "enabled": False, From fadf720c9a9b33299a057527183f58d9d89027a6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 10:38:20 +0100 Subject: [PATCH 022/130] Bump version --- server_addon/maya/server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/server/version.py b/server_addon/maya/server/version.py index 8202425a2d..a86a3ce0a1 100644 --- a/server_addon/maya/server/version.py +++ b/server_addon/maya/server/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring addon version.""" -__version__ = "0.1.9" +__version__ = "0.1.10" From 9a5d6427d5741186b4767107ae495bf36241ed09 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 11:22:53 +0100 Subject: [PATCH 023/130] Fix settings --- server_addon/maya/server/settings/publishers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 7ba2522e60..7c2688af55 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -300,12 +300,12 @@ class ExtractAlembicModel(BaseSettingsModel): default_factory=list, title="Families") bake_attributes: list[str] = SettingsField( - "", title="Bake Attributes", widget="textarea", + default_factory=list, title="Bake Attributes", description="List of attributes that will be included in the alembic " "export.", - ), + ) bake_attribute_prefixes: list[str] = SettingsField( - "", title="Bake Attribute Prefixes", widget="textarea", + default_factory=list, title="Bake Attribute Prefixes", description="List of attribute prefixes for attributes that will be " "included in the alembic export.", ) @@ -1205,7 +1205,7 @@ DEFAULT_PUBLISH_SETTINGS = { "vrayproxy.alembic" ], "bake_attributes": [], - "bake_attribute_prefixes": [], + "bake_attribute_prefixes": [] }, "ExtractObj": { "enabled": False, From 7f0098f92fae0d29c4808e17a00ed9f2233fa65c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 11:48:51 +0100 Subject: [PATCH 024/130] Houdini: Add generic filepath loader --- .../houdini/plugins/load/load_filepath.py | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 client/ayon_core/hosts/houdini/plugins/load/load_filepath.py diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_filepath.py b/client/ayon_core/hosts/houdini/plugins/load/load_filepath.py new file mode 100644 index 0000000000..515ffa6027 --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/load/load_filepath.py @@ -0,0 +1,129 @@ +import os +import re + +from ayon_core.pipeline import load +from openpype.hosts.houdini.api import pipeline + +import hou + + +class FilePathLoader(load.LoaderPlugin): + """Load a managed filepath to a null node. + + This is useful if for a particular workflow there is no existing loader + yet. A Houdini artists can load as the generic filepath loader and then + reference the relevant Houdini parm to use the exact value. The benefit + is that this filepath will be managed and can be updated as usual. + + """ + + label = "Load filepath to node" + order = 9 + icon = "link" + color = "white" + product_types = {"*"} + representations = ["*"] + + def load(self, context, name=None, namespace=None, data=None): + + # Get the root node + obj = hou.node("/obj") + + # Define node name + namespace = namespace if namespace else context["folder"]["name"] + node_name = "{}_{}".format(namespace, name) if namespace else name + + # Create a null node + container = obj.createNode("null", node_name=node_name) + + # Destroy any children + for node in container.children(): + node.destroy() + + # Add filepath attribute, set value as default value + filepath = self.format_path( + path=self.filepath_from_context(context), + representation=context["representation"] + ) + parm_template_group = container.parmTemplateGroup() + attr_folder = hou.FolderParmTemplate("attributes_folder", "Attributes") + parm = hou.StringParmTemplate(name="filepath", + label="Filepath", + num_components=1, + default_value=(filepath,)) + attr_folder.addParmTemplate(parm) + parm_template_group.append(attr_folder) + + # Hide some default labels + for folder_label in ["Transform", "Render", "Misc", "Redshift OBJ"]: + folder = parm_template_group.findFolder(folder_label) + if not folder: + continue + parm_template_group.hideFolder(folder_label, True) + + container.setParmTemplateGroup(parm_template_group) + + container.setDisplayFlag(False) + container.setSelectableInViewport(False) + container.useXray(False) + + nodes = [container] + + self[:] = nodes + + return pipeline.containerise( + node_name, + namespace, + nodes, + context, + self.__class__.__name__, + suffix="", + ) + + def update(self, container, context): + + # Update the file path + representation_entity = context["representation"] + file_path = self.format_path( + path=self.filepath_from_context(context), + representation=representation_entity + ) + + node = container["node"] + node.setParms({ + "filepath": file_path, + "representation": str(representation_entity["id"]) + }) + + # Update the parameter default value (cosmetics) + parm_template_group = node.parmTemplateGroup() + parm = parm_template_group.find("filepath") + parm.setDefaultValue((file_path,)) + parm_template_group.replace(parm_template_group.find("filepath"), + parm) + node.setParmTemplateGroup(parm_template_group) + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + + node = container["node"] + node.destroy() + + @staticmethod + def format_path(path: str, representation: dict) -> str: + """Format file path for sequence with $F.""" + if not os.path.exists(path): + raise RuntimeError("Path does not exist: %s" % path) + + # The path is either a single file or sequence in a folder. + frame = representation["context"].get("frame") + if frame is not None: + # Substitute frame number in sequence with $F with padding + ext = representation.get("ext", representation["name"]) + token = "$F{}".format(len(frame)) # e.g. $F4 + pattern = r"\.(\d+)\.{ext}$".format(ext=re.escape(ext)) + path = re.sub(pattern, ".{}.{}".format(token, ext), path) + + return os.path.normpath(path).replace("\\", "/") From 3888ce8486bc4910557e8eab33484a227beb0206 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 11:52:38 +0100 Subject: [PATCH 025/130] Houdini: Implement `switch` method for Camera Loader --- client/ayon_core/hosts/houdini/plugins/load/load_camera.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py index 605e5724e6..c57b14043a 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py @@ -167,6 +167,9 @@ class CameraLoader(load.LoaderPlugin): temp_camera.destroy() + def switch(self, container, context): + self.update(container, context) + def remove(self, container): node = container["node"] From 4f3391eb8a7d99aa97ea527b176fad2299d0704d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 11:52:58 +0100 Subject: [PATCH 026/130] Remove redundant commented print --- client/ayon_core/hosts/houdini/plugins/load/load_camera.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py index c57b14043a..7cb4542d0c 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_camera.py @@ -198,7 +198,6 @@ class CameraLoader(load.LoaderPlugin): def _match_maya_render_mask(self, camera): """Workaround to match Maya render mask in Houdini""" - # print("Setting match maya render mask ") parm = camera.parm("aperture") expression = parm.expression() expression = expression.replace("return ", "aperture = ") From c147553e8aab3691ccf8ff6933428832269a69d3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 12:15:08 +0100 Subject: [PATCH 027/130] Fusion: Add support for "custom frame range" per saver --- .../fusion/plugins/create/create_saver.py | 72 ++++++++++++++++++- .../plugins/publish/collect_instances.py | 8 +++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py index b6cda1f302..2abec597da 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py @@ -1,6 +1,11 @@ -from ayon_core.lib import EnumDef +from ayon_core.lib import ( + UILabelDef, + NumberDef, + EnumDef +) from ayon_core.hosts.fusion.api.plugin import GenericCreateSaver +from ayon_core.hosts.fusion.api.lib import get_current_comp class CreateSaver(GenericCreateSaver): @@ -44,6 +49,7 @@ class CreateSaver(GenericCreateSaver): self._get_render_target_enum(), self._get_reviewable_bool(), self._get_frame_range_enum(), + self._get_custom_frame_range_attribute_defs(), self._get_image_format_enum(), ] return attr_defs @@ -53,6 +59,7 @@ class CreateSaver(GenericCreateSaver): "current_folder": "Current Folder context", "render_range": "From render in/out", "comp_range": "From composition timeline", + "custom_range": "Custom frame range", } return EnumDef( @@ -61,3 +68,66 @@ class CreateSaver(GenericCreateSaver): label="Frame range source", default=self.default_frame_range_option ) + + @staticmethod + def _get_custom_frame_range_attribute_defs() -> list: + + # Define custom frame range defaults based on current comp + # timeline settings (if a comp is currently open) + comp = get_current_comp() + if comp is not None: + attrs = comp.GetAttrs() + frame_defaults = { + "frameStart": int(attrs["COMPN_GlobalStart"]), + "frameEnd": int(attrs["COMPN_GlobalEnd"]), + "handleStart": int( + attrs["COMPN_RenderStart"] - attrs["COMPN_GlobalStart"] + ), + "handleEnd": int( + attrs["COMPN_GlobalEnd"] - attrs["COMPN_RenderEnd"] + ), + } + else: + frame_defaults = { + "frameStart": 1001, + "frameEnd": 1100, + "handleStart": 0, + "handleEnd": 0 + } + + return [ + UILabelDef( + label="
Custom Frame Range" + ), + UILabelDef( + label="only used with 'Custom frame range' source" + ), + NumberDef( + "custom_frameStart", + label="Frame Start", + default=frame_defaults["frameStart"], + minimum=0, + decimals=0 + ), + NumberDef( + "custom_frameEnd", + label="Frame End", + default=frame_defaults["frameEnd"], + minimum=0, + decimals=0 + ), + NumberDef( + "custom_handleStart", + label="Handle Start", + default=frame_defaults["handleStart"], + minimum=0, + decimals=0 + ), + NumberDef( + "custom_handleEnd", + label="Handle End", + default=frame_defaults["handleEnd"], + minimum=0, + decimals=0 + ) + ] diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py index 51d7e68fb6..921c282877 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py @@ -57,6 +57,14 @@ class CollectInstanceData(pyblish.api.InstancePlugin): start_with_handle = comp_start end_with_handle = comp_end + if frame_range_source == "custom_range": + start = int(instance.data["custom_frameStart"]) + end = int(instance.data["custom_frameEnd"]) + handle_start = int(instance.data["custom_handleStart"]) + handle_end = int(instance.data["custom_handleEnd"]) + start_with_handle = start - handle_start + end_with_handle = end + handle_end + frame = instance.data["creator_attributes"].get("frame") # explicitly publishing only single frame if frame is not None: From f84a9115562b333616bf42c793b64636f4cf134c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 24 Mar 2024 12:21:00 +0100 Subject: [PATCH 028/130] Fix attribute definitions list + add tooltips --- .../fusion/plugins/create/create_saver.py | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py index 2abec597da..20c7b99851 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py @@ -49,8 +49,8 @@ class CreateSaver(GenericCreateSaver): self._get_render_target_enum(), self._get_reviewable_bool(), self._get_frame_range_enum(), - self._get_custom_frame_range_attribute_defs(), self._get_image_format_enum(), + *self._get_custom_frame_range_attribute_defs() ] return attr_defs @@ -97,37 +97,53 @@ class CreateSaver(GenericCreateSaver): return [ UILabelDef( - label="
Custom Frame Range" - ), - UILabelDef( - label="only used with 'Custom frame range' source" + label="
Custom Frame Range
" + "only used with 'Custom frame range' source" ), NumberDef( "custom_frameStart", label="Frame Start", default=frame_defaults["frameStart"], minimum=0, - decimals=0 + decimals=0, + tooltip=( + "Set the start frame for the export.\n" + "Only used if frame range source is 'Custom frame range'." + ) ), NumberDef( "custom_frameEnd", label="Frame End", default=frame_defaults["frameEnd"], minimum=0, - decimals=0 + decimals=0, + tooltip=( + "Set the end frame for the export.\n" + "Only used if frame range source is 'Custom frame range'." + ) ), NumberDef( "custom_handleStart", label="Handle Start", default=frame_defaults["handleStart"], minimum=0, - decimals=0 + decimals=0, + tooltip=( + "Set the start handles for the export, this will be " + "added before the start frame.\n" + "Only used if frame range source is 'Custom frame range'." + ) ), NumberDef( "custom_handleEnd", label="Handle End", default=frame_defaults["handleEnd"], minimum=0, - decimals=0 + decimals=0, + tooltip=( + "Set the end handles for the export, this will be added " + "after the end frame.\n" + "Only used if frame range source is 'Custom frame range'." + ) ) ] From e4c8cf2f7e5745a7d703f391c3b1537b90322c36 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 13:04:15 +0100 Subject: [PATCH 029/130] Resolve: Allow to minimize the menu --- client/ayon_core/hosts/resolve/api/menu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/resolve/api/menu.py b/client/ayon_core/hosts/resolve/api/menu.py index 59eba14d83..3324295615 100644 --- a/client/ayon_core/hosts/resolve/api/menu.py +++ b/client/ayon_core/hosts/resolve/api/menu.py @@ -48,6 +48,7 @@ class OpenPypeMenu(QtWidgets.QWidget): QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint + | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowStaysOnTopHint ) From d2396140106182cc4b8de4a175fa2f6fc2d9cd43 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 13:27:26 +0100 Subject: [PATCH 030/130] Refactor `OpenPype` to `AYON` --- client/ayon_core/hosts/resolve/api/menu.py | 10 +++++----- client/ayon_core/hosts/resolve/api/menu_style.qss | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/menu.py b/client/ayon_core/hosts/resolve/api/menu.py index 3324295615..7f3a669ecb 100644 --- a/client/ayon_core/hosts/resolve/api/menu.py +++ b/client/ayon_core/hosts/resolve/api/menu.py @@ -38,9 +38,9 @@ class Spacer(QtWidgets.QWidget): self.setLayout(layout) -class OpenPypeMenu(QtWidgets.QWidget): +class AYONMenu(QtWidgets.QWidget): def __init__(self, *args, **kwargs): - super(OpenPypeMenu, self).__init__(*args, **kwargs) + super(AYONMenu, self).__init__(*args, **kwargs) self.setObjectName(f"{MENU_LABEL}Menu") @@ -174,11 +174,11 @@ class OpenPypeMenu(QtWidgets.QWidget): def launch_pype_menu(): app = QtWidgets.QApplication(sys.argv) - pype_menu = OpenPypeMenu() + ayon_menu = AYONMenu() stylesheet = load_stylesheet() - pype_menu.setStyleSheet(stylesheet) + ayon_menu.setStyleSheet(stylesheet) - pype_menu.show() + ayon_menu.show() sys.exit(app.exec_()) diff --git a/client/ayon_core/hosts/resolve/api/menu_style.qss b/client/ayon_core/hosts/resolve/api/menu_style.qss index 3d51c7139f..ad8932d881 100644 --- a/client/ayon_core/hosts/resolve/api/menu_style.qss +++ b/client/ayon_core/hosts/resolve/api/menu_style.qss @@ -51,7 +51,7 @@ QLineEdit { qproperty-alignment: AlignCenter; } -#OpenPypeMenu { +#AYONMenu { qproperty-alignment: AlignLeft; min-width: 10em; border: 1px solid #fef9ef; From 44ed77a9f655c03ed585596b08eafc2e6dd2a402 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 14:20:14 +0100 Subject: [PATCH 031/130] Houdini: Implement USD loader to SOPs via `usdimport` --- .../houdini/plugins/load/load_usd_sop.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py new file mode 100644 index 0000000000..b32210e399 --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py @@ -0,0 +1,91 @@ +import os + +from ayon_core.pipeline import load +from ayon_core.hosts.houdini.api import pipeline + + +class SopUsdImportLoader(load.LoaderPlugin): + """Load USD to SOPs via `usdimport`""" + + label = "Load USD to SOPs" + product_types = {"*"} + representations = ["usd"] + order = -6 + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + import hou + + # Format file name, Houdini only wants forward slashes + file_path = self.filepath_from_context(context) + file_path = os.path.normpath(file_path) + file_path = file_path.replace("\\", "/") + + # Get the root node + obj = hou.node("/obj") + + # Define node name + namespace = namespace if namespace else context["folder"]["name"] + node_name = "{}_{}".format(namespace, name) if namespace else name + + # Create a new geo node + container = obj.createNode("geo", node_name=node_name) + + # Remove the file node, it only loads static meshes + # Houdini 17 has removed the file node from the geo node + file_node = container.node("file1") + if file_node: + file_node.destroy() + + # Create a usdimport node + usdimport = container.createNode("usdimport", node_name=node_name) + usdimport.setParms({"filepath1": file_path}) + + # Ensure display flag is on the first input node and not on the OUT + # node to optimize "debug" displaying in the viewport. + usdimport.setDisplayFlag(True) + + # Set new position for unpack node else it gets cluttered + nodes = [container, usdimport] + for nr, node in enumerate(nodes): + node.setPosition([0, (0 - nr)]) + + self[:] = nodes + + return pipeline.containerise( + node_name, + namespace, + nodes, + context, + self.__class__.__name__, + suffix="", + ) + + def update(self, container, context): + + node = container["node"] + try: + usdimport_node = next( + n for n in node.children() if n.type().name() == "usdimport" + ) + except StopIteration: + self.log.error("Could not find node of type `usdimport`") + return + + # Update the file path + file_path = self.filepath_from_context(context) + file_path = file_path.replace("\\", "/") + + usdimport_node.setParms({"filepath1": file_path}) + + # Update attribute + node.setParms({"representation": context["representation"]["id"]}) + + def remove(self, container): + + node = container["node"] + node.destroy() + + def switch(self, container, representation): + self.update(container, representation) From 24d87bf92a8be56fe837013860c2bb944f7520f6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 14:22:27 +0100 Subject: [PATCH 032/130] Remove redundant logic --- .../hosts/houdini/plugins/load/load_usd_sop.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py index b32210e399..3607987e15 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py @@ -32,24 +32,12 @@ class SopUsdImportLoader(load.LoaderPlugin): # Create a new geo node container = obj.createNode("geo", node_name=node_name) - # Remove the file node, it only loads static meshes - # Houdini 17 has removed the file node from the geo node - file_node = container.node("file1") - if file_node: - file_node.destroy() - # Create a usdimport node usdimport = container.createNode("usdimport", node_name=node_name) usdimport.setParms({"filepath1": file_path}) - # Ensure display flag is on the first input node and not on the OUT - # node to optimize "debug" displaying in the viewport. - usdimport.setDisplayFlag(True) - # Set new position for unpack node else it gets cluttered nodes = [container, usdimport] - for nr, node in enumerate(nodes): - node.setPosition([0, (0 - nr)]) self[:] = nodes From 135dd15870feb230231ce888949f5a4c56ce8749 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 14:26:18 +0100 Subject: [PATCH 033/130] Remove redundant assignment to `self[:]` --- client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py b/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py index 3607987e15..5b7e022e73 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py @@ -39,8 +39,6 @@ class SopUsdImportLoader(load.LoaderPlugin): # Set new position for unpack node else it gets cluttered nodes = [container, usdimport] - self[:] = nodes - return pipeline.containerise( node_name, namespace, From 2b1e31001c74a6dd263236e0f7a5c0600c372249 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:52:11 +0100 Subject: [PATCH 034/130] duplicated non python host launch script for photoshop --- .../hosts/photoshop/api/launch_script.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 client/ayon_core/hosts/photoshop/api/launch_script.py diff --git a/client/ayon_core/hosts/photoshop/api/launch_script.py b/client/ayon_core/hosts/photoshop/api/launch_script.py new file mode 100644 index 0000000000..c036b63c46 --- /dev/null +++ b/client/ayon_core/hosts/photoshop/api/launch_script.py @@ -0,0 +1,93 @@ +"""Script wraps launch mechanism of Photoshop implementations. + +Arguments passed to the script are passed to launch function in host +implementation. In all cases requires host app executable and may contain +workfile or others. +""" + +import os +import sys + +from ayon_core.hosts.photoshop.api.lib import main + +# Get current file to locate start point of sys.argv +CURRENT_FILE = os.path.abspath(__file__) + + +def show_error_messagebox(title, message, detail_message=None): + """Function will show message and process ends after closing it.""" + from qtpy import QtWidgets, QtCore + from ayon_core import style + + app = QtWidgets.QApplication([]) + app.setStyleSheet(style.load_stylesheet()) + + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle(title) + msgbox.setText(message) + + if detail_message: + msgbox.setDetailedText(detail_message) + + msgbox.setWindowModality(QtCore.Qt.ApplicationModal) + msgbox.show() + + sys.exit(app.exec_()) + + +def on_invalid_args(script_not_found): + """Show to user message box saying that something went wrong. + + Tell user that arguments to launch implementation are invalid with + arguments details. + + Args: + script_not_found (bool): Use different message based on this value. + """ + + title = "Invalid arguments" + joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) + if script_not_found: + submsg = "Where couldn't find script path:\n\"{}\"" + else: + submsg = "Expected Host executable after script path:\n\"{}\"" + + message = "BUG: Got invalid arguments so can't launch Host application." + detail_message = "Process was launched with arguments:\n{}\n\n{}".format( + joined_args, + submsg.format(CURRENT_FILE) + ) + + show_error_messagebox(title, message, detail_message) + + +def main(argv): + # Modify current file path to find match in sys.argv which may be different + # on windows (different letter cases and slashes). + modified_current_file = CURRENT_FILE.replace("\\", "/").lower() + + # Create a copy of sys argv + sys_args = list(argv) + after_script_idx = None + # Find script path in sys.argv to know index of argv where host + # executable should be. + for idx, item in enumerate(sys_args): + if item.replace("\\", "/").lower() == modified_current_file: + after_script_idx = idx + 1 + break + + # Validate that there is at least one argument after script path + launch_args = None + if after_script_idx is not None: + launch_args = sys_args[after_script_idx:] + + if launch_args: + # Launch host implementation + main(*launch_args) + else: + # Show message box + on_invalid_args(after_script_idx is None) + + +if __name__ == "__main__": + main(sys.argv) From fee6a9b4485de958af128c9db0f7da8f4dbfbfa6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:52:52 +0100 Subject: [PATCH 035/130] renamed 'PHOTOSHOP_HOST_DIR' to 'PHOTOSHOP_ADDON_ROOT' --- client/ayon_core/hosts/photoshop/__init__.py | 4 ++-- client/ayon_core/hosts/photoshop/addon.py | 2 +- client/ayon_core/hosts/photoshop/api/pipeline.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/__init__.py b/client/ayon_core/hosts/photoshop/__init__.py index 773f73d624..0c79afaab8 100644 --- a/client/ayon_core/hosts/photoshop/__init__.py +++ b/client/ayon_core/hosts/photoshop/__init__.py @@ -1,10 +1,10 @@ from .addon import ( + PHOTOSHOP_ADDON_ROOT, PhotoshopAddon, - PHOTOSHOP_HOST_DIR, ) __all__ = ( + "PHOTOSHOP_ADDON_ROOT", "PhotoshopAddon", - "PHOTOSHOP_HOST_DIR", ) diff --git a/client/ayon_core/hosts/photoshop/addon.py b/client/ayon_core/hosts/photoshop/addon.py index 3016912960..28833121c4 100644 --- a/client/ayon_core/hosts/photoshop/addon.py +++ b/client/ayon_core/hosts/photoshop/addon.py @@ -1,7 +1,7 @@ import os from ayon_core.addon import AYONAddon, IHostAddon -PHOTOSHOP_HOST_DIR = os.path.dirname(os.path.abspath(__file__)) +PHOTOSHOP_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__)) class PhotoshopAddon(AYONAddon, IHostAddon): diff --git a/client/ayon_core/hosts/photoshop/api/pipeline.py b/client/ayon_core/hosts/photoshop/api/pipeline.py index 32f66cf7fb..27cfa5a7b5 100644 --- a/client/ayon_core/hosts/photoshop/api/pipeline.py +++ b/client/ayon_core/hosts/photoshop/api/pipeline.py @@ -21,14 +21,14 @@ from ayon_core.host import ( ) from ayon_core.pipeline.load import any_outdated_containers -from ayon_core.hosts.photoshop import PHOTOSHOP_HOST_DIR +from ayon_core.hosts.photoshop import PHOTOSHOP_ADDON_ROOT from ayon_core.tools.utils import get_ayon_qt_app from . import lib log = Logger.get_logger(__name__) -PLUGINS_DIR = os.path.join(PHOTOSHOP_HOST_DIR, "plugins") +PLUGINS_DIR = os.path.join(PHOTOSHOP_ADDON_ROOT, "plugins") PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") LOAD_PATH = os.path.join(PLUGINS_DIR, "load") CREATE_PATH = os.path.join(PLUGINS_DIR, "create") From aaea1e14859799dd8c443c832dce2ffa75be8e3f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:53:17 +0100 Subject: [PATCH 036/130] photoshop has own prelaunch hook to modify launch arguments --- .../hooks/pre_non_python_host_launch.py | 2 +- client/ayon_core/hosts/photoshop/__init__.py | 2 + client/ayon_core/hosts/photoshop/addon.py | 14 +++ .../hosts/photoshop/hooks/pre_launch_args.py | 95 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py diff --git a/client/ayon_core/hooks/pre_non_python_host_launch.py b/client/ayon_core/hooks/pre_non_python_host_launch.py index fed4c99447..3d3c24b308 100644 --- a/client/ayon_core/hooks/pre_non_python_host_launch.py +++ b/client/ayon_core/hooks/pre_non_python_host_launch.py @@ -17,7 +17,7 @@ class NonPythonHostHook(PreLaunchHook): python script which launch the host. For these cases it is necessary to prepend python (or ayon) executable and script path before application's. """ - app_groups = {"harmony", "photoshop", "aftereffects"} + app_groups = {"harmony", "aftereffects"} order = 20 launch_types = {LaunchTypes.local} diff --git a/client/ayon_core/hosts/photoshop/__init__.py b/client/ayon_core/hosts/photoshop/__init__.py index 0c79afaab8..cf21b7df75 100644 --- a/client/ayon_core/hosts/photoshop/__init__.py +++ b/client/ayon_core/hosts/photoshop/__init__.py @@ -1,10 +1,12 @@ from .addon import ( PHOTOSHOP_ADDON_ROOT, PhotoshopAddon, + get_launch_script_path, ) __all__ = ( "PHOTOSHOP_ADDON_ROOT", "PhotoshopAddon", + "get_launch_script_path", ) diff --git a/client/ayon_core/hosts/photoshop/addon.py b/client/ayon_core/hosts/photoshop/addon.py index 28833121c4..65fe6a7cd1 100644 --- a/client/ayon_core/hosts/photoshop/addon.py +++ b/client/ayon_core/hosts/photoshop/addon.py @@ -20,3 +20,17 @@ class PhotoshopAddon(AYONAddon, IHostAddon): def get_workfile_extensions(self): return [".psd", ".psb"] + + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(PHOTOSHOP_ADDON_ROOT, "hooks") + ] + + +def get_launch_script_path(): + return os.path.join( + PHOTOSHOP_ADDON_ROOT, "api", "launch_script.py" + ) + diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py new file mode 100644 index 0000000000..0689a6c2f0 --- /dev/null +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -0,0 +1,95 @@ +import os +import platform +import subprocess + +from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib.applications import ( + PreLaunchHook, + LaunchTypes, +) +from ayon_core.hosts.photoshop import get_launch_script_path + + +def get_launch_kwargs(kwargs): + """Explicit setting of kwargs for Popen for Photoshop. + + Expected behavior + - ayon_console opens window with logs + - ayon has stdout/stderr available for capturing + + Args: + kwargs (Union[dict, None]): Current kwargs or None. + + """ + if kwargs is None: + kwargs = {} + + if platform.system().lower() != "windows": + return kwargs + + executable_path = os.environ.get("AYON_EXECUTABLE") + + executable_filename = "" + if executable_path: + executable_filename = os.path.basename(executable_path) + + is_gui_executable = "ayon_console" not in executable_filename + if is_gui_executable: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + else: + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + return kwargs + + +class PhotoshopPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and script path to Photoshop implementation + before Photoshop executable and add last workfile path to launch arguments. + + Existence of last workfile is checked. If workfile does not exists tries + to copy templated workfile from predefined path. + """ + app_groups = {"photoshop"} + + order = 20 + launch_types = {LaunchTypes.local} + + def execute(self): + # Pop executable + executable_path = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + script_path = get_launch_script_path() + + new_launch_args = get_ayon_launcher_args( + "run", script_path, executable_path + ) + # Add workfile path if exists + workfile_path = self.data["last_workfile_path"] + if ( + self.data.get("start_last_workfile") + and workfile_path + and os.path.exists(workfile_path) + ): + new_launch_args.append(workfile_path) + + # Append as whole list as these arguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.launch_context.launch_args.extend(remainders) + + self.launch_context.kwargs = get_launch_kwargs( + self.launch_context.kwargs + ) From 0d75102d8a255237b5d8fa180e13d0febe250c31 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:53:54 +0100 Subject: [PATCH 037/130] aftereffects have own launch script --- .../hosts/aftereffects/api/launch_script.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 client/ayon_core/hosts/aftereffects/api/launch_script.py diff --git a/client/ayon_core/hosts/aftereffects/api/launch_script.py b/client/ayon_core/hosts/aftereffects/api/launch_script.py new file mode 100644 index 0000000000..ad4e779bd0 --- /dev/null +++ b/client/ayon_core/hosts/aftereffects/api/launch_script.py @@ -0,0 +1,93 @@ +"""Script wraps launch mechanism of AfterEffects implementations. + +Arguments passed to the script are passed to launch function in host +implementation. In all cases requires host app executable and may contain +workfile or others. +""" + +import os +import sys + +from ayon_core.hosts.aftereffects.api.launch_logic import main + +# Get current file to locate start point of sys.argv +CURRENT_FILE = os.path.abspath(__file__) + + +def show_error_messagebox(title, message, detail_message=None): + """Function will show message and process ends after closing it.""" + from qtpy import QtWidgets, QtCore + from ayon_core import style + + app = QtWidgets.QApplication([]) + app.setStyleSheet(style.load_stylesheet()) + + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle(title) + msgbox.setText(message) + + if detail_message: + msgbox.setDetailedText(detail_message) + + msgbox.setWindowModality(QtCore.Qt.ApplicationModal) + msgbox.show() + + sys.exit(app.exec_()) + + +def on_invalid_args(script_not_found): + """Show to user message box saying that something went wrong. + + Tell user that arguments to launch implementation are invalid with + arguments details. + + Args: + script_not_found (bool): Use different message based on this value. + """ + + title = "Invalid arguments" + joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) + if script_not_found: + submsg = "Where couldn't find script path:\n\"{}\"" + else: + submsg = "Expected Host executable after script path:\n\"{}\"" + + message = "BUG: Got invalid arguments so can't launch Host application." + detail_message = "Process was launched with arguments:\n{}\n\n{}".format( + joined_args, + submsg.format(CURRENT_FILE) + ) + + show_error_messagebox(title, message, detail_message) + + +def main(argv): + # Modify current file path to find match in sys.argv which may be different + # on windows (different letter cases and slashes). + modified_current_file = CURRENT_FILE.replace("\\", "/").lower() + + # Create a copy of sys argv + sys_args = list(argv) + after_script_idx = None + # Find script path in sys.argv to know index of argv where host + # executable should be. + for idx, item in enumerate(sys_args): + if item.replace("\\", "/").lower() == modified_current_file: + after_script_idx = idx + 1 + break + + # Validate that there is at least one argument after script path + launch_args = None + if after_script_idx is not None: + launch_args = sys_args[after_script_idx:] + + if launch_args: + # Launch host implementation + main(*launch_args) + else: + # Show message box + on_invalid_args(after_script_idx is None) + + +if __name__ == "__main__": + main(sys.argv) From 720de823cf973dab3d75bede890cf49dc7fb7efc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:54:36 +0100 Subject: [PATCH 038/130] aftereffects has own prelaunch hook to modify launch arguments --- .../hooks/pre_non_python_host_launch.py | 2 +- .../ayon_core/hosts/aftereffects/__init__.py | 8 +- client/ayon_core/hosts/aftereffects/addon.py | 17 ++++ .../hosts/aftereffects/api/launch_logic.py | 1 - .../aftereffects/hooks/pre_launch_args.py | 95 +++++++++++++++++++ 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py diff --git a/client/ayon_core/hooks/pre_non_python_host_launch.py b/client/ayon_core/hooks/pre_non_python_host_launch.py index 3d3c24b308..a65e0ecba4 100644 --- a/client/ayon_core/hooks/pre_non_python_host_launch.py +++ b/client/ayon_core/hooks/pre_non_python_host_launch.py @@ -17,7 +17,7 @@ class NonPythonHostHook(PreLaunchHook): python script which launch the host. For these cases it is necessary to prepend python (or ayon) executable and script path before application's. """ - app_groups = {"harmony", "aftereffects"} + app_groups = {"harmony"} order = 20 launch_types = {LaunchTypes.local} diff --git a/client/ayon_core/hosts/aftereffects/__init__.py b/client/ayon_core/hosts/aftereffects/__init__.py index ae750d05b6..02ab287629 100644 --- a/client/ayon_core/hosts/aftereffects/__init__.py +++ b/client/ayon_core/hosts/aftereffects/__init__.py @@ -1,6 +1,12 @@ -from .addon import AfterEffectsAddon +from .addon import ( + AFTEREFFECTS_ADDON_ROOT, + AfterEffectsAddon, + get_launch_script_path, +) __all__ = ( + "AFTEREFFECTS_ADDON_ROOT", "AfterEffectsAddon", + "get_launch_script_path", ) diff --git a/client/ayon_core/hosts/aftereffects/addon.py b/client/ayon_core/hosts/aftereffects/addon.py index 46d0818247..fc54043c1d 100644 --- a/client/ayon_core/hosts/aftereffects/addon.py +++ b/client/ayon_core/hosts/aftereffects/addon.py @@ -1,5 +1,9 @@ +import os + from ayon_core.addon import AYONAddon, IHostAddon +AFTEREFFECTS_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__)) + class AfterEffectsAddon(AYONAddon, IHostAddon): name = "aftereffects" @@ -17,3 +21,16 @@ class AfterEffectsAddon(AYONAddon, IHostAddon): def get_workfile_extensions(self): return [".aep"] + + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(AFTEREFFECTS_ADDON_ROOT, "hooks") + ] + + +def get_launch_script_path(): + return os.path.join( + AFTEREFFECTS_ADDON_ROOT, "api", "launch_script.py" + ) diff --git a/client/ayon_core/hosts/aftereffects/api/launch_logic.py b/client/ayon_core/hosts/aftereffects/api/launch_logic.py index d0e4e8beae..5a23f2cb35 100644 --- a/client/ayon_core/hosts/aftereffects/api/launch_logic.py +++ b/client/ayon_core/hosts/aftereffects/api/launch_logic.py @@ -7,7 +7,6 @@ import asyncio import functools import traceback - from wsrpc_aiohttp import ( WebSocketRoute, WebSocketAsync diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py new file mode 100644 index 0000000000..2c5baa3b68 --- /dev/null +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -0,0 +1,95 @@ +import os +import platform +import subprocess + +from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib.applications import ( + PreLaunchHook, + LaunchTypes, +) +from ayon_core.hosts.aftereffects import get_launch_script_path + + +def get_launch_kwargs(kwargs): + """Explicit setting of kwargs for Popen for AfterEffects. + + Expected behavior + - ayon_console opens window with logs + - ayon has stdout/stderr available for capturing + + Args: + kwargs (Union[dict, None]): Current kwargs or None. + + """ + if kwargs is None: + kwargs = {} + + if platform.system().lower() != "windows": + return kwargs + + executable_path = os.environ.get("AYON_EXECUTABLE") + + executable_filename = "" + if executable_path: + executable_filename = os.path.basename(executable_path) + + is_in_ui_launcher = "ayon_console" not in executable_filename + if is_in_ui_launcher: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + else: + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + return kwargs + + +class AEPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and script path to AE implementation before + AE executable and add last workfile path to launch arguments. + + Existence of last workfile is checked. If workfile does not exists tries + to copy templated workfile from predefined path. + """ + app_groups = {"aftereffects"} + + order = 20 + launch_types = {LaunchTypes.local} + + def execute(self): + # Pop executable + executable_path = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + script_path = get_launch_script_path() + + new_launch_args = get_ayon_launcher_args( + "run", script_path, executable_path + ) + # Add workfile path if exists + workfile_path = self.data["last_workfile_path"] + if ( + self.data.get("start_last_workfile") + and workfile_path + and os.path.exists(workfile_path) + ): + new_launch_args.append(workfile_path) + + # Append as whole list as these arguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.launch_context.launch_args.extend(remainders) + + self.launch_context.kwargs = get_launch_kwargs( + self.launch_context.kwargs + ) From 96aac985c2545b5e8e5bf74a96e448a3b667463c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:23:23 +0100 Subject: [PATCH 039/130] copied non python launch script to harmony --- .../hosts/harmony/api/launch_script.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 client/ayon_core/hosts/harmony/api/launch_script.py diff --git a/client/ayon_core/hosts/harmony/api/launch_script.py b/client/ayon_core/hosts/harmony/api/launch_script.py new file mode 100644 index 0000000000..1f2c36b7e6 --- /dev/null +++ b/client/ayon_core/hosts/harmony/api/launch_script.py @@ -0,0 +1,93 @@ +"""Script wraps launch mechanism of Harmony implementations. + +Arguments passed to the script are passed to launch function in host +implementation. In all cases requires host app executable and may contain +workfile or others. +""" + +import os +import sys + +from ayon_core.hosts.harmony.api.lib import main + +# Get current file to locate start point of sys.argv +CURRENT_FILE = os.path.abspath(__file__) + + +def show_error_messagebox(title, message, detail_message=None): + """Function will show message and process ends after closing it.""" + from qtpy import QtWidgets, QtCore + from ayon_core import style + + app = QtWidgets.QApplication([]) + app.setStyleSheet(style.load_stylesheet()) + + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle(title) + msgbox.setText(message) + + if detail_message: + msgbox.setDetailedText(detail_message) + + msgbox.setWindowModality(QtCore.Qt.ApplicationModal) + msgbox.show() + + sys.exit(app.exec_()) + + +def on_invalid_args(script_not_found): + """Show to user message box saying that something went wrong. + + Tell user that arguments to launch implementation are invalid with + arguments details. + + Args: + script_not_found (bool): Use different message based on this value. + """ + + title = "Invalid arguments" + joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) + if script_not_found: + submsg = "Where couldn't find script path:\n\"{}\"" + else: + submsg = "Expected Host executable after script path:\n\"{}\"" + + message = "BUG: Got invalid arguments so can't launch Host application." + detail_message = "Process was launched with arguments:\n{}\n\n{}".format( + joined_args, + submsg.format(CURRENT_FILE) + ) + + show_error_messagebox(title, message, detail_message) + + +def main(argv): + # Modify current file path to find match in sys.argv which may be different + # on windows (different letter cases and slashes). + modified_current_file = CURRENT_FILE.replace("\\", "/").lower() + + # Create a copy of sys argv + sys_args = list(argv) + after_script_idx = None + # Find script path in sys.argv to know index of argv where host + # executable should be. + for idx, item in enumerate(sys_args): + if item.replace("\\", "/").lower() == modified_current_file: + after_script_idx = idx + 1 + break + + # Validate that there is at least one argument after script path + launch_args = None + if after_script_idx is not None: + launch_args = sys_args[after_script_idx:] + + if launch_args: + # Launch host implementation + main(*launch_args) + else: + # Show message box + on_invalid_args(after_script_idx is None) + + +if __name__ == "__main__": + main(sys.argv) From da395ae5305cb63cd19140cc2b48296facc94809 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:23:57 +0100 Subject: [PATCH 040/130] renamed 'HARMONY_HOST_DIR' to 'HARMONY_ADDON_ROOT' --- client/ayon_core/hosts/harmony/__init__.py | 4 ++-- client/ayon_core/hosts/harmony/addon.py | 4 ++-- client/ayon_core/hosts/harmony/api/pipeline.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/harmony/__init__.py b/client/ayon_core/hosts/harmony/__init__.py index 9177eaa285..449bb96a4f 100644 --- a/client/ayon_core/hosts/harmony/__init__.py +++ b/client/ayon_core/hosts/harmony/__init__.py @@ -1,10 +1,10 @@ from .addon import ( - HARMONY_HOST_DIR, + HARMONY_ADDON_ROOT, HarmonyAddon, ) __all__ = ( - "HARMONY_HOST_DIR", + "HARMONY_ADDON_ROOT", "HarmonyAddon", ) diff --git a/client/ayon_core/hosts/harmony/addon.py b/client/ayon_core/hosts/harmony/addon.py index 476d569415..7f8776cd74 100644 --- a/client/ayon_core/hosts/harmony/addon.py +++ b/client/ayon_core/hosts/harmony/addon.py @@ -1,7 +1,7 @@ import os from ayon_core.addon import AYONAddon, IHostAddon -HARMONY_HOST_DIR = os.path.dirname(os.path.abspath(__file__)) +HARMONY_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__)) class HarmonyAddon(AYONAddon, IHostAddon): @@ -11,7 +11,7 @@ class HarmonyAddon(AYONAddon, IHostAddon): def add_implementation_envs(self, env, _app): """Modify environments to contain all required for implementation.""" openharmony_path = os.path.join( - HARMONY_HOST_DIR, "vendor", "OpenHarmony" + HARMONY_ADDON_ROOT, "vendor", "OpenHarmony" ) # TODO check if is already set? What to do if is already set? env["LIB_OPENHARMONY_PATH"] = openharmony_path diff --git a/client/ayon_core/hosts/harmony/api/pipeline.py b/client/ayon_core/hosts/harmony/api/pipeline.py index a753a32ebb..d842ccd414 100644 --- a/client/ayon_core/hosts/harmony/api/pipeline.py +++ b/client/ayon_core/hosts/harmony/api/pipeline.py @@ -15,13 +15,13 @@ from ayon_core.pipeline import ( from ayon_core.pipeline.load import get_outdated_containers from ayon_core.pipeline.context_tools import get_current_project_folder -from ayon_core.hosts.harmony import HARMONY_HOST_DIR +from ayon_core.hosts.harmony import HARMONY_ADDON_ROOT import ayon_core.hosts.harmony.api as harmony log = logging.getLogger("ayon_core.hosts.harmony") -PLUGINS_DIR = os.path.join(HARMONY_HOST_DIR, "plugins") +PLUGINS_DIR = os.path.join(HARMONY_ADDON_ROOT, "plugins") PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") LOAD_PATH = os.path.join(PLUGINS_DIR, "load") CREATE_PATH = os.path.join(PLUGINS_DIR, "create") From 2119e735828c44ea23fc4a9d463382e474f7367a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:25:14 +0100 Subject: [PATCH 041/130] harmony has own prelaunch hook --- .../hooks/pre_non_python_host_launch.py | 58 ----------- client/ayon_core/hosts/harmony/__init__.py | 2 + client/ayon_core/hosts/harmony/addon.py | 13 +++ .../hosts/harmony/hooks/pre_launch_args.py | 95 +++++++++++++++++++ 4 files changed, 110 insertions(+), 58 deletions(-) delete mode 100644 client/ayon_core/hooks/pre_non_python_host_launch.py create mode 100644 client/ayon_core/hosts/harmony/hooks/pre_launch_args.py diff --git a/client/ayon_core/hooks/pre_non_python_host_launch.py b/client/ayon_core/hooks/pre_non_python_host_launch.py deleted file mode 100644 index a65e0ecba4..0000000000 --- a/client/ayon_core/hooks/pre_non_python_host_launch.py +++ /dev/null @@ -1,58 +0,0 @@ -import os - -from ayon_core.lib import get_ayon_launcher_args -from ayon_core.lib.applications import ( - get_non_python_host_kwargs, - PreLaunchHook, - LaunchTypes, -) - -from ayon_core import AYON_CORE_ROOT - - -class NonPythonHostHook(PreLaunchHook): - """Launch arguments preparation. - - Non python host implementation do not launch host directly but use - python script which launch the host. For these cases it is necessary to - prepend python (or ayon) executable and script path before application's. - """ - app_groups = {"harmony"} - - order = 20 - launch_types = {LaunchTypes.local} - - def execute(self): - # Pop executable - executable_path = self.launch_context.launch_args.pop(0) - - # Pop rest of launch arguments - There should not be other arguments! - remainders = [] - while self.launch_context.launch_args: - remainders.append(self.launch_context.launch_args.pop(0)) - - script_path = os.path.join( - AYON_CORE_ROOT, - "scripts", - "non_python_host_launch.py" - ) - - new_launch_args = get_ayon_launcher_args( - "run", script_path, executable_path - ) - # Add workfile path if exists - workfile_path = self.data["last_workfile_path"] - if ( - self.data.get("start_last_workfile") - and workfile_path - and os.path.exists(workfile_path)): - new_launch_args.append(workfile_path) - - # Append as whole list as these areguments should not be separated - self.launch_context.launch_args.append(new_launch_args) - - if remainders: - self.launch_context.launch_args.extend(remainders) - - self.launch_context.kwargs = \ - get_non_python_host_kwargs(self.launch_context.kwargs) diff --git a/client/ayon_core/hosts/harmony/__init__.py b/client/ayon_core/hosts/harmony/__init__.py index 449bb96a4f..6454d6f9d7 100644 --- a/client/ayon_core/hosts/harmony/__init__.py +++ b/client/ayon_core/hosts/harmony/__init__.py @@ -1,10 +1,12 @@ from .addon import ( HARMONY_ADDON_ROOT, HarmonyAddon, + get_launch_script_path, ) __all__ = ( "HARMONY_ADDON_ROOT", "HarmonyAddon", + "get_launch_script_path", ) diff --git a/client/ayon_core/hosts/harmony/addon.py b/client/ayon_core/hosts/harmony/addon.py index 7f8776cd74..1915a7eb6f 100644 --- a/client/ayon_core/hosts/harmony/addon.py +++ b/client/ayon_core/hosts/harmony/addon.py @@ -18,3 +18,16 @@ class HarmonyAddon(AYONAddon, IHostAddon): def get_workfile_extensions(self): return [".zip"] + + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(HARMONY_ADDON_ROOT, "hooks") + ] + + +def get_launch_script_path(): + return os.path.join( + HARMONY_ADDON_ROOT, "api", "launch_script.py" + ) diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py new file mode 100644 index 0000000000..1a36c890a7 --- /dev/null +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -0,0 +1,95 @@ +import os +import platform +import subprocess + +from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib.applications import ( + PreLaunchHook, + LaunchTypes, +) +from ayon_core.hosts.harmony import get_launch_script_path + + +def get_launch_kwargs(kwargs): + """Explicit setting of kwargs for Popen for Harmony. + + Expected behavior + - ayon_console opens window with logs + - ayon has stdout/stderr available for capturing + + Args: + kwargs (Union[dict, None]): Current kwargs or None. + + """ + if kwargs is None: + kwargs = {} + + if platform.system().lower() != "windows": + return kwargs + + executable_path = os.environ.get("AYON_EXECUTABLE") + + executable_filename = "" + if executable_path: + executable_filename = os.path.basename(executable_path) + + is_gui_executable = "ayon_console" not in executable_filename + if is_gui_executable: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + else: + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + return kwargs + + +class HarmonyPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and script path to Harmony implementation + before Harmony executable and add last workfile path to launch arguments. + + Existence of last workfile is checked. If workfile does not exists tries + to copy templated workfile from predefined path. + """ + app_groups = {"harmony"} + + order = 20 + launch_types = {LaunchTypes.local} + + def execute(self): + # Pop executable + executable_path = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + script_path = get_launch_script_path() + + new_launch_args = get_ayon_launcher_args( + "run", script_path, executable_path + ) + # Add workfile path if exists + workfile_path = self.data["last_workfile_path"] + if ( + self.data.get("start_last_workfile") + and workfile_path + and os.path.exists(workfile_path) + ): + new_launch_args.append(workfile_path) + + # Append as whole list as these arguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.launch_context.launch_args.extend(remainders) + + self.launch_context.kwargs = get_launch_kwargs( + self.launch_context.kwargs + ) From 4fb898615bff67a20925622e297859b369db6477 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:28:59 +0100 Subject: [PATCH 042/130] remove unused global script --- .../scripts/non_python_host_launch.py | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 client/ayon_core/scripts/non_python_host_launch.py diff --git a/client/ayon_core/scripts/non_python_host_launch.py b/client/ayon_core/scripts/non_python_host_launch.py deleted file mode 100644 index 4c18fd0ccc..0000000000 --- a/client/ayon_core/scripts/non_python_host_launch.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Script wraps launch mechanism of non python host implementations. - -Arguments passed to the script are passed to launch function in host -implementation. In all cases requires host app executable and may contain -workfile or others. -""" - -import os -import sys - -# Get current file to locate start point of sys.argv -CURRENT_FILE = os.path.abspath(__file__) - - -def show_error_messagebox(title, message, detail_message=None): - """Function will show message and process ends after closing it.""" - from qtpy import QtWidgets, QtCore - from ayon_core import style - - app = QtWidgets.QApplication([]) - app.setStyleSheet(style.load_stylesheet()) - - msgbox = QtWidgets.QMessageBox() - msgbox.setWindowTitle(title) - msgbox.setText(message) - - if detail_message: - msgbox.setDetailedText(detail_message) - - msgbox.setWindowModality(QtCore.Qt.ApplicationModal) - msgbox.show() - - sys.exit(app.exec_()) - - -def on_invalid_args(script_not_found): - """Show to user message box saying that something went wrong. - - Tell user that arguments to launch implementation are invalid with - arguments details. - - Args: - script_not_found (bool): Use different message based on this value. - """ - - title = "Invalid arguments" - joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) - if script_not_found: - submsg = "Where couldn't find script path:\n\"{}\"" - else: - submsg = "Expected Host executable after script path:\n\"{}\"" - - message = "BUG: Got invalid arguments so can't launch Host application." - detail_message = "Process was launched with arguments:\n{}\n\n{}".format( - joined_args, - submsg.format(CURRENT_FILE) - ) - - show_error_messagebox(title, message, detail_message) - - -def main(argv): - # Modify current file path to find match in sys.argv which may be different - # on windows (different letter cases and slashes). - modified_current_file = CURRENT_FILE.replace("\\", "/").lower() - - # Create a copy of sys argv - sys_args = list(argv) - after_script_idx = None - # Find script path in sys.argv to know index of argv where host - # executable should be. - for idx, item in enumerate(sys_args): - if item.replace("\\", "/").lower() == modified_current_file: - after_script_idx = idx + 1 - break - - # Validate that there is at least one argument after script path - launch_args = None - if after_script_idx is not None: - launch_args = sys_args[after_script_idx:] - - host_name = os.environ["AYON_HOST_NAME"].lower() - if host_name == "photoshop": - # TODO refactor launch logic according to AE - from ayon_core.hosts.photoshop.api.lib import main - elif host_name == "aftereffects": - from ayon_core.hosts.aftereffects.api.launch_logic import main - elif host_name == "harmony": - from ayon_core.hosts.harmony.api.lib import main - else: - title = "Unknown host name" - message = ( - "BUG: Environment variable AYON_HOST_NAME contains unknown" - " host name \"{}\"" - ).format(host_name) - show_error_messagebox(title, message) - return - - if launch_args: - # Launch host implementation - main(*launch_args) - else: - # Show message box - on_invalid_args(after_script_idx is None) - - -if __name__ == "__main__": - main(sys.argv) From 2c07fb45032d28162c32661cd977c36f20058aec Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:29:16 +0100 Subject: [PATCH 043/130] removed unused 'get_non_python_host_kwargs' --- client/ayon_core/lib/applications.py | 39 ---------------------------- 1 file changed, 39 deletions(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 4bf0c31d93..68fc3ea201 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1985,42 +1985,3 @@ def should_workfile_tool_start( if output is None: return default_output return output - - -def get_non_python_host_kwargs(kwargs, allow_console=True): - """Explicit setting of kwargs for Popen for AE/PS/Harmony. - - Expected behavior - - ayon_console opens window with logs - - ayon has stdout/stderr available for capturing - - Args: - kwargs (dict) or None - allow_console (bool): use False for inner Popen opening app itself or - it will open additional console (at least for Harmony) - """ - - if kwargs is None: - kwargs = {} - - if platform.system().lower() != "windows": - return kwargs - - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_gui_executable = "ayon_console" not in executable_filename - if is_gui_executable: - kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL - }) - elif allow_console: - kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE - }) - return kwargs From ca39fc429130942c85883d471dd5604efad235dd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:31:11 +0100 Subject: [PATCH 044/130] don't use 'get_non_python_host_kwargs' when launching harmony --- client/ayon_core/hosts/harmony/api/lib.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index bc73e19066..34cb14636d 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """Utility functions used for Avalon - Harmony integration.""" +import platform import subprocess import threading import os @@ -22,7 +23,6 @@ from .server import Server from ayon_core.tools.stdout_broker.app import StdOutBroker from ayon_core.tools.utils import host_tools from ayon_core import style -from ayon_core.lib.applications import get_non_python_host_kwargs # Setup logging. log = logging.getLogger(__name__) @@ -324,7 +324,18 @@ def launch_zip_file(filepath): return print("Launching {}".format(scene_path)) - kwargs = get_non_python_host_kwargs({}, False) + kwargs = {} + if platform.system().lower() == "windows": + executable_filename = os.path.basename( + os.getenv("AYON_EXECUTABLE", "") + ) + if "ayon_console" not in executable_filename: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + process = subprocess.Popen( [ProcessContext.application_path, scene_path], **kwargs From 508d4b559e7ed8db2da14c3e1959b403efb33b43 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:32:17 +0100 Subject: [PATCH 045/130] added comment with question --- client/ayon_core/hosts/harmony/api/lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index 34cb14636d..b78fe468b5 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -324,6 +324,7 @@ def launch_zip_file(filepath): return print("Launching {}".format(scene_path)) + # QUESTION Could we use 'run_detached_process' from 'ayon_core.lib'? kwargs = {} if platform.system().lower() == "windows": executable_filename = os.path.basename( From 562730fa65384e89fe1c72cd981c0e997200641a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:33:02 +0100 Subject: [PATCH 046/130] added helper function to determine if ui executable is used --- .../aftereffects/hooks/pre_launch_args.py | 14 +++----- client/ayon_core/hosts/harmony/api/lib.py | 25 +++++++------- .../hosts/harmony/hooks/pre_launch_args.py | 14 +++----- .../hosts/photoshop/hooks/pre_launch_args.py | 14 +++----- client/ayon_core/lib/__init__.py | 2 ++ client/ayon_core/lib/ayon_info.py | 34 ++++++++++++++++++- 6 files changed, 63 insertions(+), 40 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py index 2c5baa3b68..76ccd2bd4f 100644 --- a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -2,7 +2,10 @@ import os import platform import subprocess -from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib import ( + get_ayon_launcher_args, + is_using_ui_executable, +) from ayon_core.lib.applications import ( PreLaunchHook, LaunchTypes, @@ -27,14 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_in_ui_launcher = "ayon_console" not in executable_filename - if is_in_ui_launcher: + if is_using_ui_executable(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index b78fe468b5..828ee3863e 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -15,15 +15,17 @@ import json import signal import time from uuid import uuid4 -from qtpy import QtWidgets, QtCore, QtGui import collections -from .server import Server +from qtpy import QtWidgets, QtCore, QtGui +from ayon_core.lib import is_using_ui_executable from ayon_core.tools.stdout_broker.app import StdOutBroker from ayon_core.tools.utils import host_tools from ayon_core import style +from .server import Server + # Setup logging. log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) @@ -326,16 +328,15 @@ def launch_zip_file(filepath): print("Launching {}".format(scene_path)) # QUESTION Could we use 'run_detached_process' from 'ayon_core.lib'? kwargs = {} - if platform.system().lower() == "windows": - executable_filename = os.path.basename( - os.getenv("AYON_EXECUTABLE", "") - ) - if "ayon_console" not in executable_filename: - kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL - }) + if ( + platform.system().lower() == "windows" + and is_using_ui_executable() + ): + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) process = subprocess.Popen( [ProcessContext.application_path, scene_path], diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py index 1a36c890a7..c2c667c1d8 100644 --- a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -2,7 +2,10 @@ import os import platform import subprocess -from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib import ( + get_ayon_launcher_args, + is_using_ui_executable, +) from ayon_core.lib.applications import ( PreLaunchHook, LaunchTypes, @@ -27,14 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_gui_executable = "ayon_console" not in executable_filename - if is_gui_executable: + if is_using_ui_executable(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py index 0689a6c2f0..228413e1ea 100644 --- a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -2,7 +2,10 @@ import os import platform import subprocess -from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib import ( + get_ayon_launcher_args, + is_using_ui_executable, +) from ayon_core.lib.applications import ( PreLaunchHook, LaunchTypes, @@ -27,14 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_gui_executable = "ayon_console" not in executable_filename - if is_gui_executable: + if is_using_ui_executable(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index f69fd10b07..d82fb0de0e 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -155,6 +155,7 @@ from .path_tools import ( from .ayon_info import ( is_running_from_build, + is_using_ui_executable, is_staging_enabled, is_dev_mode_enabled, is_in_tests, @@ -275,6 +276,7 @@ __all__ = [ "Logger", "is_running_from_build", + "is_using_ui_executable", "is_staging_enabled", "is_dev_mode_enabled", "is_in_tests", diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index ec37d735d8..3d4c38c99a 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -10,6 +10,12 @@ from .local_settings import get_local_site_id def get_ayon_launcher_version(): + """Get AYON launcher version. + + Returns: + str: Version string. + + """ version_filepath = os.path.join(os.environ["AYON_ROOT"], "version.py") if not os.path.exists(version_filepath): return None @@ -24,8 +30,8 @@ def is_running_from_build(): Returns: bool: True if running from build. - """ + """ executable_path = os.environ["AYON_EXECUTABLE"] executable_filename = os.path.basename(executable_path) if "python" in executable_filename.lower(): @@ -33,6 +39,32 @@ def is_running_from_build(): return True +def is_using_ui_executable(): + """AYON launcher UI windows executable is used. + + This function make sense only on Windows platform. For other platforms + always returns False. False is also returned if process is running from + code. + + AYON launcher on windows has 2 executable files. First 'ayon_console.exe' + works as 'python.exe' executable, the second 'ayon.exe' works as + 'pythonw.exe' executable. The difference is way how stdout/stderr is + handled (especially when calling subprocess). + + Returns: + bool: True if UI executable is used. + + """ + if ( + platform.system().lower() != "windows" + or is_running_from_build() + ): + return False + executable_path = os.environ["AYON_EXECUTABLE"] + executable_filename = os.path.basename(executable_path) + return "ayon_console" not in executable_filename + + def is_staging_enabled(): return os.getenv("AYON_USE_STAGING") == "1" From 5cb07274d1fea5818c0e431c99aa0b3c36a7918c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:45:27 +0100 Subject: [PATCH 047/130] renamed 'is_using_ui_executable' to 'is_using_ayon_console' --- client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py | 4 ++-- client/ayon_core/hosts/harmony/api/lib.py | 4 ++-- client/ayon_core/hosts/harmony/hooks/pre_launch_args.py | 4 ++-- client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py | 4 ++-- client/ayon_core/lib/__init__.py | 4 ++-- client/ayon_core/lib/ayon_info.py | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py index 76ccd2bd4f..959df0eb39 100644 --- a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -4,7 +4,7 @@ import subprocess from ayon_core.lib import ( get_ayon_launcher_args, - is_using_ui_executable, + is_using_ayon_console, ) from ayon_core.lib.applications import ( PreLaunchHook, @@ -30,7 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ui_executable(): + if is_using_ayon_console(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index 828ee3863e..d9c17c6214 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -19,7 +19,7 @@ import collections from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.lib import is_using_ui_executable +from ayon_core.lib import is_using_ayon_console from ayon_core.tools.stdout_broker.app import StdOutBroker from ayon_core.tools.utils import host_tools from ayon_core import style @@ -330,7 +330,7 @@ def launch_zip_file(filepath): kwargs = {} if ( platform.system().lower() == "windows" - and is_using_ui_executable() + and is_using_ayon_console() ): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py index c2c667c1d8..66bc0a80d8 100644 --- a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -4,7 +4,7 @@ import subprocess from ayon_core.lib import ( get_ayon_launcher_args, - is_using_ui_executable, + is_using_ayon_console, ) from ayon_core.lib.applications import ( PreLaunchHook, @@ -30,7 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ui_executable(): + if is_using_ayon_console(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py index 228413e1ea..7fe6e7437f 100644 --- a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -4,7 +4,7 @@ import subprocess from ayon_core.lib import ( get_ayon_launcher_args, - is_using_ui_executable, + is_using_ayon_console, ) from ayon_core.lib.applications import ( PreLaunchHook, @@ -30,7 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ui_executable(): + if is_using_ayon_console(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index d82fb0de0e..10e290360d 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -155,7 +155,7 @@ from .path_tools import ( from .ayon_info import ( is_running_from_build, - is_using_ui_executable, + is_using_ayon_console, is_staging_enabled, is_dev_mode_enabled, is_in_tests, @@ -276,7 +276,7 @@ __all__ = [ "Logger", "is_running_from_build", - "is_using_ui_executable", + "is_using_ayon_console", "is_staging_enabled", "is_dev_mode_enabled", "is_in_tests", diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index 3d4c38c99a..c3bda70834 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -39,7 +39,7 @@ def is_running_from_build(): return True -def is_using_ui_executable(): +def is_using_ayon_console(): """AYON launcher UI windows executable is used. This function make sense only on Windows platform. For other platforms From e15460d851dd56869cbcdbec52dde1d72aef25bb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:51:03 +0100 Subject: [PATCH 048/130] fix the reversed logic --- .../hosts/aftereffects/hooks/pre_launch_args.py | 8 ++++---- client/ayon_core/hosts/harmony/api/lib.py | 2 +- .../ayon_core/hosts/harmony/hooks/pre_launch_args.py | 8 ++++---- .../ayon_core/hosts/photoshop/hooks/pre_launch_args.py | 10 +++++----- client/ayon_core/lib/ayon_info.py | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py index 959df0eb39..979d9ff3e5 100644 --- a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -32,13 +32,13 @@ def get_launch_kwargs(kwargs): if is_using_ayon_console(): kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL + "creationflags": subprocess.CREATE_NEW_CONSOLE }) else: kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL }) return kwargs diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index d9c17c6214..3c833c7b69 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -330,7 +330,7 @@ def launch_zip_file(filepath): kwargs = {} if ( platform.system().lower() == "windows" - and is_using_ayon_console() + and not is_using_ayon_console() ): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py index 66bc0a80d8..bbad14084a 100644 --- a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -32,13 +32,13 @@ def get_launch_kwargs(kwargs): if is_using_ayon_console(): kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL + "creationflags": subprocess.CREATE_NEW_CONSOLE }) else: kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL }) return kwargs diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py index 7fe6e7437f..8358c11ca1 100644 --- a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -30,16 +30,16 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ayon_console(): + if not is_using_ayon_console(): + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + else: kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL }) - else: - kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE - }) return kwargs diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index c3bda70834..adb3c1befc 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -40,10 +40,10 @@ def is_running_from_build(): def is_using_ayon_console(): - """AYON launcher UI windows executable is used. + """AYON launcher console executable is used. This function make sense only on Windows platform. For other platforms - always returns False. False is also returned if process is running from + always returns True. True is also returned if process is running from code. AYON launcher on windows has 2 executable files. First 'ayon_console.exe' @@ -52,17 +52,17 @@ def is_using_ayon_console(): handled (especially when calling subprocess). Returns: - bool: True if UI executable is used. + bool: True if console executable is used. """ if ( platform.system().lower() != "windows" or is_running_from_build() ): - return False + return True executable_path = os.environ["AYON_EXECUTABLE"] executable_filename = os.path.basename(executable_path) - return "ayon_console" not in executable_filename + return "ayon_console" in executable_filename def is_staging_enabled(): From 769f80e923a07435c0a6813a4c20048ba4d8f3c4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 20:43:55 +0100 Subject: [PATCH 049/130] Import validation message --- .../help/validate_rig_out_set_node_ids.xml | 32 +++++++++++++++++++ .../publish/validate_rig_out_set_node_ids.py | 18 +++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 client/ayon_core/hosts/maya/plugins/publish/help/validate_rig_out_set_node_ids.xml diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/validate_rig_out_set_node_ids.xml b/client/ayon_core/hosts/maya/plugins/publish/help/validate_rig_out_set_node_ids.xml new file mode 100644 index 0000000000..374b8e59ae --- /dev/null +++ b/client/ayon_core/hosts/maya/plugins/publish/help/validate_rig_out_set_node_ids.xml @@ -0,0 +1,32 @@ + + + +Shape IDs mismatch original shape +## Shapes mismatch IDs with original shape + +Meshes are detected in the **rig** where the (deformed) mesh has a different +`cbId` than the same mesh in its deformation history. +Theses should normally be the same. + +### How to repair? + +By using the repair action the IDs from the shape in history will be +copied to the deformed shape. For rig instances, in many cases the +correct fix is to use the repair action **unless** you explicitly tried +to update the `cbId` values on the meshes - in that case you actually want +to do to the reverse and copy the IDs from the deformed mesh to the history +mesh instead. + + + +### How does this happen? + +When a deformer is applied in the scene on a referenced mesh that had no +deformers then Maya will create a new shape node for the mesh that +does not have the original id. Then on scene save new ids get created for the +meshes lacking a `cbId` and thus the mesh then has a different `cbId` than +the mesh in the deformation history. + + + + diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py b/client/ayon_core/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py index ab8cc25210..c9e779e999 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py @@ -7,7 +7,7 @@ from ayon_core.hosts.maya.api import lib from ayon_core.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError, + PublishXmlValidationError, OptionalPyblishPluginMixin ) @@ -42,8 +42,20 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin, # if a deformer has been created on the shape invalid = self.get_invalid(instance) if invalid: - raise PublishValidationError( - "Nodes found with mismatching IDs: {0}".format(invalid) + + # Use the short names + invalid = cmds.ls(invalid) + invalid.sort() + + # Construct a human-readable list + invalid = "\n".join("- {}".format(node) for node in invalid) + + raise PublishXmlValidationError( + plugin=ValidateRigOutSetNodeIds, + message=( + "Rig nodes have different IDs than their input " + "history: \n{0}".format(invalid) + ) ) @classmethod From 3cee75065734549542277bf0ee9585404343fe59 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 22:10:08 +0100 Subject: [PATCH 050/130] Fix instance node --- .../ayon_core/hosts/maya/plugins/publish/validate_step_size.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py b/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py index a3419a83a9..cad584d8f9 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py @@ -29,7 +29,7 @@ class ValidateStepSize(pyblish.api.InstancePlugin, @classmethod def get_invalid(cls, instance): - objset = instance.data['name'] + objset = instance.data['instance_node'] step = instance.data.get("step", 1.0) if step < cls.MIN or step > cls.MAX: From f95d172c55f1fba372d8b7b501894016b34fde23 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Mar 2024 22:10:26 +0100 Subject: [PATCH 051/130] Improve validation message --- .../ayon_core/hosts/maya/plugins/publish/validate_step_size.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py b/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py index cad584d8f9..a276a5b644 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py @@ -47,4 +47,4 @@ class ValidateStepSize(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( - "Invalid instances found: {0}".format(invalid)) + "Instance found with invalid step size: {0}".format(invalid)) From 0e30dfb2a8d3a38c61f6d6ad668971305da77ed5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 09:29:53 +0100 Subject: [PATCH 052/130] Maya: Raise PublishValidationError in validators --- .../plugins/publish/validate_mesh_lamina_faces.py | 10 +++++++--- .../maya/plugins/publish/validate_mesh_ngons.py | 7 ++++--- .../plugins/publish/validate_mesh_single_uv_set.py | 5 +++-- .../plugins/publish/validate_mesh_uv_set_map1.py | 7 ++++--- .../plugins/publish/validate_node_no_ghosting.py | 12 +++++++----- .../plugins/publish/validate_shape_default_names.py | 7 ++++--- .../publish/validate_skinCluster_deformer_set.py | 12 +++++++++--- .../maya/plugins/publish/validate_unique_names.py | 8 +++++--- .../publish/validate_vray_referenced_aovs.py | 6 ++++-- .../maya/plugins/publish/validate_vrayproxy.py | 13 ++++++++----- 10 files changed, 55 insertions(+), 32 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py index 8f80b689fd..5543505206 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py @@ -2,7 +2,11 @@ from maya import cmds import pyblish.api import ayon_core.hosts.maya.api.action -from ayon_core.pipeline.publish import ValidateMeshOrder, OptionalPyblishPluginMixin +from ayon_core.pipeline.publish import ( + ValidateMeshOrder, + OptionalPyblishPluginMixin, + PublishValidationError +) class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin, @@ -36,5 +40,5 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: - raise ValueError("Meshes found with lamina faces: " - "{0}".format(invalid)) + raise PublishValidationError( + "Meshes found with lamina faces: {0}".format(invalid)) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py index 5f107b7f7e..1bca4d5e9a 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py @@ -5,7 +5,8 @@ import ayon_core.hosts.maya.api.action from ayon_core.hosts.maya.api import lib from ayon_core.pipeline.publish import ( ValidateContentsOrder, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) @@ -49,5 +50,5 @@ class ValidateMeshNgons(pyblish.api.Validator, invalid = self.get_invalid(instance) if invalid: - raise ValueError("Meshes found with n-gons" - "values: {0}".format(invalid)) + raise PublishValidationError( + "Meshes found with n-gons: {0}".format(invalid)) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py index 8dbd0ca264..21697cd903 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py @@ -6,7 +6,8 @@ from ayon_core.hosts.maya.api import lib from ayon_core.pipeline.publish import ( RepairAction, ValidateMeshOrder, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) @@ -66,7 +67,7 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin, if allowed: self.log.warning(message) else: - raise ValueError(message) + raise PublishValidationError(message) @classmethod def repair(cls, instance): diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_uv_set_map1.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_uv_set_map1.py index c7f405b0cf..a139b65169 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_uv_set_map1.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_uv_set_map1.py @@ -5,7 +5,8 @@ import ayon_core.hosts.maya.api.action from ayon_core.pipeline.publish import ( RepairAction, ValidateMeshOrder, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) @@ -55,8 +56,8 @@ class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: - raise ValueError("Meshes found without 'map1' " - "UV set: {0}".format(invalid)) + raise PublishValidationError( + "Meshes found without 'map1' UV set: {0}".format(invalid)) @classmethod def repair(cls, instance): diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py b/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py index 73701f8d83..10cbbc9a88 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py @@ -5,10 +5,12 @@ import pyblish.api import ayon_core.hosts.maya.api.action from ayon_core.pipeline.publish import ( ValidateContentsOrder, - OptionalPyblishPluginMixin - + OptionalPyblishPluginMixin, + PublishValidationError ) -class ValidateNodeNoGhosting(pyblish.api.InstancePlugin. + + +class ValidateNodeNoGhosting(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Ensure nodes do not have ghosting enabled. @@ -55,5 +57,5 @@ class ValidateNodeNoGhosting(pyblish.api.InstancePlugin. invalid = self.get_invalid(instance) if invalid: - raise ValueError("Nodes with ghosting enabled found: " - "{0}".format(invalid)) + raise PublishValidationError( + "Nodes with ghosting enabled found: {0}".format(invalid)) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_default_names.py b/client/ayon_core/hosts/maya/plugins/publish/validate_shape_default_names.py index 2f0811a73e..c4c4c909d3 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_default_names.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_shape_default_names.py @@ -8,7 +8,8 @@ import ayon_core.hosts.maya.api.action from ayon_core.pipeline.publish import ( ValidateContentsOrder, RepairAction, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) @@ -84,8 +85,8 @@ class ValidateShapeDefaultNames(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: - raise ValueError("Incorrectly named shapes " - "found: {0}".format(invalid)) + raise PublishValidationError( + "Incorrectly named shapes found: {0}".format(invalid)) @classmethod def repair(cls, instance): diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py b/client/ayon_core/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py index 48d8e63553..a548e12f33 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py @@ -3,7 +3,11 @@ from maya import cmds import pyblish.api import ayon_core.hosts.maya.api.action -from ayon_core.pipeline.publish import ValidateContentsOrder,OptionalPyblishPluginMixin +from ayon_core.pipeline.publish import ( + ValidateContentsOrder, + OptionalPyblishPluginMixin, + PublishValidationError +) class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin, @@ -30,8 +34,10 @@ class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: - raise ValueError("Invalid skinCluster relationships " - "found on meshes: {0}".format(invalid)) + raise PublishValidationError( + "Invalid skinCluster relationships found on meshes: {0}" + .format(invalid) + ) @classmethod def get_invalid(cls, instance): diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unique_names.py b/client/ayon_core/hosts/maya/plugins/publish/validate_unique_names.py index 8ec704ddd1..72c3c7dc72 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_unique_names.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_unique_names.py @@ -4,9 +4,11 @@ import pyblish.api import ayon_core.hosts.maya.api.action from ayon_core.pipeline.publish import ( ValidateContentsOrder, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) + class ValidateUniqueNames(pyblish.api.Validator, OptionalPyblishPluginMixin): """transform names should be unique @@ -40,5 +42,5 @@ class ValidateUniqueNames(pyblish.api.Validator, return invalid = self.get_invalid(instance) if invalid: - raise ValueError("Nodes found with none unique names. " - "values: {0}".format(invalid)) + raise PublishValidationError( + "Nodes found with non-unique names:\n{0}".format(invalid)) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py b/client/ayon_core/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py index 7c480a6bf7..9df5fb8488 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py @@ -6,9 +6,11 @@ from maya import cmds from ayon_core.pipeline.publish import ( RepairContextAction, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) + class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate whether the V-Ray Render Elements (AOVs) include references. @@ -60,7 +62,7 @@ class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin, self.log.error(( "'Use referenced' not enabled in Vray Render Settings." )) - raise AssertionError("Invalid render settings") + raise PublishValidationError("Invalid render settings") @classmethod def repair(cls, context): diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py index 29b8be411c..47006ca9de 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py @@ -1,7 +1,10 @@ import pyblish.api -from ayon_core.pipeline import KnownPublishError -from ayon_core.pipeline.publish import OptionalPyblishPluginMixin +from ayon_core.pipeline.publish import ( + OptionalPyblishPluginMixin, + PublishValidationError +) + class ValidateVrayProxy(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): @@ -17,18 +20,18 @@ class ValidateVrayProxy(pyblish.api.InstancePlugin, if not self.is_active(data): return if not data["setMembers"]: - raise KnownPublishError( + raise PublishValidationError( "'%s' is empty! This is a bug" % instance.name ) if data["animation"]: if data["frameEnd"] < data["frameStart"]: - raise KnownPublishError( + raise PublishValidationError( "End frame is smaller than start frame" ) if not data["vrmesh"] and not data["alembic"]: - raise KnownPublishError( + raise PublishValidationError( "Both vrmesh and alembic are off. Needs at least one to" " publish." ) From 64be88cff5129de55e9a487cf2cee3503e91e968 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 09:44:14 +0100 Subject: [PATCH 053/130] Add description to error message --- .../plugins/publish/validate_mesh_lamina_faces.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py index 5543505206..bfb4257f23 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py @@ -24,6 +24,16 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin, actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] optional = True + description = ( + "## Meshes with Lamina Faces\n" + "Detected meshes with lamina faces. Lamina faces are faces " + "that share all of their edges and thus are merged together on top of " + "each other.\n\n" + "### How to repair?\n" + "You can repair them by using Maya's modeling tool `Mesh > Cleanup..` " + "and select to cleanup matching polygons for lamina faces." + ) + @staticmethod def get_invalid(instance): meshes = cmds.ls(instance, type='mesh', long=True) @@ -41,4 +51,5 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin, if invalid: raise PublishValidationError( - "Meshes found with lamina faces: {0}".format(invalid)) + "Meshes found with lamina faces: {0}".format(invalid), + description=self.description) From 2bdb362a04260e33efbcb98a1b2066d64b0c2df2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 09:44:54 +0100 Subject: [PATCH 054/130] Simplify error message for readability --- .../ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py index 934cbae327..c95e1ec816 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py @@ -51,5 +51,5 @@ class ValidateMeshEmpty(pyblish.api.InstancePlugin): invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( - "Meshes found in instance without any vertices: %s" % invalid + "Meshes found without any vertices: %s" % invalid ) From 7f25e9d5f1eb473370202de460960df73c897fba Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 09:47:42 +0100 Subject: [PATCH 055/130] Add description to validation report --- .../maya/plugins/publish/validate_mesh_ngons.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py index 1bca4d5e9a..dd43b70bd2 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py @@ -28,6 +28,15 @@ class ValidateMeshNgons(pyblish.api.Validator, actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] optional = True + description = ( + "## Meshes with NGONs Faces\n" + "Detected meshes with NGON faces. NGONS are faces that " + "with more than four sides.\n\n" + "### How to repair?\n" + "You can repair them by usings Maya's modeling tool Mesh > Cleanup.. " + "and select to cleanup matching polygons for lamina faces." + ) + @staticmethod def get_invalid(instance): @@ -51,4 +60,5 @@ class ValidateMeshNgons(pyblish.api.Validator, invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( - "Meshes found with n-gons: {0}".format(invalid)) + "Meshes found with n-gons: {0}".format(invalid), + description=self.description) From 9107440bf52db8d24f6952386f36a4896db3c0f8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 09:48:08 +0100 Subject: [PATCH 056/130] Add description to validation report --- .../ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py index dd43b70bd2..b6d3dc73fd 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py @@ -30,7 +30,7 @@ class ValidateMeshNgons(pyblish.api.Validator, description = ( "## Meshes with NGONs Faces\n" - "Detected meshes with NGON faces. NGONS are faces that " + "Detected meshes with NGON faces. **NGONS** are faces that " "with more than four sides.\n\n" "### How to repair?\n" "You can repair them by usings Maya's modeling tool Mesh > Cleanup.. " From 9f6b9ddd3d119b268eaddf526346ce838e3f1f89 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 09:50:56 +0100 Subject: [PATCH 057/130] Cosmetics/hound --- .../maya/plugins/publish/validate_mesh_shader_connections.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_shader_connections.py b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_shader_connections.py index 8672ac13dd..70ede83f2d 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_shader_connections.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_shader_connections.py @@ -107,8 +107,9 @@ class ValidateMeshShaderConnections(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: - raise PublishValidationError("Shapes found with invalid shader " - "connections: {0}".format(invalid)) + raise PublishValidationError( + "Shapes found with invalid shader connections: " + "{0}".format(invalid)) @staticmethod def get_invalid(instance): From d0da71c45cc2ec80a8b6fa8cc66db36393c81bbb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 10:14:39 +0100 Subject: [PATCH 058/130] Improve readability of the artist facing report --- .../maya/plugins/publish/validate_no_namespace.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_no_namespace.py b/client/ayon_core/hosts/maya/plugins/publish/validate_no_namespace.py index 7ea2a79339..f546caff2c 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_no_namespace.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_no_namespace.py @@ -49,11 +49,17 @@ class ValidateNoNamespace(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: + invalid_namespaces = {get_namespace(node) for node in invalid} raise PublishValidationError( - "Namespaces found:\n\n{0}".format( - _as_report_list(sorted(invalid)) + message="Namespaces found:\n\n{0}".format( + _as_report_list(sorted(invalid_namespaces)) ), - title="Namespaces in model" + title="Namespaces in model", + description=( + "## Namespaces found in model\n" + "It is not allowed to publish a model that contains " + "namespaces." + ) ) @classmethod From fe8613b55e14d529ad76d9cd8a1e25e13631f6af Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 10:38:20 +0100 Subject: [PATCH 059/130] Improve description, remove docstring which was only about the `bake_attributes` to begin with --- server_addon/maya/server/settings/publishers.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index fe5c10e93c..7e1cdbf167 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -315,16 +315,13 @@ class ExtractMayaSceneRawModel(BaseSettingsModel): class ExtractCameraAlembicModel(BaseSettingsModel): - """ - List of attributes that will be added to the baked alembic camera. Needs to be written in python list syntax. - """ enabled: bool = SettingsField(title="ExtractCameraAlembic") optional: bool = SettingsField(title="Optional") active: bool = SettingsField(title="Active") bake_attributes: str = SettingsField( "[]", title="Bake Attributes", widget="textarea", description="List of attributes that will be included in the alembic " - "export.", + "camera export. Needs to be written as a JSON list.", ) @validator("bake_attributes") From 08a6cbc57c33bf346bbc4364d9473bc9d312a4be Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 11:57:15 +0100 Subject: [PATCH 060/130] Ensure unique class name compared to `extract_yeti_cache.py` --- .../hosts/maya/plugins/publish/extract_unreal_yeticache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_yeticache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_yeticache.py index 9a264959d1..9a6b4ebaed 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_yeticache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_yeticache.py @@ -5,13 +5,13 @@ from maya import cmds from ayon_core.pipeline import publish -class ExtractYetiCache(publish.Extractor): +class ExtractUnrealYetiCache(publish.Extractor): """Producing Yeti cache files using scene time range. This will extract Yeti cache file sequence and fur settings. """ - label = "Extract Yeti Cache" + label = "Extract Yeti Cache (Unreal)" hosts = ["maya"] families = ["yeticacheUE"] From 3526a1e52e60aef2892ad26892eac8c677ece6a2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 26 Mar 2024 16:02:44 +0100 Subject: [PATCH 061/130] use correct source for product type --- .../hosts/maya/plugins/load/load_redshift_proxy.py | 5 +---- client/ayon_core/hosts/maya/plugins/load/load_reference.py | 6 +----- .../ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py | 5 +---- .../hosts/maya/plugins/load/load_vdb_to_redshift.py | 5 +---- .../ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py | 5 +---- client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py | 5 +---- client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py | 5 +---- client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py | 5 +---- 8 files changed, 8 insertions(+), 33 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py index 63dae87243..0f91d9048a 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py @@ -32,10 +32,7 @@ class RedshiftProxyLoader(load.LoaderPlugin): def load(self, context, name=None, namespace=None, options=None): """Plugin entry point.""" - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "redshiftproxy" + product_type = context["product"]["productType"] folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( diff --git a/client/ayon_core/hosts/maya/plugins/load/load_reference.py b/client/ayon_core/hosts/maya/plugins/load/load_reference.py index fdd85eda43..de18b2b0ec 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_reference.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_reference.py @@ -117,11 +117,7 @@ class ReferenceLoader(plugin.ReferenceLoader): def process_reference(self, context, name, namespace, options): import maya.cmds as cmds - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "model" - + product_type = context["product"]["productType"] project_name = context["project"]["name"] # True by default to keep legacy behaviours attach_to_root = options.get("attach_to_root", True) diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py index f0fb89e5a4..3d984fdc79 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py @@ -25,10 +25,7 @@ class LoadVDBtoArnold(load.LoaderPlugin): from ayon_core.hosts.maya.api.pipeline import containerise from ayon_core.hosts.maya.api.lib import unique_namespace - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "vdbcache" + product_type = context["product"]["productType"] # Check if the plugin for arnold is available on the pc try: diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py index cad0900590..3fa490f405 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py @@ -31,10 +31,7 @@ class LoadVDBtoRedShift(load.LoaderPlugin): from ayon_core.hosts.maya.api.pipeline import containerise from ayon_core.hosts.maya.api.lib import unique_namespace - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "vdbcache" + product_type = context["product"]["productType"] # Check if the plugin for redshift is available on the pc try: diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py index 88f62e81a4..7b87c21f38 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -94,10 +94,7 @@ class LoadVDBtoVRay(load.LoaderPlugin): "Path does not exist: %s" % path ) - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "vdbcache" + product_type = context["product"]["productType"] # Ensure V-ray is loaded with the vrayvolumegrid if not cmds.pluginInfo("vrayformaya", query=True, loaded=True): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py index 59d8eadefa..895a4a4127 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py @@ -47,10 +47,7 @@ class VRayProxyLoader(load.LoaderPlugin): """ - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "vrayproxy" + product_type = context["product"]["productType"] # get all representations for this version filename = self._get_abc( diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py index 2f4ab1d080..36a25e2af1 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py @@ -26,10 +26,7 @@ class VRaySceneLoader(load.LoaderPlugin): color = "orange" def load(self, context, name, namespace, data): - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "vrayscene_layer" + product_type = context["product"]["productType"] folder_name = context["folder"]["name"] namespace = namespace or unique_namespace( diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py index 8933c4d8a6..a5cd04b0f4 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py @@ -56,10 +56,7 @@ class YetiCacheLoader(load.LoaderPlugin): """ - try: - product_type = context["representation"]["context"]["family"] - except ValueError: - product_type = "yeticache" + product_type = context["product"]["productType"] # Build namespace folder_name = context["folder"]["name"] From 99e81bf45362275aadf6006d9fd6a701d6244f76 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Mar 2024 17:11:31 +0100 Subject: [PATCH 062/130] Refactor validation logic for editorial asset names - Updated condition to check entity_type using get() method. --- .../ayon_core/plugins/publish/validate_editorial_asset_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py index 9b6794a0c4..33b4210ad5 100644 --- a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py +++ b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py @@ -117,6 +117,6 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): output[folder_path] = [ str(p["entity_name"]) for p in parents - if p["entity_type"].lower() != "project" + if p.get("entity_type") != "project" ] return output From 5a3ad328fba0005c6d579a565df8a3093909a2c3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Mar 2024 17:12:05 +0100 Subject: [PATCH 063/130] Refactor hierarchy data structure for better organization. - Refactored the hierarchy data structure to improve organization and readability. - Updated the way child entities are nested within parent entities. --- .../plugins/publish/collect_hierarchy.py | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 0751bf305b..2ae3cc67f3 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -18,14 +18,13 @@ class CollectHierarchy(pyblish.api.ContextPlugin): def process(self, context): project_name = context.data["projectName"] - temp_context = {} final_context = { project_name: { "entity_type": "project", - "children": temp_context + "children": {} }, } - + temp_context = {} for instance in context: self.log.debug("Processing instance: `{}` ...".format(instance)) @@ -68,21 +67,11 @@ class CollectHierarchy(pyblish.api.ContextPlugin): actual = {name: shot_data} for parent in reversed(instance.data["parents"]): - next_dict = {} - parent_name = parent["entity_name"] - next_dict[parent_name] = {} - next_dict[parent_name]["entity_type"] = "folder" - next_dict[parent_name]["folder_type"] = parent[ - "entity_type"].capitalize() - next_dict[parent_name]["children"] = actual - - for parent in reversed(instance.data["parents"]): - parent_name = parent["entity_name"] next_dict = { - parent_name: { + parent["entity_name"]: { "entity_type": "folder", - "folder_type": parent["entity_type"].capitalize(), - "children": actual + "folder_type": parent["folder_type"], + "children": actual, } } actual = next_dict @@ -93,6 +82,8 @@ class CollectHierarchy(pyblish.api.ContextPlugin): if not temp_context: return + final_context[project_name]["children"] = temp_context + # adding hierarchy context to context context.data["hierarchyContext"] = final_context self.log.debug("context.data[hierarchyContext] is: {}".format( From af6102afbd9e519395d6832afbd0512323a94388 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Mar 2024 17:13:11 +0100 Subject: [PATCH 064/130] Add folder type mapping and handle missing types, improve logging. - Added a dictionary to map folder types by name - Implemented handling for missing folder types with default "Folder" - Enhanced logging for missing folder types and active paths --- .../publish/extract_hierarchy_to_ayon.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index d43dcab28c..60c92aa8b1 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -115,6 +115,10 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): entity_hub = EntityHub(project_name) project = entity_hub.project_entity + folder_type_name_by_low_name = { + folder_type_item["name"].lower(): folder_type_item["name"] + for folder_type_item in project.get_folder_types() + } hierarchy_match_queue = collections.deque() hierarchy_match_queue.append((project, hierarchy_context)) @@ -167,8 +171,18 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): # TODO check if existing entity have 'folder' type child_entity = children_by_low_name.get(child_name.lower()) if child_entity is None: + folder_type = folder_type_name_by_low_name.get( + child_info["folder_type"].lower() + ) + if folder_type is None: + # TODO add validator for folder type validations + self.log.warning(( + "Couldn't find folder type '{}'" + ).format(child_info["folder_type"])) + folder_type = "Folder" + child_entity = entity_hub.add_new_folder( - child_info["entity_type"], + folder_type, parent_id=entity.id, name=child_name ) @@ -223,12 +237,11 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): # filter only the active publishing instances active_folder_paths = set() for instance in context: - if instance.data.get("publish") is not False: + if instance.data.get("publish", True) is not False: active_folder_paths.add(instance.data.get("folderPath")) active_folder_paths.discard(None) - self.log.debug("Active folder paths: {}".format(active_folder_paths)) if not active_folder_paths: return None @@ -271,10 +284,11 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): item_id = uuid.uuid4().hex new_item = copy.deepcopy(folder_info) - new_item["name"] = folder_name - new_item["children"] = [] new_children_context = new_item.pop("children", None) tasks = new_item.pop("tasks", {}) + + new_item["name"] = folder_name + new_item["children"] = [] task_items = [] for task_name, task_info in tasks.items(): task_info["name"] = task_name From cd0a3005d3d2cfc9418c4d32fa1fbac296b4d93b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 26 Mar 2024 17:27:31 +0100 Subject: [PATCH 065/130] Fix - differentiate between host main and main --- client/ayon_core/hosts/aftereffects/api/launch_script.py | 4 ++-- client/ayon_core/hosts/harmony/api/launch_script.py | 4 ++-- client/ayon_core/hosts/photoshop/api/launch_script.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/api/launch_script.py b/client/ayon_core/hosts/aftereffects/api/launch_script.py index ad4e779bd0..87926c022b 100644 --- a/client/ayon_core/hosts/aftereffects/api/launch_script.py +++ b/client/ayon_core/hosts/aftereffects/api/launch_script.py @@ -8,7 +8,7 @@ workfile or others. import os import sys -from ayon_core.hosts.aftereffects.api.launch_logic import main +from ayon_core.hosts.aftereffects.api.launch_logic import main as host_main # Get current file to locate start point of sys.argv CURRENT_FILE = os.path.abspath(__file__) @@ -83,7 +83,7 @@ def main(argv): if launch_args: # Launch host implementation - main(*launch_args) + host_main(*launch_args) else: # Show message box on_invalid_args(after_script_idx is None) diff --git a/client/ayon_core/hosts/harmony/api/launch_script.py b/client/ayon_core/hosts/harmony/api/launch_script.py index 1f2c36b7e6..3c809e210f 100644 --- a/client/ayon_core/hosts/harmony/api/launch_script.py +++ b/client/ayon_core/hosts/harmony/api/launch_script.py @@ -8,7 +8,7 @@ workfile or others. import os import sys -from ayon_core.hosts.harmony.api.lib import main +from ayon_core.hosts.harmony.api.lib import main as host_main # Get current file to locate start point of sys.argv CURRENT_FILE = os.path.abspath(__file__) @@ -83,7 +83,7 @@ def main(argv): if launch_args: # Launch host implementation - main(*launch_args) + host_main(*launch_args) else: # Show message box on_invalid_args(after_script_idx is None) diff --git a/client/ayon_core/hosts/photoshop/api/launch_script.py b/client/ayon_core/hosts/photoshop/api/launch_script.py index c036b63c46..bb4de80086 100644 --- a/client/ayon_core/hosts/photoshop/api/launch_script.py +++ b/client/ayon_core/hosts/photoshop/api/launch_script.py @@ -8,7 +8,7 @@ workfile or others. import os import sys -from ayon_core.hosts.photoshop.api.lib import main +from ayon_core.hosts.photoshop.api.lib import main as host_main # Get current file to locate start point of sys.argv CURRENT_FILE = os.path.abspath(__file__) @@ -83,7 +83,7 @@ def main(argv): if launch_args: # Launch host implementation - main(*launch_args) + host_main(*launch_args) else: # Show message box on_invalid_args(after_script_idx is None) From 27f546dc35e44b778a0e2800ebc2a16a413e4ee8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 17:52:46 +0100 Subject: [PATCH 066/130] Remove "This is a bug" --- .../ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py b/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py index 47006ca9de..0288d4b865 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py @@ -21,7 +21,7 @@ class ValidateVrayProxy(pyblish.api.InstancePlugin, return if not data["setMembers"]: raise PublishValidationError( - "'%s' is empty! This is a bug" % instance.name + f"Instance '{instance.name}' is empty." ) if data["animation"]: From 243df68ea2cf72fa7472d5c45dfe473f4ece57ea Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 18:06:46 +0100 Subject: [PATCH 067/130] Houdini: Prompt reset scene context on saving to another task --- client/ayon_core/hosts/houdini/api/lib.py | 123 +++++++++++++++--- .../ayon_core/hosts/houdini/api/pipeline.py | 21 +++ 2 files changed, 125 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index 681052a44d..6a314d097d 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -526,7 +526,7 @@ def maintained_selection(): node.setSelected(on=True) -def reset_framerange(): +def reset_framerange(fps=True, frame_range=True): """Set frame range and FPS to current folder.""" project_name = get_current_project_name() @@ -535,29 +535,32 @@ def reset_framerange(): folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) folder_attributes = folder_entity["attrib"] - # Get FPS - fps = get_folder_fps(folder_entity) + # Set FPS + if fps: + fps = get_folder_fps(folder_entity) + print("Setting scene FPS to {}".format(int(fps))) + set_scene_fps(fps) - # Get Start and End Frames - frame_start = folder_attributes.get("frameStart") - frame_end = folder_attributes.get("frameEnd") + if frame_range: - if frame_start is None or frame_end is None: - log.warning("No edit information found for '{}'".format(folder_path)) - return + # Set Start and End Frames + frame_start = folder_attributes.get("frameStart") + frame_end = folder_attributes.get("frameEnd") - handle_start = folder_attributes.get("handleStart", 0) - handle_end = folder_attributes.get("handleEnd", 0) + if frame_start is None or frame_end is None: + log.warning("No edit information found for '%s'", folder_path) + return - frame_start -= int(handle_start) - frame_end += int(handle_end) + handle_start = folder_attributes.get("handleStart", 0) + handle_end = folder_attributes.get("handleEnd", 0) - # Set frame range and FPS - print("Setting scene FPS to {}".format(int(fps))) - set_scene_fps(fps) - hou.playbar.setFrameRange(frame_start, frame_end) - hou.playbar.setPlaybackRange(frame_start, frame_end) - hou.setFrame(frame_start) + frame_start -= int(handle_start) + frame_end += int(handle_end) + + # Set frame range and FPS + hou.playbar.setFrameRange(frame_start, frame_end) + hou.playbar.setPlaybackRange(frame_start, frame_end) + hou.setFrame(frame_start) def get_main_window(): @@ -1072,3 +1075,85 @@ def add_self_publish_button(node): template = node.parmTemplateGroup() template.insertBefore((0,), button_parm) node.setParmTemplateGroup(template) + + +def update_content_on_context_change(): + """Update all Creator instances to current asset""" + host = registered_host() + context = host.get_current_context() + + folder_path = context["folder_path"] + task = context["task_name"] + + create_context = CreateContext(host, reset=True) + + for instance in create_context.instances: + instance_folder_path = instance.get("folderPath") + if instance_folder_path and instance_folder_path != folder_path: + instance["folderPath"] = folder_path + instance_task = instance.get("task") + if instance_task and instance_task != task: + instance["task"] = task + + create_context.save_changes() + + +def prompt_reset_context(): + """Prompt the user what context settings to reset. + This prompt is used on saving to a different task to allow the scene to + get matched to the new context. + """ + # TODO: Cleanup this prototyped mess of imports and odd dialog + from ayon_core.tools.attribute_defs.dialog import ( + AttributeDefinitionsDialog + ) + from ayon_core.style import load_stylesheet + from ayon_core.lib import BoolDef, UILabelDef + + definitions = [ + UILabelDef( + label=( + "You are saving your scene into a different task." + "\n\n" + "Would you like to reset some settings for the " + "for the new context?\n" + ) + ), + BoolDef( + "fps", + label="FPS", + tooltip="Reset workfile FPS", + default=True + ), + BoolDef( + "frame_range", + label="Frame Range", + tooltip="Reset workfile start and end frame ranges", + default=True + ), + BoolDef( + "instances", + label="Publish instances", + tooltip="Update all publish instance's folder and task to match " + "the new folder and task", + default=True + ), + ] + + dialog = AttributeDefinitionsDialog(definitions) + dialog.setWindowTitle("Saving to different context. Reset options") + dialog.setStyleSheet(load_stylesheet()) + if not dialog.exec_(): + return None + + options = dialog.get_values() + if options["fps"] or options["frame_range"]: + reset_framerange( + fps=options["fps"], + frame_range=options["frame_range"] + ) + + if options["instances"]: + update_content_on_context_change() + + dialog.deleteLater() \ No newline at end of file diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/client/ayon_core/hosts/houdini/api/pipeline.py index d5144200cf..bad23f8db5 100644 --- a/client/ayon_core/hosts/houdini/api/pipeline.py +++ b/client/ayon_core/hosts/houdini/api/pipeline.py @@ -39,6 +39,9 @@ LOAD_PATH = os.path.join(PLUGINS_DIR, "load") CREATE_PATH = os.path.join(PLUGINS_DIR, "create") INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") +# Track whether the workfile tool is about to save +ABOUT_TO_SAVE = False + class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): name = "houdini" @@ -61,10 +64,12 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): log.info("Installing callbacks ... ") # register_event_callback("init", on_init) self._register_callbacks() + register_event_callback("workfile.save.before", before_workfile_save) register_event_callback("before.save", before_save) register_event_callback("save", on_save) register_event_callback("open", on_open) register_event_callback("new", on_new) + register_event_callback("taskChanged", on_task_changed) self._has_been_setup = True @@ -287,6 +292,11 @@ def ls(): yield parse_container(container) +def before_workfile_save(event): + global ABOUT_TO_SAVE + ABOUT_TO_SAVE = True + + def before_save(): return lib.validate_fps() @@ -302,6 +312,17 @@ def on_save(): for node, new_id in lib.generate_ids(nodes): lib.set_id(node, new_id, overwrite=False) + # We are now starting the actual save directly + global ABOUT_TO_SAVE + ABOUT_TO_SAVE = False + + +def on_task_changed(): + global ABOUT_TO_SAVE + if not IS_HEADLESS and ABOUT_TO_SAVE: + # Let's prompt the user to update the context settings or not + lib.prompt_reset_context() + def _show_outdated_content_popup(): # Get main window From b1ba751f3a15a6a2e6c1602fc6244c93cc3348fb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 18:11:06 +0100 Subject: [PATCH 068/130] Remove legacy unused id logic in Houdini - this was never used --- client/ayon_core/hosts/houdini/api/lib.py | 78 ------------------- .../ayon_core/hosts/houdini/api/pipeline.py | 4 - 2 files changed, 82 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index 681052a44d..673183a15d 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -44,84 +44,6 @@ def get_folder_fps(folder_entity=None): return folder_entity["attrib"]["fps"] -def set_id(node, unique_id, overwrite=False): - exists = node.parm("id") - if not exists: - imprint(node, {"id": unique_id}) - - if not exists and overwrite: - node.setParm("id", unique_id) - - -def get_id(node): - """Get the `cbId` attribute of the given node. - - Args: - node (hou.Node): the name of the node to retrieve the attribute from - - Returns: - str: cbId attribute of the node. - - """ - - if node is not None: - return node.parm("id") - - -def generate_ids(nodes, folder_id=None): - """Returns new unique ids for the given nodes. - - Note: This does not assign the new ids, it only generates the values. - - To assign new ids using this method: - >>> nodes = ["a", "b", "c"] - >>> for node, id in generate_ids(nodes): - >>> set_id(node, id) - - To also override any existing values (and assign regenerated ids): - >>> nodes = ["a", "b", "c"] - >>> for node, id in generate_ids(nodes): - >>> set_id(node, id, overwrite=True) - - Args: - nodes (list): List of nodes. - folder_id (str): Folder id . Use current folder id if is ``None``. - - Returns: - list: A list of (node, id) tuples. - - """ - - if folder_id is None: - project_name = get_current_project_name() - folder_path = get_current_folder_path() - # Get folder id of current context folder - folder_entity = ayon_api.get_folder_by_path( - project_name, folder_path, fields={"id"} - ) - if not folder_entity: - raise ValueError("No current folder is set.") - - folder_id = folder_entity["id"] - - node_ids = [] - for node in nodes: - _, uid = str(uuid.uuid4()).rsplit("-", 1) - unique_id = "{}:{}".format(folder_id, uid) - node_ids.append((node, unique_id)) - - return node_ids - - -def get_id_required_nodes(): - - valid_types = ["geometry"] - nodes = {n for n in hou.node("/out").children() if - n.type().name() in valid_types} - - return list(nodes) - - def get_output_parameter(node): """Return the render output parameter of the given node diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/client/ayon_core/hosts/houdini/api/pipeline.py index d5144200cf..3439bdea0a 100644 --- a/client/ayon_core/hosts/houdini/api/pipeline.py +++ b/client/ayon_core/hosts/houdini/api/pipeline.py @@ -298,10 +298,6 @@ def on_save(): # update houdini vars lib.update_houdini_vars_context_dialog() - nodes = lib.get_id_required_nodes() - for node, new_id in lib.generate_ids(nodes): - lib.set_id(node, new_id, overwrite=False) - def _show_outdated_content_popup(): # Get main window From c0cf9de455e4ec1153248415bee46aa5da9fd362 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Mar 2024 18:13:54 +0100 Subject: [PATCH 069/130] Fix correctly updating context data on saving in publisher --- client/ayon_core/hosts/houdini/api/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/client/ayon_core/hosts/houdini/api/pipeline.py index d5144200cf..dd635cfd48 100644 --- a/client/ayon_core/hosts/houdini/api/pipeline.py +++ b/client/ayon_core/hosts/houdini/api/pipeline.py @@ -166,7 +166,7 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): if not op_ctx: op_ctx = self.create_context_node() - lib.imprint(op_ctx, data) + lib.imprint(op_ctx, data, update=True) def get_context_data(self): op_ctx = hou.node(CONTEXT_CONTAINER) From df1d0d4a864fce397ba02d2f009666e1e09ef2cf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 26 Mar 2024 18:32:55 +0100 Subject: [PATCH 070/130] fix unreal load plugins too --- .../hosts/unreal/plugins/load/load_alembic_animation.py | 2 +- client/ayon_core/hosts/unreal/plugins/load/load_layout.py | 2 +- .../ayon_core/hosts/unreal/plugins/load/load_layout_existing.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py index 02259b706c..64d684939c 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_alembic_animation.py @@ -72,7 +72,7 @@ class AnimationAlembicLoader(plugin.Loader): root = unreal_pipeline.AYON_ASSET_DIR folder_name = context["folder"]["name"] folder_path = context["folder"]["path"] - product_type = context["representation"]["context"]["family"] + product_type = context["product"]["productType"] suffix = "_CON" if folder_name: asset_name = "{}_{}".format(folder_name, name) diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py index 6c667d3d2f..6c01925453 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout.py @@ -659,7 +659,7 @@ class LayoutLoader(plugin.Loader): "loader": str(self.__class__.__name__), "representation": context["representation"]["id"], "parent": context["representation"]["versionId"], - "family": context["representation"]["context"]["family"], + "family": context["product"]["productType"], "loaded_assets": loaded_assets } imprint( diff --git a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py index 700b6957a2..56e36f6185 100644 --- a/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py +++ b/client/ayon_core/hosts/unreal/plugins/load/load_layout_existing.py @@ -393,7 +393,7 @@ class ExistingLayoutLoader(plugin.Loader): folder_name = context["folder"]["name"] folder_path = context["folder"]["path"] - product_type = context["representation"]["context"]["family"] + product_type = context["product"]["productType"] asset_name = f"{folder_name}_{name}" if folder_name else name container_name = f"{folder_name}_{name}_CON" From eadfc1542ba65afc711b96c35c3e8a8be322c8ed Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Mar 2024 22:42:29 +0100 Subject: [PATCH 071/130] Update families data key to use the correct value from the data dictionary. This change ensures accurate information is passed for processing. --- client/ayon_core/hosts/hiero/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index 1115eab9d3..4d228ac3a2 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -906,7 +906,7 @@ class PublishClip: "hierarchyData": hierarchy_formatting_data, "productName": self.product_name, "productType": self.product_type, - "families": [self.product_type, self.data["family"]] + "families": [self.product_type, self.data["productType"]] } def _convert_to_entity(self, src_type, template): From d1cc880968da13e900649ee223df2cef99cbfc8c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Mar 2024 23:23:31 +0100 Subject: [PATCH 072/130] Refactor folder type to lowercase in ShotMetadataSolver. Adjusted parent_token_type to lowercase for consistency. --- client/ayon_core/hosts/traypublisher/api/editorial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/client/ayon_core/hosts/traypublisher/api/editorial.py index 92a7b315f8..09a2ab17ac 100644 --- a/client/ayon_core/hosts/traypublisher/api/editorial.py +++ b/client/ayon_core/hosts/traypublisher/api/editorial.py @@ -186,7 +186,7 @@ class ShotMetadataSolver: # in case first parent is project then start parents from start if ( _index == 0 - and parent_token_type == "project" + and parent_token_type == "Project" ): project_parent = parents[0] parents = [project_parent] @@ -194,7 +194,7 @@ class ShotMetadataSolver: parents.append({ "entity_type": "folder", - "folder_type": parent_token_type, + "folder_type": parent_token_type.lower(), "entity_name": parent_name }) From 6c9794c78926bbff169de1d38f30ef36223a83b0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:03:51 +0100 Subject: [PATCH 073/130] Raise PublishValidationError --- .../plugins/publish/validate_yeti_renderscript_callbacks.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py b/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py index 35b2443718..086cb7b1f5 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py @@ -3,9 +3,11 @@ from maya import cmds import pyblish.api from ayon_core.pipeline.publish import ( ValidateContentsOrder, + PublishValidationError, OptionalPyblishPluginMixin ) + class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Check if the render script callbacks will be used during the rendering @@ -45,8 +47,8 @@ class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin, return invalid = self.get_invalid(instance) if invalid: - raise ValueError("Invalid render callbacks found for '%s'!" - % instance.name) + raise PublishValidationError( + f"Invalid render callbacks found for '{instance.name}'.") @classmethod def get_invalid(cls, instance): From b940cabfaf96b3e79d9211b398a5ba76599299c2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:07:19 +0100 Subject: [PATCH 074/130] Change more validations to PublishValidationError --- .../maya/plugins/publish/validate_instance_subset.py | 6 +++--- .../hosts/maya/plugins/publish/validate_setdress_root.py | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py index da3a194e58..df9ca0bf13 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py @@ -36,18 +36,18 @@ class ValidateSubsetName(pyblish.api.InstancePlugin): ) if not isinstance(product_name, six.string_types): - raise TypeError(( + raise PublishValidationError(( "Instance product name must be string, got: {0} ({1})" ).format(product_name, type(product_name))) # Ensure is not empty product if not product_name: - raise ValueError( + raise PublishValidationError( "Instance product name is empty: {0}".format(product_name) ) # Validate product characters if not validate_name(product_name): - raise ValueError(( + raise PublishValidationError(( "Instance product name contains invalid characters: {0}" ).format(product_name)) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_setdress_root.py b/client/ayon_core/hosts/maya/plugins/publish/validate_setdress_root.py index 906f6fbd1a..f88e33fdfb 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_setdress_root.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_setdress_root.py @@ -1,5 +1,8 @@ import pyblish.api -from ayon_core.pipeline.publish import ValidateContentsOrder +from ayon_core.pipeline.publish import ( + ValidateContentsOrder, + PublishValidationError +) class ValidateSetdressRoot(pyblish.api.InstancePlugin): @@ -20,4 +23,6 @@ class ValidateSetdressRoot(pyblish.api.InstancePlugin): root = cmds.ls(set_member, assemblies=True, long=True) if not root or root[0] not in set_member: - raise Exception("Setdress top root node is not being published.") + raise PublishValidationError( + "Setdress top root node is not being published." + ) From 5aff2a6fc1b79c99f21bbbfe33b36a9abbaf603c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:14:53 +0100 Subject: [PATCH 075/130] Tweak dialog wording to match #259 --- client/ayon_core/hosts/houdini/api/lib.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index 6a314d097d..e509e2c166 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -1113,10 +1113,9 @@ def prompt_reset_context(): definitions = [ UILabelDef( label=( - "You are saving your scene into a different task." + "You are saving your workfile into a different folder or task." "\n\n" - "Would you like to reset some settings for the " - "for the new context?\n" + "Would you like to update some settings to the new context?\n" ) ), BoolDef( @@ -1141,7 +1140,7 @@ def prompt_reset_context(): ] dialog = AttributeDefinitionsDialog(definitions) - dialog.setWindowTitle("Saving to different context. Reset options") + dialog.setWindowTitle("Saving to different context.") dialog.setStyleSheet(load_stylesheet()) if not dialog.exec_(): return None From c152e459da51638ab0f11434ce7627cdea5735e8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:23:04 +0100 Subject: [PATCH 076/130] Change assert to `PublishValidationError` --- .../plugins/publish/validate_single_assembly.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_single_assembly.py b/client/ayon_core/hosts/maya/plugins/publish/validate_single_assembly.py index 1987f93e32..f5d73553d3 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_single_assembly.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_single_assembly.py @@ -1,5 +1,8 @@ import pyblish.api -from ayon_core.pipeline.publish import ValidateContentsOrder +from ayon_core.pipeline.publish import ( + ValidateContentsOrder, + PublishValidationError +) class ValidateSingleAssembly(pyblish.api.InstancePlugin): @@ -30,7 +33,11 @@ class ValidateSingleAssembly(pyblish.api.InstancePlugin): # ensure unique (somehow `maya.cmds.ls` doesn't manage that) assemblies = set(assemblies) - assert len(assemblies) > 0, ( - "One assembly required for: %s (currently empty?)" % instance) - assert len(assemblies) < 2, ( - 'Multiple assemblies found: %s' % assemblies) + if len(assemblies) == 0: + raise PublishValidationError( + "One assembly required for: %s (currently empty?)" % instance + ) + elif len(assemblies) > 1: + raise PublishValidationError( + 'Multiple assemblies found: %s' % assemblies + ) From 38d4a3231a8cecd43cb2044afe2161755ccbd7f8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:23:46 +0100 Subject: [PATCH 077/130] Change assert to `PublishValidationError` --- .../publish/validate_unreal_mesh_triangulated.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py index 101bd5bf04..6440c00eae 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py @@ -5,7 +5,8 @@ import pyblish.api from ayon_core.pipeline.publish import ( ValidateMeshOrder, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) import ayon_core.hosts.maya.api.action @@ -26,8 +27,8 @@ class ValidateUnrealMeshTriangulated(pyblish.api.InstancePlugin, invalid = [] meshes = cmds.ls(instance, type="mesh", long=True) for mesh in meshes: - faces = cmds.polyEvaluate(mesh, f=True) - tris = cmds.polyEvaluate(mesh, t=True) + faces = cmds.polyEvaluate(mesh, face=True) + tris = cmds.polyEvaluate(mesh, triangle=True) if faces != tris: invalid.append(mesh) @@ -37,5 +38,5 @@ class ValidateUnrealMeshTriangulated(pyblish.api.InstancePlugin, if not self.is_active(instance.data): return invalid = self.get_invalid(instance) - assert len(invalid) == 0, ( - "Found meshes without triangles") + if invalid: + raise PublishValidationError("Found meshes without triangles") From 11e0df6a2af38941ad61c4488bafe2b4736faecf Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:24:15 +0100 Subject: [PATCH 078/130] Change assert to `PublishValidationError` --- .../maya/plugins/publish/validate_unreal_up_axis.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_up_axis.py b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_up_axis.py index ef7296e628..f7acd41cea 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_up_axis.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_up_axis.py @@ -6,7 +6,8 @@ import pyblish.api from ayon_core.pipeline.publish import ( ValidateContentsOrder, RepairAction, - OptionalPyblishPluginMixin + OptionalPyblishPluginMixin, + PublishValidationError ) @@ -26,9 +27,10 @@ class ValidateUnrealUpAxis(pyblish.api.ContextPlugin, if not self.is_active(context.data): return - assert cmds.upAxis(q=True, axis=True) == "z", ( - "Invalid axis set as up axis" - ) + if cmds.upAxis(q=True, axis=True) != "z": + raise PublishValidationError( + "Invalid axis set as up axis" + ) @classmethod def repair(cls, instance): From 2b175b806ea51da2d371f7dda5212b0e8c891d37 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:25:08 +0100 Subject: [PATCH 079/130] Cosmetics --- .../hosts/maya/plugins/publish/validate_visible_only.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py b/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py index af6c9a64c6..1fdb476dba 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py @@ -34,8 +34,9 @@ class ValidateAlembicVisibleOnly(pyblish.api.InstancePlugin, invalid = self.get_invalid(instance) if invalid: start, end = self.get_frame_range(instance) - raise PublishValidationError("No visible nodes found in " - "frame range {}-{}.".format(start, end)) + raise PublishValidationError( + f"No visible nodes found in frame range {start}-{end}." + ) @classmethod def get_invalid(cls, instance): From 5fd209970c1ea8000474f1063de89ee0eb5f7cf8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:26:36 +0100 Subject: [PATCH 080/130] Change assert to `KnownPublishError` --- .../publish/validate_vray_distributed_rendering.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py b/client/ayon_core/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py index b35508d635..b3978b8483 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py @@ -3,6 +3,7 @@ from maya import cmds from ayon_core.hosts.maya.api import lib from ayon_core.pipeline.publish import ( + KnownPublishError, PublishValidationError, RepairAction, ValidateContentsOrder, @@ -35,11 +36,14 @@ class ValidateVRayDistributedRendering(pyblish.api.InstancePlugin, if not self.is_active(instance.data): return if instance.data.get("renderer") != "vray": - # If not V-Ray ignore.. + # If not V-Ray, ignore return vray_settings = cmds.ls("vraySettings", type="VRaySettingsNode") - assert vray_settings, "Please ensure a VRay Settings Node is present" + if not vray_settings: + raise KnownPublishError( + "Please ensure a VRay Settings Node is present" + ) renderlayer = instance.data['renderlayer'] @@ -51,8 +55,8 @@ class ValidateVRayDistributedRendering(pyblish.api.InstancePlugin, # during batch mode we invalidate the instance if not lib.get_attr_in_layer(self.ignored_attr, layer=renderlayer): raise PublishValidationError( - ("Renderlayer has distributed rendering enabled " - "but is not set to ignore in batch mode.")) + "Renderlayer has distributed rendering enabled " + "but is not set to ignore in batch mode.") @classmethod def repair(cls, instance): From 3d3ef75a0f3c511dc8e5a63f0c34c169edb9ea2e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:29:58 +0100 Subject: [PATCH 081/130] Add docstring as description --- .../maya/plugins/publish/validate_yeti_rig_cache_state.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py b/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py index d81534192a..84614fc0be 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py @@ -1,3 +1,5 @@ +import inspect + import pyblish.api import maya.cmds as cmds import ayon_core.hosts.maya.api.action @@ -8,7 +10,6 @@ from ayon_core.pipeline.publish import ( ) - class ValidateYetiRigCacheState(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate the I/O attributes of the node @@ -32,7 +33,10 @@ class ValidateYetiRigCacheState(pyblish.api.InstancePlugin, return invalid = self.get_invalid(instance) if invalid: - raise PublishValidationError("Nodes have incorrect I/O settings") + raise PublishValidationError( + "Nodes have incorrect I/O settings", + description=inspect.getdoc(self) + ) @classmethod def get_invalid(cls, instance): From d69ba8395c4898ddfdda157b5c436a3d10139db9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 00:50:40 +0100 Subject: [PATCH 082/130] Fix initial state for `CachedData.remapping` --- client/ayon_core/pipeline/colorspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 034c90d27b..efa3bbf968 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -23,7 +23,7 @@ log = Logger.get_logger(__name__) class CachedData: - remapping = None + remapping = {} has_compatible_ocio_package = None config_version_data = {} ocio_config_colorspaces = {} From f6cf5e06514ff347ce03c323dc5fcc6b655ffbae Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 01:06:52 +0100 Subject: [PATCH 083/130] Remove unused import --- client/ayon_core/hosts/houdini/api/lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index 673183a15d..b395a06dc5 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -3,7 +3,6 @@ import sys import os import errno import re -import uuid import logging import json from contextlib import contextmanager From 0b36cbc5ec65e995ae6f82ee0044fce37968bada Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 09:15:15 +0100 Subject: [PATCH 084/130] Remove deprecated `AVALON_ACTIONS` --- client/ayon_core/modules/launcher_action.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/ayon_core/modules/launcher_action.py b/client/ayon_core/modules/launcher_action.py index 1faf6ef4b1..38e88d36ca 100644 --- a/client/ayon_core/modules/launcher_action.py +++ b/client/ayon_core/modules/launcher_action.py @@ -37,20 +37,6 @@ class LauncherAction(AYONAddon, ITrayAction): if path and os.path.exists(path): register_launcher_action_path(path) - paths_str = os.environ.get("AVALON_ACTIONS") or "" - if paths_str: - self.log.warning( - "WARNING: 'AVALON_ACTIONS' is deprecated. Support of this" - " environment variable will be removed in future versions." - " Please consider using 'OpenPypeModule' to define custom" - " action paths. Planned version to drop the support" - " is 3.17.2 or 3.18.0 ." - ) - - for path in paths_str.split(os.pathsep): - if path and os.path.exists(path): - register_launcher_action_path(path) - def on_action_trigger(self): """Implementation for ITrayAction interface. From 908e17430cb645ca6d2773b294cc02ea387063d3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 11:46:07 +0100 Subject: [PATCH 085/130] fix typos in codebase --- client/ayon_core/addon/base.py | 4 ++-- client/ayon_core/hosts/blender/api/ops.py | 2 +- .../ayon_core/hosts/blender/plugins/load/load_blend.py | 2 +- .../blender/plugins/publish/validate_deadline_publish.py | 2 +- .../hosts/celaction/hooks/pre_celaction_setup.py | 2 +- .../celaction/plugins/publish/collect_render_path.py | 2 +- client/ayon_core/hosts/flame/api/lib.py | 4 ++-- client/ayon_core/hosts/flame/api/plugin.py | 2 +- client/ayon_core/hosts/flame/otio/utils.py | 2 +- .../hosts/flame/plugins/create/create_shot_clip.py | 4 ++-- .../flame/plugins/publish/collect_timeline_instances.py | 2 +- .../startup/openpype_babypublisher/modules/ftrack_lib.py | 2 +- client/ayon_core/hosts/harmony/api/lib.py | 2 +- client/ayon_core/hosts/hiero/api/lib.py | 8 ++++---- client/ayon_core/hosts/hiero/api/otio/hiero_import.py | 2 +- client/ayon_core/hosts/hiero/api/otio/utils.py | 2 +- .../startup/Python/StartupUI/otioimporter/OTIOImport.py | 2 +- .../hosts/hiero/plugins/create/create_shot_clip.py | 4 ++-- .../hosts/hiero/plugins/publish/precollect_instances.py | 8 ++++---- client/ayon_core/hosts/houdini/api/colorspace.py | 2 +- client/ayon_core/hosts/maya/api/lib.py | 6 +++--- .../hosts/maya/plugins/publish/collect_render.py | 2 +- .../hosts/maya/plugins/publish/validate_xgen.py | 2 +- client/ayon_core/hosts/nuke/api/lib.py | 4 ++-- client/ayon_core/hosts/nuke/api/plugin.py | 4 ++-- .../photoshop/plugins/load/load_image_from_sequence.py | 2 +- client/ayon_core/hosts/resolve/api/lib.py | 2 +- client/ayon_core/hosts/resolve/otio/utils.py | 2 +- .../hosts/resolve/plugins/create/create_shot_clip.py | 4 ++-- .../plugins/publish/validate_frame_ranges.py | 2 +- client/ayon_core/hosts/unreal/api/tools_ui.py | 2 +- client/ayon_core/lib/local_settings.py | 2 +- client/ayon_core/lib/path_templates.py | 2 +- .../plugins/publish/create_publish_royalrender_job.py | 2 +- client/ayon_core/pipeline/create/context.py | 2 +- client/ayon_core/pipeline/create/creator_plugins.py | 9 +++++---- client/ayon_core/pipeline/editorial.py | 2 +- client/ayon_core/pipeline/load/plugins.py | 2 +- .../pipeline/workfile/workfile_template_builder.py | 2 +- .../plugins/publish/extract_otio_audio_tracks.py | 2 +- client/ayon_core/tools/common_models/thumbnails.py | 2 +- client/ayon_core/tools/pyblish_pype/util.py | 2 +- server/settings/publish_plugins.py | 2 +- 43 files changed, 62 insertions(+), 61 deletions(-) diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index 42b53c59e3..6bac25b8ac 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -1075,7 +1075,7 @@ class AddonsManager: """Print out report of time spent on addons initialization parts. Reporting is not automated must be implemented for each initialization - part separatelly. Reports must be stored to `_report` attribute. + part separately. Reports must be stored to `_report` attribute. Print is skipped if `_report` is empty. Attribute `_report` is dictionary where key is "label" describing @@ -1267,7 +1267,7 @@ class TrayAddonsManager(AddonsManager): def add_doubleclick_callback(self, addon, callback): """Register doubleclick callbacks on tray icon. - Currently there is no way how to determine which is launched. Name of + Currently, there is no way how to determine which is launched. Name of callback can be defined with `doubleclick_callback` attribute. Missing feature how to define default callback. diff --git a/client/ayon_core/hosts/blender/api/ops.py b/client/ayon_core/hosts/blender/api/ops.py index d71ee6faf5..c03ec98d0c 100644 --- a/client/ayon_core/hosts/blender/api/ops.py +++ b/client/ayon_core/hosts/blender/api/ops.py @@ -191,7 +191,7 @@ def _process_app_events() -> Optional[float]: class LaunchQtApp(bpy.types.Operator): - """A Base class for opertors to launch a Qt app.""" + """A Base class for operators to launch a Qt app.""" _app: QtWidgets.QApplication _window = Union[QtWidgets.QDialog, ModuleType] diff --git a/client/ayon_core/hosts/blender/plugins/load/load_blend.py b/client/ayon_core/hosts/blender/plugins/load/load_blend.py index e84dddc88f..1984193a30 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_blend.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_blend.py @@ -227,7 +227,7 @@ class BlendLoader(plugin.AssetLoader): obj.animation_data_create() obj.animation_data.action = actions[obj.name] - # Restore the old data, but reset memebers, as they don't exist anymore + # Restore the old data, but reset members, as they don't exist anymore # This avoids a crash, because the memory addresses of those members # are not valid anymore old_data["members"] = [] diff --git a/client/ayon_core/hosts/blender/plugins/publish/validate_deadline_publish.py b/client/ayon_core/hosts/blender/plugins/publish/validate_deadline_publish.py index b37db44cd4..a86e73ba81 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/validate_deadline_publish.py +++ b/client/ayon_core/hosts/blender/plugins/publish/validate_deadline_publish.py @@ -32,7 +32,7 @@ class ValidateDeadlinePublish(pyblish.api.InstancePlugin, tree = bpy.context.scene.node_tree output_type = "CompositorNodeOutputFile" output_node = None - # Remove all output nodes that inlcude "AYON" in the name. + # Remove all output nodes that include "AYON" in the name. # There should be only one. for node in tree.nodes: if node.bl_idname == output_type and "AYON" in node.name: diff --git a/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py b/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py index 73b368e4e3..d94fff8f2b 100644 --- a/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py +++ b/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py @@ -118,7 +118,7 @@ class CelactionPrelaunchHook(PreLaunchHook): def workfile_path(self): workfile_path = self.data["last_workfile_path"] - # copy workfile from template if doesnt exist any on path + # copy workfile from template if doesn't exist any on path if not os.path.exists(workfile_path): # TODO add ability to set different template workfile path via # settings diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py b/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py index 52bb183663..1bb4d54831 100644 --- a/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py +++ b/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py @@ -38,7 +38,7 @@ class CollectRenderPath(pyblish.api.InstancePlugin): render_path = r_template_item["path"].format_strict(anatomy_data) self.log.debug("__ render_path: `{}`".format(render_path)) - # create dir if it doesnt exists + # create dir if it doesn't exists try: if not os.path.isdir(render_dir): os.makedirs(render_dir, exist_ok=True) diff --git a/client/ayon_core/hosts/flame/api/lib.py b/client/ayon_core/hosts/flame/api/lib.py index efa23fe01e..e1316658bf 100644 --- a/client/ayon_core/hosts/flame/api/lib.py +++ b/client/ayon_core/hosts/flame/api/lib.py @@ -615,7 +615,7 @@ def get_reformated_filename(filename, padded=True): filename (str): file name Returns: - type: string with reformated path + type: string with reformatted path Example: get_reformated_filename("plate.1001.exr") > plate.%04d.exr @@ -980,7 +980,7 @@ class MediaInfoFile(object): @property def file_pattern(self): - """Clips file patter + """Clips file pattern. Returns: str: file pattern. ex. file.[1-2].exr diff --git a/client/ayon_core/hosts/flame/api/plugin.py b/client/ayon_core/hosts/flame/api/plugin.py index c57d021c69..35fe1b351d 100644 --- a/client/ayon_core/hosts/flame/api/plugin.py +++ b/client/ayon_core/hosts/flame/api/plugin.py @@ -1018,7 +1018,7 @@ class OpenClipSolver(flib.MediaInfoFile): self.feed_version_name)) else: self.log.debug("adding new track element ..") - # create new track as it doesnt exists yet + # create new track as it doesn't exist yet # set current version to feeds on tmp tmp_xml_feeds = tmp_xml_track.find('feeds') tmp_xml_feeds.set('currentVersion', self.feed_version_name) diff --git a/client/ayon_core/hosts/flame/otio/utils.py b/client/ayon_core/hosts/flame/otio/utils.py index 7ded8e55d8..a1206b6710 100644 --- a/client/ayon_core/hosts/flame/otio/utils.py +++ b/client/ayon_core/hosts/flame/otio/utils.py @@ -29,7 +29,7 @@ def get_reformated_filename(filename, padded=True): filename (str): file name Returns: - type: string with reformated path + type: string with reformatted path Example: get_reformated_filename("plate.1001.exr") > plate.%04d.exr diff --git a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py b/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py index e8eb2b9fab..56f5319f21 100644 --- a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py +++ b/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py @@ -17,7 +17,7 @@ class CreateShotClip(opfapi.Creator): presets = deepcopy(self.presets) gui_inputs = self.get_gui_inputs() - # get key pares from presets and match it on ui inputs + # get key pairs from presets and match it on ui inputs for k, v in gui_inputs.items(): if v["type"] in ("dict", "section"): # nested dictionary (only one level allowed @@ -236,7 +236,7 @@ class CreateShotClip(opfapi.Creator): "type": "QCheckBox", "label": "Source resolution", "target": "tag", - "toolTip": "Is resloution taken from timeline or source?", # noqa + "toolTip": "Is resolution taken from timeline or source?", # noqa "order": 4}, } }, diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py index 9d6560023c..c0dea0b891 100644 --- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -37,7 +37,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): self.otio_timeline = context.data["otioTimeline"] self.fps = context.data["fps"] - # process all sellected + # process all selected for segment in selected_segments: # get openpype tag data marker_data = opfapi.get_segment_data_marker(segment) diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/ftrack_lib.py b/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/ftrack_lib.py index 0e84a5ef52..a66980493e 100644 --- a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/ftrack_lib.py +++ b/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/ftrack_lib.py @@ -396,7 +396,7 @@ class FtrackEntityOperator: entity = session.query(query).first() - # if entity doesnt exist then create one + # if entity doesn't exist then create one if not entity: entity = self.create_ftrack_entity( session, diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index 3c833c7b69..f9980cb65e 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -568,7 +568,7 @@ def save_scene(): """Save the Harmony scene safely. The built-in (to Avalon) background zip and moving of the Harmony scene - folder, interfers with server/client communication by sending two requests + folder, interferes with server/client communication by sending two requests at the same time. This only happens when sending "scene.saveAll()". This method prevents this double request and safely saves the scene. diff --git a/client/ayon_core/hosts/hiero/api/lib.py b/client/ayon_core/hosts/hiero/api/lib.py index 8e08e8cbf3..ecb3460fb4 100644 --- a/client/ayon_core/hosts/hiero/api/lib.py +++ b/client/ayon_core/hosts/hiero/api/lib.py @@ -166,7 +166,7 @@ def get_current_track(sequence, name, audio=False): Creates new if none is found. Args: - sequence (hiero.core.Sequence): hiero sequene object + sequence (hiero.core.Sequence): hiero sequence object name (str): name of track we want to return audio (bool)[optional]: switch to AudioTrack @@ -846,8 +846,8 @@ def create_nuke_workfile_clips(nuke_workfiles, seq=None): [{ 'path': 'P:/Jakub_testy_pipeline/test_v01.nk', 'name': 'test', - 'handleStart': 15, # added asymetrically to handles - 'handleEnd': 10, # added asymetrically to handles + 'handleStart': 15, # added asymmetrically to handles + 'handleEnd': 10, # added asymmetrically to handles "clipIn": 16, "frameStart": 991, "frameEnd": 1023, @@ -1192,7 +1192,7 @@ def get_sequence_pattern_and_padding(file): Return: string: any matching sequence pattern - int: padding of sequnce numbering + int: padding of sequence numbering """ foundall = re.findall( r"(#+)|(%\d+d)|(?<=[^a-zA-Z0-9])(\d+)(?=\.\w+$)", file) diff --git a/client/ayon_core/hosts/hiero/api/otio/hiero_import.py b/client/ayon_core/hosts/hiero/api/otio/hiero_import.py index 257c434011..f123b81ca6 100644 --- a/client/ayon_core/hosts/hiero/api/otio/hiero_import.py +++ b/client/ayon_core/hosts/hiero/api/otio/hiero_import.py @@ -90,7 +90,7 @@ def apply_transition(otio_track, otio_item, track): if isinstance(track, hiero.core.AudioTrack): kind = 'Audio' - # Gather TrackItems involved in trasition + # Gather TrackItems involved in transition item_in, item_out = get_neighboring_trackitems( otio_item, otio_track, diff --git a/client/ayon_core/hosts/hiero/api/otio/utils.py b/client/ayon_core/hosts/hiero/api/otio/utils.py index 4c5d46bd51..f7cb58f1e8 100644 --- a/client/ayon_core/hosts/hiero/api/otio/utils.py +++ b/client/ayon_core/hosts/hiero/api/otio/utils.py @@ -25,7 +25,7 @@ def get_reformated_path(path, padded=True): path (str): path url or simple file name Returns: - type: string with reformated path + type: string with reformatted path Example: get_reformated_path("plate.[0001-1008].exr") > plate.%04d.exr diff --git a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py index 17c044f3ec..8331c429df 100644 --- a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py +++ b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py @@ -90,7 +90,7 @@ def apply_transition(otio_track, otio_item, track): kind = "Audio" try: - # Gather TrackItems involved in trasition + # Gather TrackItems involved in transition item_in, item_out = get_neighboring_trackitems( otio_item, otio_track, diff --git a/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py b/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py index 62e7041286..2985a81317 100644 --- a/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py +++ b/client/ayon_core/hosts/hiero/plugins/create/create_shot_clip.py @@ -166,7 +166,7 @@ class CreateShotClip(phiero.Creator): "type": "QCheckBox", "label": "Source resolution", "target": "tag", - "toolTip": "Is resloution taken from timeline or source?", # noqa + "toolTip": "Is resolution taken from timeline or source?", # noqa "order": 4}, } }, @@ -211,7 +211,7 @@ class CreateShotClip(phiero.Creator): presets = deepcopy(self.presets) gui_inputs = deepcopy(self.gui_inputs) - # get key pares from presets and match it on ui inputs + # get key pairs from presets and match it on ui inputs for k, v in gui_inputs.items(): if v["type"] in ("dict", "section"): # nested dictionary (only one level allowed diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index d921f37934..26f6968884 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -43,7 +43,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): tracks_effect_items = self.collect_sub_track_items(all_tracks) context.data["tracksEffectItems"] = tracks_effect_items - # process all sellected timeline track items + # process all selected timeline track items for track_item in selected_timeline_items: data = {} clip_name = track_item.name() @@ -62,7 +62,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): }: continue - # get clips subtracks and anotations + # get clips subtracks and annotations annotations = self.clip_annotations(source_clip) subtracks = self.clip_subtrack(track_item) self.log.debug("Annotations: {}".format(annotations)) @@ -439,10 +439,10 @@ class PrecollectInstances(pyblish.api.ContextPlugin): for item in subTrackItems: if "TimeWarp" in item.name(): continue - # avoid all anotation + # avoid all annotation if isinstance(item, hiero.core.Annotation): continue - # # avoid all not anaibled + # avoid all disabled if not item.isEnabled(): continue subtracks.append(item) diff --git a/client/ayon_core/hosts/houdini/api/colorspace.py b/client/ayon_core/hosts/houdini/api/colorspace.py index 66581d6f20..6a92c77e49 100644 --- a/client/ayon_core/hosts/houdini/api/colorspace.py +++ b/client/ayon_core/hosts/houdini/api/colorspace.py @@ -59,7 +59,7 @@ class ARenderProduct(object): def get_default_display_view_colorspace(): """Returns the colorspace attribute of the default (display, view) pair. - It's used for 'ociocolorspace' parm in OpenGL Node.""" + It's used for 'ociocolorspace' param in OpenGL Node.""" prefs = get_color_management_preferences() return get_display_view_colorspace_name( diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 8ca898f621..cad5b0405f 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -131,7 +131,7 @@ def get_main_window(): def suspended_refresh(suspend=True): """Suspend viewport refreshes - cmds.ogs(pause=True) is a toggle so we cant pass False. + cmds.ogs(pause=True) is a toggle so we can't pass False. """ if IS_HEADLESS: yield @@ -583,7 +583,7 @@ def pairwise(iterable): def collect_animation_defs(fps=False): - """Get the basic animation attribute defintions for the publisher. + """Get the basic animation attribute definitions for the publisher. Returns: OrderedDict @@ -3834,7 +3834,7 @@ def get_color_management_output_transform(): def image_info(file_path): # type: (str) -> dict - """Based on tha texture path, get its bit depth and format information. + """Based on the texture path, get its bit depth and format information. Take reference from makeTx.py in Arnold: ImageInfo(filename): Get Image Information for colorspace AiTextureGetFormat(filename): Get Texture Format diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index 13eb8fd49e..c981c37123 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -314,7 +314,7 @@ class CollectMayaRender(pyblish.api.InstancePlugin): if not extend_frames: instance.data["overrideExistingFrame"] = False - # Update the instace + # Update the instance instance.data.update(data) @staticmethod diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_xgen.py b/client/ayon_core/hosts/maya/plugins/publish/validate_xgen.py index e2c006be9f..7e0f01c482 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_xgen.py @@ -34,7 +34,7 @@ class ValidateXgen(pyblish.api.InstancePlugin): " Node type found: {}".format(node_type) ) - # Cant have inactive modifiers in collection cause Xgen will try and + # Can't have inactive modifiers in collection cause Xgen will try and # look for them when loading. palette = instance.data["xgmPalette"].replace("|", "") inactive_modifiers = {} diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 1bb0ff79e0..deeab47885 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -814,7 +814,7 @@ def on_script_load(): def check_inventory_versions(): """ - Actual version idetifier of Loaded containers + Actual version identifier of Loaded containers Any time this function is run it will check all nodes and filter only Loader nodes for its version. It will get all versions from database @@ -2381,7 +2381,7 @@ def launch_workfiles_app(): Context.workfiles_launched = True - # get all imortant settings + # get all important settings open_at_start = env_value_to_bool( env_key="AYON_WORKFILE_TOOL_ON_START", default=None) diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index 7f016d9c66..650b67dd2c 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -910,7 +910,7 @@ class ExporterReviewMov(ExporterReview): self._connect_to_above_nodes( node, product_name, "Reposition node... `{}`" ) - # append reformated tag + # append reformatted tag add_tags.append("reformated") # only create colorspace baking if toggled on @@ -1114,7 +1114,7 @@ def convert_to_valid_instaces(): transfer_data["active"] = ( node["publish"].value()) - # add idetifier + # add identifier transfer_data["creator_identifier"] = product_type_to_identifier( product_type ) diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py b/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py index 25b22f53a4..73e8c3683c 100644 --- a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -19,7 +19,7 @@ class ImageFromSequenceLoader(photoshop.PhotoshopLoader): This loader will be triggered multiple times, but selected name will match only to proper path. - Loader doesnt do containerization as there is currently no data model + Loader doesn't do containerization as there is currently no data model of 'frame of rendered files' (only rendered sequence), update would be difficult. """ diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index a60f3cd4ec..b9ad81c79d 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -925,7 +925,7 @@ def get_reformated_path(path, padded=False, first=False): path (str): path url or simple file name Returns: - type: string with reformated path + type: string with reformatted path Example: get_reformated_path("plate.[0001-1008].exr") > plate.%04d.exr diff --git a/client/ayon_core/hosts/resolve/otio/utils.py b/client/ayon_core/hosts/resolve/otio/utils.py index 7d8089e055..c03305ff23 100644 --- a/client/ayon_core/hosts/resolve/otio/utils.py +++ b/client/ayon_core/hosts/resolve/otio/utils.py @@ -25,7 +25,7 @@ def get_reformated_path(path, padded=True, first=False): path (str): path url or simple file name Returns: - type: string with reformated path + type: string with reformatted path Example: get_reformated_path("plate.[0001-1008].exr") > plate.%04d.exr diff --git a/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py b/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py index 3a2a0345ea..cbc03da3b6 100644 --- a/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py +++ b/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py @@ -166,7 +166,7 @@ class CreateShotClip(plugin.Creator): "type": "QCheckBox", "label": "Source resolution", "target": "tag", - "toolTip": "Is resloution taken from timeline or source?", # noqa + "toolTip": "Is resolution taken from timeline or source?", # noqa "order": 4}, } }, @@ -207,7 +207,7 @@ class CreateShotClip(plugin.Creator): presets = None def process(self): - # get key pares from presets and match it on ui inputs + # get key pairs from presets and match it on ui inputs for k, v in self.gui_inputs.items(): if v["type"] in ("dict", "section"): # nested dictionary (only one level allowed diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py index e5bf034d00..4f11571efe 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py @@ -20,7 +20,7 @@ class ValidateFrameRange(OptionalPyblishPluginMixin, optional = True # published data might be sequence (.mov, .mp4) in that counting files - # doesnt make sense + # doesn't make sense check_extensions = ["exr", "dpx", "jpg", "jpeg", "png", "tiff", "tga", "gif", "svg"] skip_timelines_check = [] # skip for specific task names (regex) diff --git a/client/ayon_core/hosts/unreal/api/tools_ui.py b/client/ayon_core/hosts/unreal/api/tools_ui.py index 084da9a0f0..efae5bb702 100644 --- a/client/ayon_core/hosts/unreal/api/tools_ui.py +++ b/client/ayon_core/hosts/unreal/api/tools_ui.py @@ -125,7 +125,7 @@ class WindowCache: @classmethod def _before_show(cls): - """Create QApplication if does not exists yet.""" + """Create QApplication if does not exist yet.""" if not cls._first_show: return diff --git a/client/ayon_core/lib/local_settings.py b/client/ayon_core/lib/local_settings.py index 9eba3d1ed1..fd255c997f 100644 --- a/client/ayon_core/lib/local_settings.py +++ b/client/ayon_core/lib/local_settings.py @@ -524,7 +524,7 @@ def get_ayon_appdirs(*args): def get_local_site_id(): """Get local site identifier. - Identifier is created if does not exists yet. + Identifier is created if does not exist yet. """ # used for background syncing site_id = os.environ.get("AYON_SITE_ID") diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index 09d11ea1de..a766dbd9c1 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -102,7 +102,7 @@ class StringTemplate(object): """ Figure out with whole formatting. Separate advanced keys (*Like '{project[name]}') from string which must - be formatted separatelly in case of missing or incomplete keys in data. + be formatted separately in case of missing or incomplete keys in data. Args: data (dict): Containing keys to be filled into template. diff --git a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py index 5d177fec07..662913cadf 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/create_publish_royalrender_job.py @@ -198,7 +198,7 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, priority = self.priority or instance.data.get("priority", 50) - # rr requires absolut path or all jobs won't show up in rControl + # rr requires absolute path or all jobs won't show up in rrControl abs_metadata_path = self.anatomy.fill_root(rootless_metadata_path) # command line set in E01__OpenPype__PublishJob.cfg, here only diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 8c6a7f1bb6..ca9896fb3f 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -529,7 +529,7 @@ class AttributeValues(object): Has dictionary like methods. Not all of them are allowed all the time. Args: - attr_defs(AbstractAttrDef): Defintions of value type and properties. + attr_defs(AbstractAttrDef): Definitions of value type and properties. values(dict): Values after possible conversion. origin_data(dict): Values loaded from host before conversion. """ diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 5505427d7e..e0b30763d0 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -347,7 +347,7 @@ class BaseCreator: Returns: str: Group label that can be used for grouping of instances in UI. - Group label can be overriden by instance itself. + Group label can be overridden by instance itself. """ if self._cached_group_label is None: @@ -607,18 +607,19 @@ class Creator(BaseCreator): """ # GUI Purposes - # - default_variants may not be used if `get_default_variants` is overriden + # - default_variants may not be used if `get_default_variants` + # is overridden default_variants = [] # Default variant used in 'get_default_variant' _default_variant = None # Short description of product type - # - may not be used if `get_description` is overriden + # - may not be used if `get_description` is overridden description = None # Detailed description of product type for artists - # - may not be used if `get_detail_description` is overriden + # - may not be used if `get_detail_description` is overridden detailed_description = None # It does make sense to change context on creation diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 564d78ea6f..84bffbe1ec 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -64,7 +64,7 @@ def convert_to_padded_path(path, padding): padding (int): number of padding Returns: - type: string with reformated path + type: string with reformatted path Example: convert_to_padded_path("plate.%d.exr") > plate.%04d.exr diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 91f839ebf3..aa2542d936 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -116,7 +116,7 @@ class LoaderPlugin(list): def is_compatible_loader(cls, context): """Return whether a loader is compatible with a context. - On override make sure it is overriden as class or static method. + On override make sure it is overridden as class or static method. This checks the product type and the representation for the given loader plugin. diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 1d7b5ed5a7..5e63ba444a 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -1865,7 +1865,7 @@ class PlaceholderCreateMixin(object): self.log.debug("Clean up of placeholder is not implemented.") def _before_instance_create(self, placeholder): - """Can be overriden. Is called before instance is created.""" + """Can be overridden. Is called before instance is created.""" pass diff --git a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py index a19b5b9090..98723beffa 100644 --- a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py +++ b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py @@ -80,7 +80,7 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): # create duration duration = (timeline_out_h - timeline_in_h) + 1 - # ffmpeg generate new file only if doesnt exists already + # ffmpeg generate new file only if doesn't exists already if not recycling_file: # convert to seconds start_sec = float(timeline_in_h / fps) diff --git a/client/ayon_core/tools/common_models/thumbnails.py b/client/ayon_core/tools/common_models/thumbnails.py index 138cee4ea2..1c3aadc49f 100644 --- a/client/ayon_core/tools/common_models/thumbnails.py +++ b/client/ayon_core/tools/common_models/thumbnails.py @@ -112,7 +112,7 @@ class ThumbnailsCache: """ thumbnails_dir = self.get_thumbnails_dir() - # Skip if thumbnails dir does not exists yet + # Skip if thumbnails dir does not exist yet if not os.path.exists(thumbnails_dir): return diff --git a/client/ayon_core/tools/pyblish_pype/util.py b/client/ayon_core/tools/pyblish_pype/util.py index 8126637060..0c3a7a8ba6 100644 --- a/client/ayon_core/tools/pyblish_pype/util.py +++ b/client/ayon_core/tools/pyblish_pype/util.py @@ -39,7 +39,7 @@ def defer(delay, func): This aids in keeping the GUI responsive, but complicates logic when producing tests. To combat this, the environment variable ensures - that every operation is synchonous. + that every operation is synchronous. Arguments: delay (float): Delay multiplier; default 1, 0 means no delay diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 9b5f3ae571..f9ac1059ac 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -424,7 +424,7 @@ class ExtractReviewOutputDefModel(BaseSettingsModel): title="Scale pixel aspect", description=( "Rescale input when it's pixel aspect ratio is not 1." - " Usefull for anamorph reviews." + " Useful for anamorphic reviews." ) ) bg_color: ColorRGBA_uint8 = SettingsField( From 8e740caf2c9609b8874c06809084d93f7ac8864b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 11:48:45 +0100 Subject: [PATCH 086/130] No need to define `apply_settings` since it gets auto-applied --- .../maya/plugins/create/create_unreal_skeletalmesh.py | 7 ------- .../hosts/maya/plugins/create/create_unreal_staticmesh.py | 5 ----- 2 files changed, 12 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py index 9ded28b812..a32e94971e 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py @@ -20,13 +20,6 @@ class CreateUnrealSkeletalMesh(plugin.MayaCreator): # Defined in settings joint_hints = set() - def apply_settings(self, project_settings): - """Apply project settings to creator""" - settings = ( - project_settings["maya"]["create"]["CreateUnrealSkeletalMesh"] - ) - self.joint_hints = set(settings.get("joint_hints", [])) - def get_dynamic_data( self, project_name, diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py index 1991f92915..76c33f00cc 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -15,11 +15,6 @@ class CreateUnrealStaticMesh(plugin.MayaCreator): # Defined in settings collision_prefixes = [] - def apply_settings(self, project_settings): - """Apply project settings to creator""" - settings = project_settings["maya"]["create"]["CreateUnrealStaticMesh"] - self.collision_prefixes = settings["collision_prefixes"] - def get_dynamic_data( self, project_name, From f2056e8b34f2f8fc31bb62e4416fdb1e048b0246 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 27 Mar 2024 11:53:34 +0100 Subject: [PATCH 087/130] Make Creator class name compared to existing `CreateYetiCache` in `create_yeti_cache.py` --- .../hosts/maya/plugins/create/create_unreal_yeticache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py b/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py index 1eac8a5ea9..dea64b40fb 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py @@ -5,7 +5,7 @@ from ayon_core.hosts.maya.api import ( from ayon_core.lib import NumberDef -class CreateYetiCache(plugin.MayaCreator): +class CreateUnrealYetiCache(plugin.MayaCreator): """Output for procedural plugin nodes of Yeti """ identifier = "io.openpype.creators.maya.unrealyeticache" From 499dd39f850ad1cef970f65a65b888bc569127a3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:05:59 +0100 Subject: [PATCH 088/130] removed unused imports --- client/ayon_core/cli_commands.py | 1 - .../hosts/aftereffects/plugins/publish/collect_render.py | 5 +---- .../hosts/blender/plugins/publish/extract_camera_abc.py | 1 - .../ayon_core/hosts/blender/plugins/publish/extract_fbx.py | 1 - client/ayon_core/hosts/fusion/api/pipeline.py | 1 - .../hosts/harmony/plugins/publish/collect_audio.py | 2 +- client/ayon_core/hosts/hiero/api/events.py | 4 +++- .../hiero/api/startup/Python/Startup/SpreadsheetExport.py | 4 +++- .../hosts/hiero/plugins/publish/collect_clip_effects.py | 2 +- client/ayon_core/hosts/houdini/api/pipeline.py | 1 - client/ayon_core/hosts/max/plugins/load/load_model_obj.py | 1 - .../hosts/max/plugins/publish/validate_camera_contents.py | 2 +- .../hosts/maya/plugins/load/load_arnold_standin.py | 1 - client/ayon_core/hosts/maya/plugins/load/load_gpucache.py | 2 -- client/ayon_core/hosts/maya/plugins/load/load_image.py | 2 -- .../plugins/publish/extract_unreal_skeletalmesh_abc.py | 1 - .../hosts/maya/plugins/publish/extract_workfile_xgen.py | 1 - .../ayon_core/hosts/maya/plugins/publish/extract_xgen.py | 1 - .../maya/plugins/publish/validate_node_no_ghosting.py | 6 ++---- client/ayon_core/hosts/nuke/api/pipeline.py | 2 -- client/ayon_core/hosts/nuke/api/plugin.py | 7 ------- .../ayon_core/hosts/nuke/plugins/publish/extract_camera.py | 1 - .../hosts/nuke/plugins/publish/validate_rendered_frames.py | 2 +- client/ayon_core/hosts/photoshop/api/launch_logic.py | 2 +- .../ayon_core/hosts/photoshop/plugins/publish/closePS.py | 2 -- .../unreal/plugins/publish/collect_render_instances.py | 3 +-- client/ayon_core/modules/clockify/clockify_api.py | 2 -- .../deadline/plugins/publish/submit_maya_deadline.py | 1 - .../deadline/repository/custom/plugins/Ayon/Ayon.py | 1 - .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 1 - client/ayon_core/modules/royalrender/lib.py | 1 - .../plugins/publish/submit_jobs_to_royalrender.py | 1 - client/ayon_core/pipeline/create/product_name.py | 2 -- client/ayon_core/pipeline/farm/pyblish_functions.pyi | 2 +- client/ayon_core/pipeline/load/plugins.py | 1 - client/ayon_core/pipeline/publish/publish_plugins.py | 1 - .../tools/publisher/widgets/create_context_widgets.py | 2 +- client/ayon_core/tools/publisher/widgets/folders_dialog.py | 2 +- client/ayon_core/tools/publisher/widgets/publish_frame.py | 4 ---- client/ayon_core/tools/publisher/widgets/tasks_model.py | 2 +- client/ayon_core/tools/pyblish_pype/util.py | 2 -- client/ayon_core/tools/utils/lib.py | 1 - client/ayon_core/tools/utils/models.py | 2 +- server_addon/create_ayon_addons.py | 1 - server_addon/nuke/server/settings/main.py | 1 - server_addon/tvpaint/server/settings/main.py | 1 - 46 files changed, 20 insertions(+), 69 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index bc0a22382c..fa90571462 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -3,7 +3,6 @@ import os import sys import json -import warnings class Commands: diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py index afd58ca758..4134e9d593 100644 --- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py @@ -1,14 +1,11 @@ import os -import re import tempfile -import attr +import attr import pyblish.api -from ayon_core.settings import get_project_settings from ayon_core.pipeline import publish from ayon_core.pipeline.publish import RenderInstance - from ayon_core.hosts.aftereffects.api import get_stub diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py index cc783e552c..c60c92dee1 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_camera_abc.py @@ -4,7 +4,6 @@ import bpy from ayon_core.pipeline import publish from ayon_core.hosts.blender.api import plugin -from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin): diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py index 7ebda2c4cd..e6367dbc0d 100644 --- a/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py +++ b/client/ayon_core/hosts/blender/plugins/publish/extract_fbx.py @@ -4,7 +4,6 @@ import bpy from ayon_core.pipeline import publish from ayon_core.hosts.blender.api import plugin -from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin): diff --git a/client/ayon_core/hosts/fusion/api/pipeline.py b/client/ayon_core/hosts/fusion/api/pipeline.py index 50157cfae6..dfac0640b0 100644 --- a/client/ayon_core/hosts/fusion/api/pipeline.py +++ b/client/ayon_core/hosts/fusion/api/pipeline.py @@ -28,7 +28,6 @@ from ayon_core.tools.utils import host_tools from .lib import ( get_current_comp, - comp_lock_and_undo_chunk, validate_comp_prefs ) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_audio.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_audio.py index 40b4107a62..cc959a23b9 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_audio.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_audio.py @@ -1,8 +1,8 @@ import os -import pyblish.api import pyblish.api + class CollectAudio(pyblish.api.InstancePlugin): """ Collect relative path for audio file to instance. diff --git a/client/ayon_core/hosts/hiero/api/events.py b/client/ayon_core/hosts/hiero/api/events.py index 0e509747d5..304605e24e 100644 --- a/client/ayon_core/hosts/hiero/api/events.py +++ b/client/ayon_core/hosts/hiero/api/events.py @@ -1,10 +1,12 @@ import os + import hiero.core.events + from ayon_core.lib import Logger, register_event_callback + from .lib import ( sync_avalon_data_to_workfile, launch_workfiles_app, - selection_changed_timeline, before_project_save, ) from .tags import add_tags_to_workfile diff --git a/client/ayon_core/hosts/hiero/api/startup/Python/Startup/SpreadsheetExport.py b/client/ayon_core/hosts/hiero/api/startup/Python/Startup/SpreadsheetExport.py index 9c919e7cb4..6a8057ec1e 100644 --- a/client/ayon_core/hosts/hiero/api/startup/Python/Startup/SpreadsheetExport.py +++ b/client/ayon_core/hosts/hiero/api/startup/Python/Startup/SpreadsheetExport.py @@ -3,9 +3,11 @@ # Note: This only prints the text data that is visible in the active Spreadsheet View. # If you've filtered text, only the visible text will be printed to the CSV file # Usage: Copy to ~/.hiero/Python/StartupUI +import os +import csv + import hiero.core.events import hiero.ui -import os, csv try: from PySide.QtGui import * from PySide.QtCore import * diff --git a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py index 32b4864022..bfc63f2551 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/collect_clip_effects.py @@ -1,5 +1,5 @@ -from itertools import product import re + import pyblish.api diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/client/ayon_core/hosts/houdini/api/pipeline.py index d5144200cf..5131344483 100644 --- a/client/ayon_core/hosts/houdini/api/pipeline.py +++ b/client/ayon_core/hosts/houdini/api/pipeline.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Pipeline tools for OpenPype Houdini integration.""" import os -import sys import logging import hou # noqa diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py index 4f8a22af07..2330dbfc24 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py +++ b/client/ayon_core/hosts/max/plugins/load/load_model_obj.py @@ -7,7 +7,6 @@ from ayon_core.hosts.max.api.lib import ( maintained_selection, object_transform_set ) -from ayon_core.hosts.max.api.lib import maintained_selection from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_camera_contents.py b/client/ayon_core/hosts/max/plugins/publish/validate_camera_contents.py index 0473fd4a8a..334e7dcec9 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_camera_contents.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_camera_contents.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pyblish.api + from ayon_core.pipeline import PublishValidationError -from pymxs import runtime as rt class ValidateCameraContent(pyblish.api.InstancePlugin): diff --git a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py index 920ad762b3..7170c30422 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py @@ -12,7 +12,6 @@ from ayon_core.hosts.maya.api.lib import ( unique_namespace, get_attribute_input, maintained_selection, - convert_to_maya_fps ) from ayon_core.hosts.maya.api.pipeline import containerise from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type diff --git a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py index 494bc7cfc6..9689282ae9 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py @@ -1,5 +1,3 @@ -import os - import maya.cmds as cmds from ayon_core.hosts.maya.api.pipeline import containerise diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image.py b/client/ayon_core/hosts/maya/plugins/load/load_image.py index 4976c46d7f..3641655d49 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image.py @@ -1,10 +1,8 @@ -import os import copy from ayon_core.lib import EnumDef from ayon_core.pipeline import ( load, - get_representation_context, get_current_host_name, ) from ayon_core.pipeline.load.utils import get_representation_path_from_context diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py index 8b88bfb9f8..1a389f3d33 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Create Unreal Skeletal Mesh data to be extracted as FBX.""" import os -from contextlib import contextmanager from maya import cmds # noqa diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py b/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py index d305b8dc6c..d799486184 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -7,7 +7,6 @@ from maya import cmds import pyblish.api from ayon_core.hosts.maya.api.lib import extract_alembic from ayon_core.pipeline import publish -from ayon_core.lib import StringTemplate class ExtractWorkfileXgen(publish.Extractor): diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_xgen.py b/client/ayon_core/hosts/maya/plugins/publish/extract_xgen.py index 73668da28d..b672089a63 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_xgen.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_xgen.py @@ -9,7 +9,6 @@ from ayon_core.pipeline import publish from ayon_core.hosts.maya.api.lib import ( maintained_selection, attribute_values, write_xgen_file, delete_after ) -from ayon_core.lib import StringTemplate class ExtractXgen(publish.Extractor): diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py b/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py index 73701f8d83..843ee0d9d5 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py @@ -3,11 +3,9 @@ from maya import cmds import pyblish.api import ayon_core.hosts.maya.api.action -from ayon_core.pipeline.publish import ( - ValidateContentsOrder, - OptionalPyblishPluginMixin +from ayon_core.pipeline.publish import ValidateContentsOrder + -) class ValidateNodeNoGhosting(pyblish.api.InstancePlugin. OptionalPyblishPluginMixin): """Ensure nodes do not have ghosting enabled. diff --git a/client/ayon_core/hosts/nuke/api/pipeline.py b/client/ayon_core/hosts/nuke/api/pipeline.py index 2255276c56..0d44aba2f9 100644 --- a/client/ayon_core/hosts/nuke/api/pipeline.py +++ b/client/ayon_core/hosts/nuke/api/pipeline.py @@ -30,13 +30,11 @@ from ayon_core.tools.utils import host_tools from ayon_core.hosts.nuke import NUKE_ROOT_DIR from ayon_core.tools.workfile_template_build import open_template_ui -from .command import viewer_update_and_undo_stop from .lib import ( Context, ROOT_DATA_KNOB, INSTANCE_DATA_KNOB, get_main_window, - add_publish_knob, WorkfileSettings, # TODO: remove this once workfile builder will be removed process_workfile_builder, diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index 7f016d9c66..a5f240e42a 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -6,7 +6,6 @@ import six import random import string from collections import OrderedDict, defaultdict -from abc import abstractmethod from ayon_core.settings import get_current_project_settings from ayon_core.lib import ( @@ -14,7 +13,6 @@ from ayon_core.lib import ( EnumDef ) from ayon_core.pipeline import ( - LegacyCreator, LoaderPlugin, CreatorError, Creator as NewCreator, @@ -34,18 +32,13 @@ from ayon_core.lib.transcoding import ( from .lib import ( INSTANCE_DATA_KNOB, Knobby, - check_product_name_exists, maintained_selection, get_avalon_knob_data, - set_avalon_knob_data, - add_publish_knob, - get_nuke_imageio_settings, set_node_knobs_from_settings, set_node_data, get_node_data, get_view_process_node, get_viewer_config_from_string, - deprecated, get_filenames_without_hash, link_knobs ) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py index 1f5a8c73e1..a1a5acb63b 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py @@ -1,6 +1,5 @@ import os import math -from pprint import pformat import nuke diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_rendered_frames.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_rendered_frames.py index 852267f68c..76ac7e97ad 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_rendered_frames.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_rendered_frames.py @@ -1,6 +1,6 @@ -import os import pyblish.api import clique + from ayon_core.pipeline import PublishXmlValidationError from ayon_core.pipeline.publish import get_errored_instances_from_context diff --git a/client/ayon_core/hosts/photoshop/api/launch_logic.py b/client/ayon_core/hosts/photoshop/api/launch_logic.py index d0823646d7..c388f93044 100644 --- a/client/ayon_core/hosts/photoshop/api/launch_logic.py +++ b/client/ayon_core/hosts/photoshop/api/launch_logic.py @@ -11,7 +11,7 @@ from wsrpc_aiohttp import ( import ayon_api from qtpy import QtCore -from ayon_core.lib import Logger, StringTemplate +from ayon_core.lib import Logger from ayon_core.pipeline import ( registered_host, Anatomy, diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/closePS.py b/client/ayon_core/hosts/photoshop/plugins/publish/closePS.py index 6f86d98580..68c3b5b249 100644 --- a/client/ayon_core/hosts/photoshop/plugins/publish/closePS.py +++ b/client/ayon_core/hosts/photoshop/plugins/publish/closePS.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- """Close PS after publish. For Webpublishing only.""" -import os - import pyblish.api from ayon_core.hosts.photoshop import api as photoshop diff --git a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py index ea53f221ea..ce2a03155b 100644 --- a/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py +++ b/client/ayon_core/hosts/unreal/plugins/publish/collect_render_instances.py @@ -1,12 +1,11 @@ -import os from pathlib import Path import unreal +import pyblish.api from ayon_core.pipeline import get_current_project_name from ayon_core.pipeline import Anatomy from ayon_core.hosts.unreal.api import pipeline -import pyblish.api class CollectRenderInstances(pyblish.api.InstancePlugin): diff --git a/client/ayon_core/modules/clockify/clockify_api.py b/client/ayon_core/modules/clockify/clockify_api.py index f8c9c537ee..2e1d8f008f 100644 --- a/client/ayon_core/modules/clockify/clockify_api.py +++ b/client/ayon_core/modules/clockify/clockify_api.py @@ -1,6 +1,4 @@ import os -import re -import time import json import datetime import requests diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py index 0e871eb90e..a31a11ffb1 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -651,7 +651,6 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, return job_info, attr.asdict(plugin_info) def _get_arnold_render_payload(self, data): - from maya import cmds # Job Info job_info = copy.deepcopy(self.job_info) job_info.Name = self._job_info_label("Render") diff --git a/client/ayon_core/modules/deadline/repository/custom/plugins/Ayon/Ayon.py b/client/ayon_core/modules/deadline/repository/custom/plugins/Ayon/Ayon.py index de0a2c6d7a..bb7f932013 100644 --- a/client/ayon_core/modules/deadline/repository/custom/plugins/Ayon/Ayon.py +++ b/client/ayon_core/modules/deadline/repository/custom/plugins/Ayon/Ayon.py @@ -7,7 +7,6 @@ from Deadline.Plugins import PluginType, DeadlinePlugin from Deadline.Scripting import ( StringUtils, FileUtils, - DirectoryUtils, RepositoryUtils ) diff --git a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 1565b2c496..8df96b425e 100644 --- a/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/client/ayon_core/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -12,7 +12,6 @@ from Deadline.Scripting import ( RepositoryUtils, FileUtils, DirectoryUtils, - ProcessUtils, ) __version__ = "1.0.1" VERSION_REGEX = re.compile( diff --git a/client/ayon_core/modules/royalrender/lib.py b/client/ayon_core/modules/royalrender/lib.py index d552e7fb19..5392803710 100644 --- a/client/ayon_core/modules/royalrender/lib.py +++ b/client/ayon_core/modules/royalrender/lib.py @@ -2,7 +2,6 @@ """Submitting render job to RoyalRender.""" import os import json -import platform import re import tempfile import uuid diff --git a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py index 54de943428..09c1dc4a54 100644 --- a/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py +++ b/client/ayon_core/modules/royalrender/plugins/publish/submit_jobs_to_royalrender.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Submit jobs to RoyalRender.""" import tempfile -import platform import pyblish.api from ayon_core.modules.royalrender.api import ( diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 74e268fbb3..fecda867e5 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -1,5 +1,3 @@ -import ayon_api - from ayon_core.settings import get_project_settings from ayon_core.lib import filter_profiles, prepare_template_data diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.pyi b/client/ayon_core/pipeline/farm/pyblish_functions.pyi index 16c11aa480..fe0ae57da0 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.pyi +++ b/client/ayon_core/pipeline/farm/pyblish_functions.pyi @@ -1,6 +1,6 @@ import pyblish.api from ayon_core.pipeline import Anatomy -from typing import Tuple, Union, List +from typing import Tuple, List class TimeData: diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 91f839ebf3..bced89e3a1 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -2,7 +2,6 @@ import os import logging from ayon_core.settings import get_project_settings -from ayon_core.pipeline import schema from ayon_core.pipeline.plugin_discover import ( discover, register_plugin, diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 2386558091..6b1984d92b 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -2,7 +2,6 @@ import inspect from abc import ABCMeta import pyblish.api from pyblish.plugin import MetaPlugin, ExplicitMetaPlugin -from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS from ayon_core.lib import BoolDef from .lib import ( diff --git a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py index 61223bbe75..235a778d0f 100644 --- a/client/ayon_core/tools/publisher/widgets/create_context_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/create_context_widgets.py @@ -1,4 +1,4 @@ -from qtpy import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore from ayon_core.lib.events import QueuedEventSystem from ayon_core.tools.utils import PlaceholderLineEdit, GoToCurrentButton diff --git a/client/ayon_core/tools/publisher/widgets/folders_dialog.py b/client/ayon_core/tools/publisher/widgets/folders_dialog.py index 03336e10a6..8dce7aba3a 100644 --- a/client/ayon_core/tools/publisher/widgets/folders_dialog.py +++ b/client/ayon_core/tools/publisher/widgets/folders_dialog.py @@ -1,4 +1,4 @@ -from qtpy import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets from ayon_core.lib.events import QueuedEventSystem from ayon_core.tools.utils import PlaceholderLineEdit, FoldersWidget diff --git a/client/ayon_core/tools/publisher/widgets/publish_frame.py b/client/ayon_core/tools/publisher/widgets/publish_frame.py index d423f97047..ee65c69c19 100644 --- a/client/ayon_core/tools/publisher/widgets/publish_frame.py +++ b/client/ayon_core/tools/publisher/widgets/publish_frame.py @@ -1,7 +1,3 @@ -import os -import json -import time - from qtpy import QtWidgets, QtCore from .widgets import ( diff --git a/client/ayon_core/tools/publisher/widgets/tasks_model.py b/client/ayon_core/tools/publisher/widgets/tasks_model.py index e36de80fcf..78b1f23b17 100644 --- a/client/ayon_core/tools/publisher/widgets/tasks_model.py +++ b/client/ayon_core/tools/publisher/widgets/tasks_model.py @@ -1,4 +1,4 @@ -from qtpy import QtWidgets, QtCore, QtGui +from qtpy import QtCore, QtGui from ayon_core.style import get_default_entity_icon_color from ayon_core.tools.utils import get_qt_icon diff --git a/client/ayon_core/tools/pyblish_pype/util.py b/client/ayon_core/tools/pyblish_pype/util.py index 8126637060..ddbb7ddad2 100644 --- a/client/ayon_core/tools/pyblish_pype/util.py +++ b/client/ayon_core/tools/pyblish_pype/util.py @@ -7,8 +7,6 @@ from __future__ import ( import os import sys -import numbers -import copy import collections from qtpy import QtCore diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index 4b7ca5425e..d56b370d75 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -7,7 +7,6 @@ from qtpy import QtWidgets, QtCore, QtGui import qtawesome from ayon_core.style import ( - get_default_entity_icon_color, get_objected_colors, get_app_icon_path, ) diff --git a/client/ayon_core/tools/utils/models.py b/client/ayon_core/tools/utils/models.py index 92bed16e98..9b32cc5710 100644 --- a/client/ayon_core/tools/utils/models.py +++ b/client/ayon_core/tools/utils/models.py @@ -2,7 +2,7 @@ import re import logging import qtpy -from qtpy import QtCore, QtGui +from qtpy import QtCore log = logging.getLogger(__name__) diff --git a/server_addon/create_ayon_addons.py b/server_addon/create_ayon_addons.py index 9553980f5d..dd765b381e 100644 --- a/server_addon/create_ayon_addons.py +++ b/server_addon/create_ayon_addons.py @@ -1,7 +1,6 @@ import os import sys import re -import json import shutil import argparse import zipfile diff --git a/server_addon/nuke/server/settings/main.py b/server_addon/nuke/server/settings/main.py index 2b269f1fce..936686d6ce 100644 --- a/server_addon/nuke/server/settings/main.py +++ b/server_addon/nuke/server/settings/main.py @@ -1,7 +1,6 @@ from ayon_server.settings import ( BaseSettingsModel, SettingsField, - ensure_unique_names ) from .general import ( diff --git a/server_addon/tvpaint/server/settings/main.py b/server_addon/tvpaint/server/settings/main.py index c6b6c9ab12..f20e9ecc9c 100644 --- a/server_addon/tvpaint/server/settings/main.py +++ b/server_addon/tvpaint/server/settings/main.py @@ -1,7 +1,6 @@ from ayon_server.settings import ( BaseSettingsModel, SettingsField, - ensure_unique_names, ) from .imageio import TVPaintImageIOModel From 1e4dff2735b36b7e0302b120e0f96bab75d5da7a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:07:09 +0100 Subject: [PATCH 089/130] add missing functions and classes in '__all__ ' --- client/ayon_core/__init__.py | 12 ++++++++++++ .../hosts/aftereffects/api/__init__.py | 1 + client/ayon_core/modules/base.py | 15 +++++++++++++++ .../ayon_core/scripts/slates/slate_base/api.py | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/client/ayon_core/__init__.py b/client/ayon_core/__init__.py index 7d95587e8a..c1b93405f3 100644 --- a/client/ayon_core/__init__.py +++ b/client/ayon_core/__init__.py @@ -14,3 +14,15 @@ AYON_SERVER_ENABLED = True # Indicate if AYON entities should be used instead of OpenPype entities USE_AYON_ENTITIES = True # ------------------------- + + +__all__ = ( + "__version__", + + # Deprecated + "AYON_CORE_ROOT", + "PACKAGE_DIR", + "PLUGINS_DIR", + "AYON_SERVER_ENABLED", + "USE_AYON_ENTITIES", +) \ No newline at end of file diff --git a/client/ayon_core/hosts/aftereffects/api/__init__.py b/client/ayon_core/hosts/aftereffects/api/__init__.py index 4c4a8cce2f..b1d83c5ad9 100644 --- a/client/ayon_core/hosts/aftereffects/api/__init__.py +++ b/client/ayon_core/hosts/aftereffects/api/__init__.py @@ -31,6 +31,7 @@ __all__ = [ "get_stub", # pipeline + "AfterEffectsHost", "ls", "containerise", diff --git a/client/ayon_core/modules/base.py b/client/ayon_core/modules/base.py index 8a78edf961..3f2a7d4ea5 100644 --- a/client/ayon_core/modules/base.py +++ b/client/ayon_core/modules/base.py @@ -1,3 +1,5 @@ +# Backwards compatibility support +# - TODO should be removed before release 1.0.0 from ayon_core.addon import ( AYONAddon, AddonsManager, @@ -12,3 +14,16 @@ from ayon_core.addon.base import ( ModulesManager = AddonsManager TrayModulesManager = TrayAddonsManager load_modules = load_addons + + +__all__ = ( + "AYONAddon", + "AddonsManager", + "TrayAddonsManager", + "load_addons", + "OpenPypeModule", + "OpenPypeAddOn", + "ModulesManager", + "TrayModulesManager", + "load_modules", +) diff --git a/client/ayon_core/scripts/slates/slate_base/api.py b/client/ayon_core/scripts/slates/slate_base/api.py index cd64c68134..d1b4b22979 100644 --- a/client/ayon_core/scripts/slates/slate_base/api.py +++ b/client/ayon_core/scripts/slates/slate_base/api.py @@ -13,3 +13,21 @@ from .items import ( ) from .lib import create_slates from .example import example + + +__all__ = ( + "FontFactory", + "BaseObj", + "load_default_style", + "MainFrame", + "Layer", + "BaseItem", + "ItemImage", + "ItemRectangle", + "ItemPlaceHolder", + "ItemText", + "ItemTable", + "TableField", + "create_slates", + "example", +) From 0300c69806dade675712e7acea12ffaa7162de40 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:08:10 +0100 Subject: [PATCH 090/130] use 'get_project_settings' in substance painter --- .../ayon_core/hosts/substancepainter/api/pipeline.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/api/pipeline.py b/client/ayon_core/hosts/substancepainter/api/pipeline.py index c75cc3135a..cc24e41702 100644 --- a/client/ayon_core/hosts/substancepainter/api/pipeline.py +++ b/client/ayon_core/hosts/substancepainter/api/pipeline.py @@ -12,17 +12,15 @@ import substance_painter.project import pyblish.api from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost -from ayon_core.settings import ( - get_current_project_settings, - get_project_settings, -) +from ayon_core.settings import get_project_settings from ayon_core.pipeline.template_data import get_template_data_with_names from ayon_core.pipeline import ( + get_current_project_name, register_creator_plugin_path, register_loader_plugin_path, AVALON_CONTAINER_ID, - Anatomy + Anatomy, ) from ayon_core.lib import ( StringTemplate, @@ -76,7 +74,7 @@ class SubstanceHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): log.info("Installing menu ... ") self._install_menu() - project_settings = get_current_project_settings() + project_settings = get_project_settings(get_current_project_name()) self._install_shelves(project_settings) self._has_been_setup = True From defe61496b8625db46d96ecd7ed6d5787544b570 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:08:48 +0100 Subject: [PATCH 091/130] added missing import to validate knobs --- client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py b/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py index 281e172788..8bcde9609d 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py @@ -1,3 +1,5 @@ +import json + import nuke import six import pyblish.api From d0ba1123373deefab264b40754e71b87176fd038 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:09:33 +0100 Subject: [PATCH 092/130] revert 'parm' in comment --- client/ayon_core/hosts/houdini/api/colorspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/api/colorspace.py b/client/ayon_core/hosts/houdini/api/colorspace.py index 6a92c77e49..66581d6f20 100644 --- a/client/ayon_core/hosts/houdini/api/colorspace.py +++ b/client/ayon_core/hosts/houdini/api/colorspace.py @@ -59,7 +59,7 @@ class ARenderProduct(object): def get_default_display_view_colorspace(): """Returns the colorspace attribute of the default (display, view) pair. - It's used for 'ociocolorspace' param in OpenGL Node.""" + It's used for 'ociocolorspace' parm in OpenGL Node.""" prefs = get_color_management_preferences() return get_display_view_colorspace_name( From 7eeb7ccaa30a92662e10702fb1987df4ee9695b2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:12:51 +0100 Subject: [PATCH 093/130] add new line at the end of file --- client/ayon_core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/__init__.py b/client/ayon_core/__init__.py index c1b93405f3..ce5a28601c 100644 --- a/client/ayon_core/__init__.py +++ b/client/ayon_core/__init__.py @@ -25,4 +25,4 @@ __all__ = ( "PLUGINS_DIR", "AYON_SERVER_ENABLED", "USE_AYON_ENTITIES", -) \ No newline at end of file +) From 005271b28629e6cf5abe2c7c785661c2bd12beb4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:26:32 +0100 Subject: [PATCH 094/130] renamed 'get_reformated_filename' to 'get_reformatted_filename' --- client/ayon_core/hosts/flame/api/__init__.py | 4 ++-- client/ayon_core/hosts/flame/api/lib.py | 4 ++-- client/ayon_core/hosts/flame/otio/flame_export.py | 2 +- client/ayon_core/hosts/flame/otio/utils.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/flame/api/__init__.py b/client/ayon_core/hosts/flame/api/__init__.py index e2c5ee154a..8fcf0c92b0 100644 --- a/client/ayon_core/hosts/flame/api/__init__.py +++ b/client/ayon_core/hosts/flame/api/__init__.py @@ -23,7 +23,7 @@ from .lib import ( reset_segment_selection, get_segment_attributes, get_clips_in_reels, - get_reformated_filename, + get_reformatted_filename, get_frame_from_filename, get_padding_from_filename, maintained_object_duplication, @@ -101,7 +101,7 @@ __all__ = [ "reset_segment_selection", "get_segment_attributes", "get_clips_in_reels", - "get_reformated_filename", + "get_reformatted_filename", "get_frame_from_filename", "get_padding_from_filename", "maintained_object_duplication", diff --git a/client/ayon_core/hosts/flame/api/lib.py b/client/ayon_core/hosts/flame/api/lib.py index e1316658bf..8bfe6348ea 100644 --- a/client/ayon_core/hosts/flame/api/lib.py +++ b/client/ayon_core/hosts/flame/api/lib.py @@ -607,7 +607,7 @@ def get_clips_in_reels(project): return output_clips -def get_reformated_filename(filename, padded=True): +def get_reformatted_filename(filename, padded=True): """ Return fixed python expression path @@ -618,7 +618,7 @@ def get_reformated_filename(filename, padded=True): type: string with reformatted path Example: - get_reformated_filename("plate.1001.exr") > plate.%04d.exr + get_reformatted_filename("plate.1001.exr") > plate.%04d.exr """ found = FRAME_PATTERN.search(filename) diff --git a/client/ayon_core/hosts/flame/otio/flame_export.py b/client/ayon_core/hosts/flame/otio/flame_export.py index e5ea4dcf5e..cb038f9e9a 100644 --- a/client/ayon_core/hosts/flame/otio/flame_export.py +++ b/client/ayon_core/hosts/flame/otio/flame_export.py @@ -256,7 +256,7 @@ def create_otio_reference(clip_data, fps=None): if not otio_ex_ref_item: dirname, file_name = os.path.split(path) - file_name = utils.get_reformated_filename(file_name, padded=False) + file_name = utils.get_reformatted_filename(file_name, padded=False) reformated_path = os.path.join(dirname, file_name) # in case old OTIO or video file create `ExternalReference` otio_ex_ref_item = otio.schema.ExternalReference( diff --git a/client/ayon_core/hosts/flame/otio/utils.py b/client/ayon_core/hosts/flame/otio/utils.py index a1206b6710..5a28263fc2 100644 --- a/client/ayon_core/hosts/flame/otio/utils.py +++ b/client/ayon_core/hosts/flame/otio/utils.py @@ -21,7 +21,7 @@ def frames_to_seconds(frames, framerate): return otio.opentime.to_seconds(rt) -def get_reformated_filename(filename, padded=True): +def get_reformatted_filename(filename, padded=True): """ Return fixed python expression path @@ -32,7 +32,7 @@ def get_reformated_filename(filename, padded=True): type: string with reformatted path Example: - get_reformated_filename("plate.1001.exr") > plate.%04d.exr + get_reformatted_filename("plate.1001.exr") > plate.%04d.exr """ found = FRAME_PATTERN.search(filename) From c69955c7b500173c7ff1970c02d4ce384e7d0459 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:28:26 +0100 Subject: [PATCH 095/130] use correct variable name 'version_attributes' --- client/ayon_core/hosts/houdini/plugins/load/actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/actions.py b/client/ayon_core/hosts/houdini/plugins/load/actions.py index c277005919..fbd89ab9c2 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/actions.py +++ b/client/ayon_core/hosts/houdini/plugins/load/actions.py @@ -76,8 +76,8 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): return # Include handles - start -= version_data.get("handleStart", 0) - end += version_data.get("handleEnd", 0) + start -= version_attributes.get("handleStart", 0) + end += version_attributes.get("handleEnd", 0) hou.playbar.setFrameRange(start, end) hou.playbar.setPlaybackRange(start, end) From 52f4af67418dee20fc59fa4d9e13246b3c6a0717 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:28:52 +0100 Subject: [PATCH 096/130] use correct constant name 'NODE_TAB_NAME' --- client/ayon_core/hosts/nuke/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index deeab47885..a9c5aac659 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -921,7 +921,7 @@ def writes_version_sync(): for each in nuke.allNodes(filter="Write"): # check if the node is avalon tracked - if _NODE_TAB_NAME not in each.knobs(): + if NODE_TAB_NAME not in each.knobs(): continue avalon_knob_data = read_avalon_data(each) From 2c08de530471c88336730e289366e04418865e3d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:32:01 +0100 Subject: [PATCH 097/130] use full variable name in tvpaint --- client/ayon_core/hosts/tvpaint/plugins/create/create_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index 8d91afc74e..e3a24c79a2 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -599,7 +599,7 @@ class CreateRenderPass(TVPaintCreator): if filtered_layers: self.log.info(( "Changing group of " - f"{','.join([l['name'] for l in filtered_layers])}" + f"{','.join([layer['name'] for layer in filtered_layers])}" f" to {group_id}" )) george_lines = [ From 71b4bb4527c6b6a348d471bec98ee8ab1b964685 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:33:25 +0100 Subject: [PATCH 098/130] removed unused variables --- client/ayon_core/hosts/hiero/api/plugin.py | 1 - .../api/startup/Python/StartupUI/otioimporter/OTIOImport.py | 6 ------ .../hosts/hiero/plugins/publish/precollect_instances.py | 2 -- .../hiero/plugins/publish_old_workflow/precollect_retime.py | 4 ++-- .../ayon_core/hosts/maya/plugins/publish/collect_render.py | 1 - client/ayon_core/hosts/unreal/lib.py | 2 -- server_addon/create_ayon_addons.py | 1 - 7 files changed, 2 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/plugin.py b/client/ayon_core/hosts/hiero/api/plugin.py index 4878368716..a6264add33 100644 --- a/client/ayon_core/hosts/hiero/api/plugin.py +++ b/client/ayon_core/hosts/hiero/api/plugin.py @@ -449,7 +449,6 @@ class ClipLoader: repr = self.context["representation"] repr_cntx = repr["context"] folder_path = self.context["folder"]["path"] - folder_name = self.context["folder"]["name"] product_name = self.context["product"]["name"] representation = repr["name"] self.data["clip_name"] = self.clip_name_template.format(**repr_cntx) diff --git a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py index 8331c429df..5a84bbdfaf 100644 --- a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py +++ b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py @@ -254,12 +254,6 @@ def add_markers(otio_item, hiero_item, tagsbin): if _tag is None: _tag = hiero.core.Tag(marker_color_map[marker.color]) - start = marker.marked_range.start_time.value - end = ( - marker.marked_range.start_time.value + - marker.marked_range.duration.value - ) - tag = hiero_item.addTag(_tag) tag.setName(marker.name or marker_color_map[marker_color]) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index 26f6968884..d4d75d14ec 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -378,8 +378,6 @@ class PrecollectInstances(pyblish.api.ContextPlugin): # collect all subtrack items sub_track_items = {} for track in tracks: - items = track.items() - effet_items = track.subTrackItems() # skip if no clips on track > need track with effect only diff --git a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py b/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py index 297ffa8001..d0f28840b5 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py +++ b/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py @@ -36,8 +36,8 @@ class PrecollectRetime(api.InstancePlugin): speed = track_item.playbackSpeed() # calculate available material before retime - available_in = int(track_item.handleInLength() * speed) - available_out = int(track_item.handleOutLength() * speed) + # available_in = int(track_item.handleInLength() * speed) + # available_out = int(track_item.handleOutLength() * speed) self.log.debug(( "_BEFORE: \n timeline_in: `{0}`,\n timeline_out: `{1}`, \n " diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py index c981c37123..ff959afabc 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/maya/plugins/publish/collect_render.py @@ -78,7 +78,6 @@ class CollectMayaRender(pyblish.api.InstancePlugin): layer = instance.data["transientData"]["layer"] objset = instance.data.get("instance_node") filepath = context.data["currentFile"].replace("\\", "/") - workspace = context.data["workspaceDir"] # check if layer is renderable if not layer.isRenderable(): diff --git a/client/ayon_core/hosts/unreal/lib.py b/client/ayon_core/hosts/unreal/lib.py index fe9e239ed5..cbb589ad1e 100644 --- a/client/ayon_core/hosts/unreal/lib.py +++ b/client/ayon_core/hosts/unreal/lib.py @@ -216,10 +216,8 @@ def create_unreal_project(project_name: str, since 3.16.0 """ - env = env or os.environ preset = get_project_settings(project_name)["unreal"]["project_setup"] - ue_id = ".".join(ue_version.split(".")[:2]) # get unreal engine identifier # ------------------------------------------------------------------------- # FIXME (antirotor): As of 4.26 this is problem with UE4 built from diff --git a/server_addon/create_ayon_addons.py b/server_addon/create_ayon_addons.py index 9553980f5d..8464a2f127 100644 --- a/server_addon/create_ayon_addons.py +++ b/server_addon/create_ayon_addons.py @@ -220,7 +220,6 @@ def main( addons=None, ): current_dir = Path(os.path.dirname(os.path.abspath(__file__))) - root_dir = current_dir.parent create_zip = not skip_zip if output_dir: From 7a005dbb8a1ffea70829fb7fa110f2f23d74c1bc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:33:40 +0100 Subject: [PATCH 099/130] fix typo in 'effet_items' > 'effect_items' --- .../hosts/hiero/plugins/publish/precollect_instances.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py index d4d75d14ec..e10f2c7e13 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py +++ b/client/ayon_core/hosts/hiero/plugins/publish/precollect_instances.py @@ -378,10 +378,10 @@ class PrecollectInstances(pyblish.api.ContextPlugin): # collect all subtrack items sub_track_items = {} for track in tracks: - effet_items = track.subTrackItems() + effect_items = track.subTrackItems() # skip if no clips on track > need track with effect only - if not effet_items: + if not effect_items: continue # skip all disabled tracks @@ -389,7 +389,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin): continue track_index = track.trackIndex() - _sub_track_items = phiero.flatten(effet_items) + _sub_track_items = phiero.flatten(effect_items) _sub_track_items = list(_sub_track_items) # continue only if any subtrack items are collected From 89c310e9c9b77efceafffe7bafba43fba59cb13d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:34:31 +0100 Subject: [PATCH 100/130] removed unnecessary f-strings --- .../hosts/harmony/plugins/create/create_farm_render.py | 4 ++-- .../ayon_core/hosts/harmony/plugins/publish/collect_scene.py | 4 ++-- .../hosts/max/plugins/publish/validate_renderpasses.py | 2 +- client/ayon_core/hosts/unreal/plugins/create/create_render.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py b/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py index 16c403de6a..3039d56ead 100644 --- a/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py +++ b/client/ayon_core/hosts/harmony/plugins/create/create_farm_render.py @@ -21,12 +21,12 @@ class CreateFarmRender(plugin.Creator): path = "render/{0}/{0}.".format(node.split("/")[-1]) harmony.send( { - "function": f"PypeHarmony.Creators.CreateRender.create", + "function": "PypeHarmony.Creators.CreateRender.create", "args": [node, path] }) harmony.send( { - "function": f"PypeHarmony.color", + "function": "PypeHarmony.color", "args": [[0.9, 0.75, 0.3, 1.0]] } ) diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_scene.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_scene.py index a60e44b69b..bc2ccca1be 100644 --- a/client/ayon_core/hosts/harmony/plugins/publish/collect_scene.py +++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_scene.py @@ -17,7 +17,7 @@ class CollectScene(pyblish.api.ContextPlugin): """Plugin entry point.""" result = harmony.send( { - f"function": "PypeHarmony.getSceneSettings", + "function": "PypeHarmony.getSceneSettings", "args": []} )["result"] @@ -62,7 +62,7 @@ class CollectScene(pyblish.api.ContextPlugin): result = harmony.send( { - f"function": "PypeHarmony.getVersion", + "function": "PypeHarmony.getVersion", "args": []} )["result"] context.data["harmonyVersion"] = "{}.{}".format(result[0], result[1]) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index ba948747b9..394d3119c4 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -140,7 +140,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid = [] if instance.name not in file_name: cls.log.error("The renderpass filename should contain the instance name.") - invalid.append((f"Invalid instance name", + invalid.append(("Invalid instance name", file_name)) if renderpass is not None: if not file_name.rstrip(".").endswith(renderpass): diff --git a/client/ayon_core/hosts/unreal/plugins/create/create_render.py b/client/ayon_core/hosts/unreal/plugins/create/create_render.py index cbec84c543..5a96d9809c 100644 --- a/client/ayon_core/hosts/unreal/plugins/create/create_render.py +++ b/client/ayon_core/hosts/unreal/plugins/create/create_render.py @@ -50,7 +50,7 @@ class CreateRender(UnrealAssetCreator): # If the option to create a new level sequence is selected, # create a new level sequence and a master level. - root = f"/Game/Ayon/Sequences" + root = "/Game/Ayon/Sequences" # Create a new folder for the sequence in root sequence_dir_name = create_folder(root, product_name) @@ -166,7 +166,7 @@ class CreateRender(UnrealAssetCreator): master_lvl = levels[0].get_asset().get_path_name() except IndexError: raise RuntimeError( - f"Could not find the hierarchy for the selected sequence.") + "Could not find the hierarchy for the selected sequence.") # If the selected asset is the master sequence, we get its data # and then we create the instance for the master sequence. From e309c34202f79c7aed1dc341eb1958d478890aa5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:34:47 +0100 Subject: [PATCH 101/130] remove unnecessary f-strings in unreal lib --- client/ayon_core/hosts/unreal/lib.py | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/hosts/unreal/lib.py b/client/ayon_core/hosts/unreal/lib.py index cbb589ad1e..37122b2096 100644 --- a/client/ayon_core/hosts/unreal/lib.py +++ b/client/ayon_core/hosts/unreal/lib.py @@ -236,10 +236,12 @@ def create_unreal_project(project_name: str, project_file = pr_dir / f"{unreal_project_name}.uproject" print("--- Generating a new project ...") - commandlet_cmd = [f'{ue_editor_exe.as_posix()}', - f'{cmdlet_project.as_posix()}', - f'-run=AyonGenerateProject', - f'{project_file.resolve().as_posix()}'] + commandlet_cmd = [ + ue_editor_exe.as_posix(), + cmdlet_project.as_posix(), + "-run=AyonGenerateProject", + project_file.resolve().as_posix() + ] if dev_mode or preset["dev_mode"]: commandlet_cmd.append('-GenerateCode') @@ -266,7 +268,7 @@ def create_unreal_project(project_name: str, pf.seek(0) json.dump(pf_json, pf, indent=4) pf.truncate() - print(f'--- Engine ID has been written into the project file') + print("--- Engine ID has been written into the project file") if dev_mode or preset["dev_mode"]: u_build_tool = get_path_to_ubt(engine_path, ue_version) @@ -280,17 +282,25 @@ def create_unreal_project(project_name: str, # we need to test this out arch = "Mac" - command1 = [u_build_tool.as_posix(), "-projectfiles", - f"-project={project_file}", "-progress"] + command1 = [ + u_build_tool.as_posix(), + "-projectfiles", + f"-project={project_file}", + "-progress" + ] subprocess.run(command1) - command2 = [u_build_tool.as_posix(), - f"-ModuleWithSuffix={unreal_project_name},3555", arch, - "Development", "-TargetType=Editor", - f'-Project={project_file}', - f'{project_file}', - "-IgnoreJunk"] + command2 = [ + u_build_tool.as_posix(), + f"-ModuleWithSuffix={unreal_project_name},3555", + arch, + "Development", + "-TargetType=Editor", + f"-Project={project_file}", + project_file, + "-IgnoreJunk" + ] subprocess.run(command2) From d60a7be65cce914959f0b6fb0318961f6767f3fc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:35:11 +0100 Subject: [PATCH 102/130] change formatting in hiero import functionality --- .../hosts/hiero/api/otio/hiero_import.py | 36 ++++------ .../StartupUI/otioimporter/OTIOImport.py | 67 ++++++------------- 2 files changed, 34 insertions(+), 69 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/otio/hiero_import.py b/client/ayon_core/hosts/hiero/api/otio/hiero_import.py index f123b81ca6..29ff7f7325 100644 --- a/client/ayon_core/hosts/hiero/api/otio/hiero_import.py +++ b/client/ayon_core/hosts/hiero/api/otio/hiero_import.py @@ -101,7 +101,7 @@ def apply_transition(otio_track, otio_item, track): if transition_type == 'dissolve': transition_func = getattr( hiero.core.Transition, - 'create{kind}DissolveTransition'.format(kind=kind) + "create{kind}DissolveTransition".format(kind=kind) ) try: @@ -109,7 +109,7 @@ def apply_transition(otio_track, otio_item, track): item_in, item_out, otio_item.in_offset.value, - otio_item.out_offset.value + otio_item.out_offset.value, ) # Catch error raised if transition is bigger than TrackItem source @@ -134,7 +134,7 @@ def apply_transition(otio_track, otio_item, track): transition = transition_func( item_out, - otio_item.out_offset.value + otio_item.out_offset.value, ) elif transition_type == 'fade_out': @@ -183,9 +183,7 @@ def prep_url(url_in): def create_offline_mediasource(otio_clip, path=None): global _otio_old - hiero_rate = hiero.core.TimeBase( - otio_clip.source_range.start_time.rate - ) + hiero_rate = hiero.core.TimeBase(otio_clip.source_range.start_time.rate) try: legal_media_refs = ( @@ -212,7 +210,7 @@ def create_offline_mediasource(otio_clip, path=None): source_range.start_time.value, source_range.duration.value, hiero_rate, - source_range.start_time.value + source_range.start_time.value, ) return media @@ -385,7 +383,8 @@ def create_trackitem(playhead, track, otio_clip, clip): # Only reverse effect can be applied here if abs(time_scalar) == 1.: trackitem.setPlaybackSpeed( - trackitem.playbackSpeed() * time_scalar) + trackitem.playbackSpeed() * time_scalar + ) elif isinstance(effect, otio.schema.FreezeFrame): # For freeze frame, playback speed must be set after range @@ -397,28 +396,21 @@ def create_trackitem(playhead, track, otio_clip, clip): source_in = source_range.end_time_inclusive().value timeline_in = playhead + source_out - timeline_out = ( - timeline_in + - source_range.duration.value - ) - 1 + timeline_out = (timeline_in + source_range.duration.value) - 1 else: # Normal playback speed source_in = source_range.start_time.value source_out = source_range.end_time_inclusive().value timeline_in = playhead - timeline_out = ( - timeline_in + - source_range.duration.value - ) - 1 + timeline_out = (timeline_in + source_range.duration.value) - 1 # Set source and timeline in/out points trackitem.setTimes( timeline_in, timeline_out, source_in, - source_out - + source_out, ) # Apply playback speed for freeze frames @@ -435,7 +427,8 @@ def create_trackitem(playhead, track, otio_clip, clip): def build_sequence( - otio_timeline, project=None, sequence=None, track_kind=None): + otio_timeline, project=None, sequence=None, track_kind=None +): if project is None: if sequence: project = sequence.project() @@ -509,10 +502,7 @@ def build_sequence( # Create TrackItem trackitem = create_trackitem( - playhead, - track, - otio_clip, - clip + playhead, track, otio_clip, clip ) # Add markers diff --git a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py index 5a84bbdfaf..01fd047123 100644 --- a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py +++ b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py @@ -101,14 +101,14 @@ def apply_transition(otio_track, otio_item, track): if transition_type == "dissolve": transition_func = getattr( hiero.core.Transition, - 'create{kind}DissolveTransition'.format(kind=kind) + "create{kind}DissolveTransition".format(kind=kind) ) transition = transition_func( item_in, item_out, otio_item.in_offset.value, - otio_item.out_offset.value + otio_item.out_offset.value, ) elif transition_type == "fade_in": @@ -116,20 +116,14 @@ def apply_transition(otio_track, otio_item, track): hiero.core.Transition, 'create{kind}FadeInTransition'.format(kind=kind) ) - transition = transition_func( - item_out, - otio_item.out_offset.value - ) + transition = transition_func(item_out, otio_item.out_offset.value) elif transition_type == "fade_out": transition_func = getattr( hiero.core.Transition, 'create{kind}FadeOutTransition'.format(kind=kind) ) - transition = transition_func( - item_in, - otio_item.in_offset.value - ) + transition = transition_func(item_in, otio_item.in_offset.value) else: # Unknown transition @@ -138,11 +132,10 @@ def apply_transition(otio_track, otio_item, track): # Apply transition to track track.addTransition(transition) - except Exception, e: + except Exception as e: sys.stderr.write( 'Unable to apply transition "{t}": "{e}"\n'.format( - t=otio_item, - e=e + t=otio_item, e=e ) ) @@ -153,18 +146,13 @@ def prep_url(url_in): if url.startswith("file://localhost/"): return url.replace("file://localhost/", "") - url = '{url}'.format( - sep=url.startswith(os.sep) and "" or os.sep, - url=url.startswith(os.sep) and url[1:] or url - ) + url = "{url}".format(url=url.startswith(os.sep) and url[1:] or url) return url def create_offline_mediasource(otio_clip, path=None): - hiero_rate = hiero.core.TimeBase( - otio_clip.source_range.start_time.rate - ) + hiero_rate = hiero.core.TimeBase(otio_clip.source_range.start_time.rate) if isinstance(otio_clip.media_reference, otio.schema.ExternalReference): source_range = otio_clip.available_range() @@ -180,7 +168,7 @@ def create_offline_mediasource(otio_clip, path=None): source_range.start_time.value, source_range.duration.value, hiero_rate, - source_range.start_time.value + source_range.start_time.value, ) return media @@ -203,7 +191,7 @@ marker_color_map = { "MAGENTA": "Magenta", "BLACK": "Blue", "WHITE": "Green", - "MINT": "Cyan" + "MINT": "Cyan", } @@ -269,12 +257,12 @@ def create_track(otio_track, tracknum, track_kind): # Create a Track if otio_track.kind == otio.schema.TrackKind.Video: track = hiero.core.VideoTrack( - otio_track.name or 'Video{n}'.format(n=tracknum) + otio_track.name or "Video{n}".format(n=tracknum) ) else: track = hiero.core.AudioTrack( - otio_track.name or 'Audio{n}'.format(n=tracknum) + otio_track.name or "Audio{n}".format(n=tracknum) ) return track @@ -309,34 +297,25 @@ def create_trackitem(playhead, track, otio_clip, clip, tagsbin): for effect in otio_clip.effects: if isinstance(effect, otio.schema.LinearTimeWarp): trackitem.setPlaybackSpeed( - trackitem.playbackSpeed() * - effect.time_scalar + trackitem.playbackSpeed() * effect.time_scalar ) # If reverse playback speed swap source in and out if trackitem.playbackSpeed() < 0: source_out = source_range.start_time.value source_in = ( - source_range.start_time.value + - source_range.duration.value + source_range.start_time.value + source_range.duration.value ) - 1 timeline_in = playhead + source_out - timeline_out = ( - timeline_in + - source_range.duration.value - ) - 1 + timeline_out = (timeline_in + source_range.duration.value) - 1 else: # Normal playback speed source_in = source_range.start_time.value source_out = ( - source_range.start_time.value + - source_range.duration.value + source_range.start_time.value + source_range.duration.value ) - 1 timeline_in = playhead - timeline_out = ( - timeline_in + - source_range.duration.value - ) - 1 + timeline_out = (timeline_in + source_range.duration.value) - 1 # Set source and timeline in/out points trackitem.setSourceIn(source_in) @@ -351,7 +330,8 @@ def create_trackitem(playhead, track, otio_clip, clip, tagsbin): def build_sequence( - otio_timeline, project=None, sequence=None, track_kind=None): + otio_timeline, project=None, sequence=None, track_kind=None +): if project is None: if sequence: @@ -408,8 +388,7 @@ def build_sequence( if isinstance(otio_clip, otio.schema.Stack): bar = hiero.ui.mainWindow().statusBar() bar.showMessage( - "Nested sequences are created separately.", - timeout=3000 + "Nested sequences are created separately.", timeout=3000 ) build_sequence(otio_clip, project, otio_track.kind) @@ -422,11 +401,7 @@ def build_sequence( # Create TrackItem trackitem = create_trackitem( - playhead, - track, - otio_clip, - clip, - tagsbin + playhead, track, otio_clip, clip, tagsbin ) # Add trackitem to track From 0c2a54c5679d65d875b3f851dd5619477591cc05 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:35:39 +0100 Subject: [PATCH 103/130] fix invalid formatting --- client/ayon_core/hosts/houdini/plugins/load/load_alembic.py | 2 +- .../houdini/plugins/publish/validate_cop_output_node.py | 6 ++++-- .../maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py index a77d06d409..37657cbdff 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py @@ -59,7 +59,7 @@ class AbcLoader(load.LoaderPlugin): normal_node.setInput(0, unpack) - null = container.createNode("null", node_name="OUT".format(name)) + null = container.createNode("null", node_name="OUT") null.setInput(0, normal_node) # Ensure display flag is on the Alembic input node and not on the OUT diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_cop_output_node.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_cop_output_node.py index 95414ae7f1..fdf03d5cba 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_cop_output_node.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_cop_output_node.py @@ -71,6 +71,8 @@ class ValidateCopOutputNode(pyblish.api.InstancePlugin): # the isinstance check above should be stricter than this category if output_node.type().category().name() != "Cop2": raise PublishValidationError( - ("Output node %s is not of category Cop2. " - "This is a bug...").format(output_node.path()), + ( + "Output node {} is not of category Cop2." + " This is a bug..." + ).format(output_node.path()), title=cls.label) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py index edbb5f845e..6292afcf41 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py @@ -74,7 +74,7 @@ class ExtractUnrealSkeletalMeshFbx(publish.Extractor): renamed_to_extract.append("|".join(node_path)) with renamed(original_parent, parent_node): - self.log.debug("Extracting: {}".format(renamed_to_extract, path)) + self.log.debug("Extracting: {}".format(renamed_to_extract)) fbx_exporter.export(renamed_to_extract, path) if "representations" not in instance.data: From a817a0a4d288b68ff52bfe42bed4ff075d318932 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:36:07 +0100 Subject: [PATCH 104/130] use isinstance for type check --- client/ayon_core/hosts/hiero/api/tags.py | 2 +- .../deadline/plugins/publish/submit_maya_deadline.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/tags.py b/client/ayon_core/hosts/hiero/api/tags.py index 32620aa2f5..5abfee75d0 100644 --- a/client/ayon_core/hosts/hiero/api/tags.py +++ b/client/ayon_core/hosts/hiero/api/tags.py @@ -89,7 +89,7 @@ def update_tag(tag, data): # set all data metadata to tag metadata for _k, _v in data_mtd.items(): value = str(_v) - if type(_v) == dict: + if isinstance(_v, dict): value = json.dumps(_v) # set the value diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py index 0e871eb90e..dcc589ffd4 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -856,10 +856,10 @@ def _format_tiles( """ # Math used requires integers for correct output - as such # we ensure our inputs are correct. - assert type(tiles_x) is int, "tiles_x must be an integer" - assert type(tiles_y) is int, "tiles_y must be an integer" - assert type(width) is int, "width must be an integer" - assert type(height) is int, "height must be an integer" + assert isinstance(tiles_x, int), "tiles_x must be an integer" + assert isinstance(tiles_y, int), "tiles_y must be an integer" + assert isinstance(width, int), "width must be an integer" + assert isinstance(height, int), "height must be an integer" out = {"JobInfo": {}, "PluginInfo": {}} cfg = OrderedDict() From b3aba193251e96806b206173700d81ba77e8fb49 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:37:04 +0100 Subject: [PATCH 105/130] use "not in" expression --- .../api/startup/Python/StartupUI/PimpMySpreadsheet.py | 2 +- client/ayon_core/hosts/maya/api/lib.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/PimpMySpreadsheet.py b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/PimpMySpreadsheet.py index b8dfb07b47..fcfa24310e 100644 --- a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/PimpMySpreadsheet.py +++ b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/PimpMySpreadsheet.py @@ -641,7 +641,7 @@ def _setStatus(self, status): global gStatusTags # Get a valid Tag object from the Global list of statuses - if not status in gStatusTags.keys(): + if status not in gStatusTags.keys(): print("Status requested was not a valid Status string.") return diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index cad5b0405f..9c57b885f1 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -2152,9 +2152,13 @@ def get_related_sets(node): sets = cmds.ls(sets) # Ignore `avalon.container` - sets = [s for s in sets if - not cmds.attributeQuery("id", node=s, exists=True) or - not cmds.getAttr("%s.id" % s) in ignored] + sets = [ + s for s in sets + if ( + not cmds.attributeQuery("id", node=s, exists=True) + or cmds.getAttr("%s.id" % s) not in ignored + ) + ] # Exclude deformer sets (`type=2` for `maya.cmds.listSets`) deformer_sets = cmds.listSets(object=node, From 85a9c0559cc896336fca09b38dbba25941a21501 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:37:17 +0100 Subject: [PATCH 106/130] sorter is not lamda function --- client/ayon_core/hosts/maya/tools/mayalookassigner/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py b/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py index f5dad25ff0..b0807be6a6 100644 --- a/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py +++ b/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py @@ -29,7 +29,8 @@ class AssetModel(models.TreeModel): self.beginResetModel() # Add the items sorted by label - sorter = lambda x: x["label"] + def sorter(x): + return x["label"] for item in sorted(items, key=sorter): From d7afb03a5373fd1b8237ae561f9825811c077a8d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:41:52 +0100 Subject: [PATCH 107/130] fix george script in tvpaint auto create plugin --- .../ayon_core/hosts/tvpaint/plugins/create/create_render.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py index 8d91afc74e..09533c9057 100644 --- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py +++ b/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py @@ -760,7 +760,9 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): grg_lines: list[str] = [] for group_id, group_name in new_group_name_by_id.items(): group: dict[str, Any] = groups_by_id[group_id] - grg_line: str = "tv_layercolor \"setcolor\" {} {} {} {} {}".format( + grg_line: str = ( + "tv_layercolor \"setcolor\" {} {} {} {} {} \"{}\"" + ).format( group["clip_id"], group_id, group["red"], From 3d4aa23c5ed2eb66ddd0a512a3d79fc194500d51 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 12:49:57 +0100 Subject: [PATCH 108/130] use correct variable in maya legacy convertor plugin --- client/ayon_core/hosts/maya/plugins/create/convert_legacy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py index 685602ef0b..81cf9613b4 100644 --- a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py +++ b/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py @@ -83,7 +83,7 @@ class MayaLegacyConvertor(ProductConvertorPlugin, ).format(product_type)) continue - creator_id = product_type_to_id[family] + creator_id = product_type_to_id[product_type] creator = self.create_context.creators[creator_id] data["creator_identifier"] = creator_id From 54406c0489d6973040936942f6ca49d77d453b50 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:53:09 +0100 Subject: [PATCH 109/130] forgotten formatting change Co-authored-by: Petr Kalis --- .../api/startup/Python/StartupUI/otioimporter/OTIOImport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py index 01fd047123..cd775e8eea 100644 --- a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py +++ b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py @@ -121,7 +121,7 @@ def apply_transition(otio_track, otio_item, track): elif transition_type == "fade_out": transition_func = getattr( hiero.core.Transition, - 'create{kind}FadeOutTransition'.format(kind=kind) + "create{kind}FadeOutTransition".format(kind=kind) ) transition = transition_func(item_in, otio_item.in_offset.value) From 93b6ded5df3e661340d9c209ca2543e6f50dd500 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 27 Mar 2024 13:36:25 +0100 Subject: [PATCH 110/130] Refactor task field to use None instead of empty string Changed the task field in base_instance_data dictionary from an empty string to None for better clarity and consistency. --- .../hosts/traypublisher/plugins/create/create_editorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py index a9ee343dfb..843729786c 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py @@ -675,7 +675,7 @@ or updating already created. Publishing will create OTIO file. base_instance_data = { "shotName": shot_name, "variant": variant_name, - "task": "", + "task": None, "newAssetPublishing": True, "trackStartFrame": track_start_frame, "timelineOffset": timeline_offset, From 8b1ff955669976a6c47c7e6994e6ee634c375a10 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 27 Mar 2024 13:36:41 +0100 Subject: [PATCH 111/130] Refactor folder path extraction logic for validation plugin Adjust how folder paths are processed for better accuracy. --- .../ayon_core/plugins/publish/validate_editorial_asset_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py index 33b4210ad5..eba816d275 100644 --- a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py +++ b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py @@ -35,7 +35,7 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): existing_folder_paths = { folder_entity["path"]: ( - folder_entity["path"].lstrip("/").rsplit("/")[0] + folder_entity["path"].lstrip("/").rsplit("/")[:-1] ) for folder_entity in folder_entities } From 9ebb59cc358741c354c304275b7ed853bd06e423 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 27 Mar 2024 13:54:16 +0100 Subject: [PATCH 112/130] Refactor folder path handling for validation plugin Improved folder path processing for better accuracy in validation. --- .../ayon_core/plugins/publish/validate_editorial_asset_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py index eba816d275..ad47d00006 100644 --- a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py +++ b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py @@ -35,7 +35,7 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): existing_folder_paths = { folder_entity["path"]: ( - folder_entity["path"].lstrip("/").rsplit("/")[:-1] + folder_entity["path"].lstrip("/").split("/")[:-1] ) for folder_entity in folder_entities } From 87205c5f8e8f29e40180b5010d1f3bbf161c65ef Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:59:04 +0100 Subject: [PATCH 113/130] use f-string Co-authored-by: Roy Nieterau --- client/ayon_core/hosts/maya/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/lib.py b/client/ayon_core/hosts/maya/api/lib.py index 9c57b885f1..acfac760e6 100644 --- a/client/ayon_core/hosts/maya/api/lib.py +++ b/client/ayon_core/hosts/maya/api/lib.py @@ -2156,7 +2156,7 @@ def get_related_sets(node): s for s in sets if ( not cmds.attributeQuery("id", node=s, exists=True) - or cmds.getAttr("%s.id" % s) not in ignored + or cmds.getAttr(f"{s}.id") not in ignored ) ] From 1ba2b97e9f26d51b264651afd98dfb5069ef22f2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:00:00 +0100 Subject: [PATCH 114/130] simplify url strip Co-authored-by: Roy Nieterau --- .../api/startup/Python/StartupUI/otioimporter/OTIOImport.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py index cd775e8eea..d2fe608d99 100644 --- a/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py +++ b/client/ayon_core/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py @@ -146,7 +146,8 @@ def prep_url(url_in): if url.startswith("file://localhost/"): return url.replace("file://localhost/", "") - url = "{url}".format(url=url.startswith(os.sep) and url[1:] or url) + if url.startswith(os.sep): + url = url[1:] return url From 4134b8947bebef737ebe484399dd39ff7a2ca1f0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 14:02:22 +0100 Subject: [PATCH 115/130] remove commented out variables --- .../hiero/plugins/publish_old_workflow/precollect_retime.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py b/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py index d0f28840b5..8503a0b6a7 100644 --- a/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py +++ b/client/ayon_core/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py @@ -35,10 +35,6 @@ class PrecollectRetime(api.InstancePlugin): source_out = int(track_item.sourceOut()) speed = track_item.playbackSpeed() - # calculate available material before retime - # available_in = int(track_item.handleInLength() * speed) - # available_out = int(track_item.handleOutLength() * speed) - self.log.debug(( "_BEFORE: \n timeline_in: `{0}`,\n timeline_out: `{1}`, \n " "source_in: `{2}`,\n source_out: `{3}`,\n speed: `{4}`,\n " From 37e7b31feb4f336a3e78c4c0eca9a80654b7fa77 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 27 Mar 2024 14:19:32 +0100 Subject: [PATCH 116/130] Refactor parent folder handling in validation plugin - Added comments to clarify the list of parents for context and folders. --- .../ayon_core/plugins/publish/validate_editorial_asset_name.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py index ad47d00006..f1a2eb38bb 100644 --- a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py +++ b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py @@ -23,7 +23,7 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): ] def process(self, context): - + # list of parents for current context, converted from folderPath folder_and_parents = self.get_parents(context) self.log.debug("__ folder_and_parents: {}".format(folder_and_parents)) @@ -35,6 +35,7 @@ class ValidateEditorialAssetName(pyblish.api.ContextPlugin): existing_folder_paths = { folder_entity["path"]: ( + # list of parents for current folder folder_entity["path"].lstrip("/").split("/")[:-1] ) for folder_entity in folder_entities From 4ce342d356dcd86c06c0964fbebf266a12055dbc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:21:49 +0100 Subject: [PATCH 117/130] check against lowered value --- client/ayon_core/hosts/traypublisher/api/editorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/client/ayon_core/hosts/traypublisher/api/editorial.py index 09a2ab17ac..c71dae336c 100644 --- a/client/ayon_core/hosts/traypublisher/api/editorial.py +++ b/client/ayon_core/hosts/traypublisher/api/editorial.py @@ -186,7 +186,7 @@ class ShotMetadataSolver: # in case first parent is project then start parents from start if ( _index == 0 - and parent_token_type == "Project" + and parent_token_type.lower() == "project" ): project_parent = parents[0] parents = [project_parent] From d073f1afdf4c4f8aa0946d220a3d286d56be725c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 14:33:19 +0100 Subject: [PATCH 118/130] removed validate editorial asset name --- .../publish/validate_editorial_asset_name.py | 123 ------------------ 1 file changed, 123 deletions(-) delete mode 100644 client/ayon_core/plugins/publish/validate_editorial_asset_name.py diff --git a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py b/client/ayon_core/plugins/publish/validate_editorial_asset_name.py deleted file mode 100644 index f1a2eb38bb..0000000000 --- a/client/ayon_core/plugins/publish/validate_editorial_asset_name.py +++ /dev/null @@ -1,123 +0,0 @@ -from pprint import pformat - -import ayon_api -import pyblish.api - -from ayon_core.pipeline import KnownPublishError - - -class ValidateEditorialAssetName(pyblish.api.ContextPlugin): - """ Validating if editorial's folder names are not already created in db. - - Checking variations of names with different size of caps or with - or without underscores. - """ - - order = pyblish.api.ValidatorOrder - label = "Validate Editorial Folder Name" - hosts = [ - "hiero", - "resolve", - "flame", - "traypublisher" - ] - - def process(self, context): - # list of parents for current context, converted from folderPath - folder_and_parents = self.get_parents(context) - self.log.debug("__ folder_and_parents: {}".format(folder_and_parents)) - - project_name = context.data["projectName"] - folder_entities = list(ayon_api.get_folders( - project_name, fields={"path"} - )) - self.log.debug("__ folder_entities: {}".format(folder_entities)) - - existing_folder_paths = { - folder_entity["path"]: ( - # list of parents for current folder - folder_entity["path"].lstrip("/").split("/")[:-1] - ) - for folder_entity in folder_entities - } - - self.log.debug("__ project_entities: {}".format( - pformat(existing_folder_paths))) - - folders_missing_name = {} - folders_wrong_parent = {} - for folder_path in folder_and_parents.keys(): - if folder_path not in existing_folder_paths.keys(): - # add to some nonexistent list for next layer of check - folders_missing_name[folder_path] = ( - folder_and_parents[folder_path] - ) - continue - - existing_parents = existing_folder_paths[folder_path] - if folder_and_parents[folder_path] != existing_parents: - # add to some nonexistent list for next layer of check - folders_wrong_parent[folder_path] = { - "required": folder_and_parents[folder_path], - "already_in_db": existing_folder_paths[folder_path] - } - continue - - self.log.debug("correct folder: {}".format(folder_path)) - - if folders_missing_name: - wrong_names = {} - self.log.debug( - ">> folders_missing_name: {}".format(folders_missing_name)) - - # This will create set of folder paths - folder_paths = { - folder_path.lower().replace("_", "") - for folder_path in existing_folder_paths - } - - for folder_path in folders_missing_name: - _folder_path = folder_path.lower().replace("_", "") - if _folder_path in folder_paths: - wrong_names[folder_path].update( - { - "required_name": folder_path, - "used_variants_in_db": [ - p - for p in existing_folder_paths - if p.lower().replace("_", "") == _folder_path - ] - } - ) - - if wrong_names: - self.log.debug( - ">> wrong_names: {}".format(wrong_names)) - raise Exception( - "Some already existing folder name variants `{}`".format( - wrong_names)) - - if folders_wrong_parent: - self.log.debug( - ">> folders_wrong_parent: {}".format(folders_wrong_parent)) - raise KnownPublishError( - "Wrong parents on folders `{}`".format(folders_wrong_parent)) - - def get_parents(self, context): - output = {} - for instance in context: - folder_path = instance.data["folderPath"] - families = instance.data.get("families", []) + [ - instance.data["family"] - ] - # filter out non-shot families - if "shot" not in families: - continue - - parents = instance.data["parents"] - - output[folder_path] = [ - str(p["entity_name"]) for p in parents - if p.get("entity_type") != "project" - ] - return output From e6e722c05a2613472f1917d59ac1a0df4035be98 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 14:37:54 +0100 Subject: [PATCH 119/130] use 'get_current_project_settings' --- client/ayon_core/hosts/substancepainter/api/pipeline.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/api/pipeline.py b/client/ayon_core/hosts/substancepainter/api/pipeline.py index cc24e41702..23d629533c 100644 --- a/client/ayon_core/hosts/substancepainter/api/pipeline.py +++ b/client/ayon_core/hosts/substancepainter/api/pipeline.py @@ -12,11 +12,10 @@ import substance_painter.project import pyblish.api from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost -from ayon_core.settings import get_project_settings +from ayon_core.settings import get_current_project_settings from ayon_core.pipeline.template_data import get_template_data_with_names from ayon_core.pipeline import ( - get_current_project_name, register_creator_plugin_path, register_loader_plugin_path, AVALON_CONTAINER_ID, @@ -74,7 +73,7 @@ class SubstanceHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): log.info("Installing menu ... ") self._install_menu() - project_settings = get_project_settings(get_current_project_name()) + project_settings = get_current_project_settings() self._install_shelves(project_settings) self._has_been_setup = True From 6717db4482c43f87b153f98567db20a183849b28 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 27 Mar 2024 15:30:16 +0100 Subject: [PATCH 120/130] Update ColorRGBA import and add regex validation for output name field. - Import statement changed to only import ColorRGBA_uint8. - Added regex validation for the 'name' field in ExtractOIIOTranscodeOutputModel. --- server/settings/publish_plugins.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index f9ac1059ac..e61bf6986b 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -9,7 +9,7 @@ from ayon_server.settings import ( task_types_enum, ) -from ayon_server.types import ColorRGB_uint8, ColorRGBA_uint8 +from ayon_server.types import ColorRGBA_uint8 class ValidateBaseModel(BaseSettingsModel): @@ -221,7 +221,12 @@ class OIIOToolArgumentsModel(BaseSettingsModel): class ExtractOIIOTranscodeOutputModel(BaseSettingsModel): _layout = "expanded" - name: str = SettingsField("", title="Name") + name: str = SettingsField( + "", + title="Name", + description="Output name (no space)", + regex=r"[a-zA-Z0-9_]([a-zA-Z0-9_\.\-]*[a-zA-Z0-9_])?$", + ) extension: str = SettingsField("", title="Extension") transcoding_type: str = SettingsField( "colorspace", From 9669f62a44f8f33f5ae70e62986db807e0d5881e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 27 Mar 2024 16:06:52 +0100 Subject: [PATCH 121/130] removed pysync from vendor --- .../ayon_core/vendor/python/common/pysync.py | 216 ------------------ 1 file changed, 216 deletions(-) delete mode 100644 client/ayon_core/vendor/python/common/pysync.py diff --git a/client/ayon_core/vendor/python/common/pysync.py b/client/ayon_core/vendor/python/common/pysync.py deleted file mode 100644 index 14a6dda34c..0000000000 --- a/client/ayon_core/vendor/python/common/pysync.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/local/bin/python3 -# https://github.com/snullp/pySync/blob/master/pySync.py - -import sys -import shutil -import os -import time -import configparser -from os.path import ( - getsize, - getmtime, - isfile, - isdir, - join, - abspath, - expanduser, - realpath -) -import logging - -log = logging.getLogger(__name__) - -ignoreFiles = ("Thumbs.db", ".DS_Store") - -# this feature is not yet implemented -ignorePaths = [] - -if os.name == 'nt': - # msvcrt can't function correctly in IDLE - if 'idlelib.run' in sys.modules: - print("Please don't run this script in IDLE.") - sys.exit(0) - import msvcrt - - def flush_input(str, set=None): - if not set: - while msvcrt.kbhit(): - ch = msvcrt.getch() - if ch == '\xff': - print("msvcrt is broken, this is weird.") - sys.exit(0) - return input(str) - else: - return set -else: - import select - - def flush_input(str, set=None): - if not set: - while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0: - os.read(sys.stdin.fileno(), 4096) - return input(str) - else: - return set - - -def compare(fa, fb, options_input=[]): - if isfile(fa) == isfile(fb): - if isdir(fa): - walktree(fa, fb, options_input) - elif isfile(fa): - if getsize(fa) != getsize(fb) \ - or int(getmtime(fa)) != int(getmtime(fb)): - log.info(str((fa, ': size=', getsize(fa), 'mtime=', - time.asctime(time.localtime(getmtime(fa)))))) - log.info(str((fb, ': size=', getsize(fb), 'mtime=', - time.asctime(time.localtime(getmtime(fb)))))) - if getmtime(fa) > getmtime(fb): - act = '>' - else: - act = '<' - - set = [i for i in options_input if i in [">", "<"]][0] - - s = flush_input('What to do?(>,<,r,n)[' + act + ']', set=set) - if len(s) > 0: - act = s[0] - if act == '>': - shutil.copy2(fa, fb) - elif act == '<': - shutil.copy2(fb, fa) - elif act == 'r': - if isdir(fa): - shutil.rmtree(fa) - elif isfile(fa): - os.remove(fa) - else: - log.info(str(('Remove: Skipping', fa))) - if isdir(fb): - shutil.rmtree(fb) - elif isfile(fb): - os.remove(fb) - else: - log.info(str(('Remove: Skipping', fb))) - - else: - log.debug(str(('Compare: Skipping non-dir and non-file', fa))) - else: - log.error(str(('Error:', fa, ',', fb, 'have different file type'))) - - -def copy(fa, fb, options_input=[]): - set = [i for i in options_input if i in ["y"]][0] - s = flush_input('Copy ' + fa + ' to another side?(r,y,n)[y]', set=set) - if len(s) > 0: - act = s[0] - else: - act = 'y' - if act == 'y': - if isdir(fa): - shutil.copytree(fa, fb) - elif isfile(fa): - shutil.copy2(fa, fb) - else: - log.debug(str(('Copy: Skipping ', fa))) - elif act == 'r': - if isdir(fa): - shutil.rmtree(fa) - elif isfile(fa): - os.remove(fa) - else: - log.debug(str(('Remove: Skipping ', fa))) - - -stoentry = [] -tarentry = [] - - -def walktree(source, target, options_input=[]): - srclist = os.listdir(source) - tarlist = os.listdir(target) - if '!sync' in srclist: - return - if '!sync' in tarlist: - return - # files in source dir... - for f in srclist: - if f in ignoreFiles: - continue - spath = join(source, f) - tpath = join(target, f) - if spath in ignorePaths: - continue - if spath in stoentry: - # just in case target also have this one - if f in tarlist: - del tarlist[tarlist.index(f)] - continue - - # if also exists in target dir - if f in tarlist: - del tarlist[tarlist.index(f)] - compare(spath, tpath, options_input) - - # exists in source dir only - else: - copy(spath, tpath, options_input) - - # exists in target dir only - set = [i for i in options_input if i in ["<"]] - - for f in tarlist: - if f in ignoreFiles: - continue - spath = join(source, f) - tpath = join(target, f) - if tpath in ignorePaths: - continue - if tpath in tarentry: - continue - if set: - copy(tpath, spath, options_input) - else: - print("REMOVING: {}".format(f)) - if os.path.isdir(tpath): - shutil.rmtree(tpath) - else: - os.remove(tpath) - print("REMOVING: {}".format(f)) - - -if __name__ == '__main__': - stoconf = configparser.RawConfigParser() - tarconf = configparser.RawConfigParser() - stoconf.read("pySync.ini") - tarconf.read(expanduser("~/.pysync")) - stoname = stoconf.sections()[0] - tarname = tarconf.sections()[0] - - # calculate storage's base folder - if stoconf.has_option(stoname, 'BASE'): - stobase = abspath(stoconf.get(stoname, 'BASE')) - stoconf.remove_option(stoname, 'BASE') - else: - stobase = os.getcwd() - - # same, for target's base folder - if tarconf.has_option(tarname, 'BASE'): - tarbase = abspath(tarconf.get(tarname, 'BASE')) - tarconf.remove_option(tarname, 'BASE') - else: - tarbase = expanduser('~/') - - print("Syncing between", stoname, "and", tarname) - sto_content = {x: realpath(join(stobase, stoconf.get(stoname, x))) - for x in stoconf.options(stoname)} - tar_content = {x: realpath(join(tarbase, tarconf.get(tarname, x))) - for x in tarconf.options(tarname)} - stoentry = [sto_content[x] for x in sto_content] - tarentry = [tar_content[x] for x in tar_content] - - for folder in sto_content: - if folder in tar_content: - print('Processing', folder) - walktree(sto_content[folder], tar_content[folder], options_input) - print("Done.") From 27793cef1f93f87e75bd1652929f8c4da5deccc0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 28 Mar 2024 16:06:57 +0100 Subject: [PATCH 122/130] added helper object to handle selection in launcher --- client/ayon_core/pipeline/actions.py | 237 +++++++++++++++++- .../tools/launcher/models/actions.py | 62 ++--- 2 files changed, 248 insertions(+), 51 deletions(-) diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index 8e0ce7e583..d6e589c0ef 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -1,4 +1,7 @@ import logging + +import ayon_api + from ayon_core.pipeline.plugin_discover import ( discover, register_plugin, @@ -10,6 +13,224 @@ from ayon_core.pipeline.plugin_discover import ( from .load.utils import get_representation_path_from_context +class LauncherActionSelection: + """Object helper to pass selection to actions. + + Object support backwards compatibility for 'session' from OpenPype where + environment variable keys were used to define selection. + + Args: + project_name (str): Selected project name. + folder_id (str): Selected folder id. + task_id (str): Selected task id. + folder_path (Optional[str]): Selected folder path. + task_name (Optional[str]): Selected task name. + project_entity (Optional[dict[str, Any]]): Project entity. + folder_entity (Optional[dict[str, Any]]): Folder entity. + task_entity (Optional[dict[str, Any]]): Task entity. + + """ + def __init__( + self, + project_name, + folder_id, + task_id, + folder_path=None, + task_name=None, + project_entity=None, + folder_entity=None, + task_entity=None + ): + self._project_name = project_name + self._folder_id = folder_id + self._task_id = task_id + + self._folder_path = folder_path + self._task_name = task_name + + self._project_entity = project_entity + self._folder_entity = folder_entity + self._task_entity = task_entity + + def __getitem__(self, key): + if key in {"project_name", "AYON_PROJECT_NAME", "AVALON_PROJECT"}: + return self.project_name + if key == {"folder_path", "AYON_FOLDER_PATH", "AVALON_ASSET"}: + return self.folder_path + if key == {"task_name", "AYON_TASK_NAME", "AVALON_TASK"}: + return self.task_name + if key == "folder_id": + return self.folder_id + if key == "task_id": + return self.task_id + if key == "project_entity": + return self.project_entity + if key == "folder_entity": + return self.folder_entity + if key == "task_entity": + return self.task_entity + raise KeyError(f"Key: {key} not found") + + def __contains__(self, key): + # Fake missing keys check for backwards compatibility + if key in { + "AYON_PROJECT_NAME", + "AVALON_PROJECT", + "project_entity", + }: + return self._project_name is not None + if key in { + "AYON_FOLDER_PATH", + "folder_id", + "folder_path", + "folder_entity", + "AVALON_ASSET", + }: + return self._folder_id is not None + if key in { + "AYON_TASK_NAME", + "task_id", + "task_name", + "task_entity", + "AVALON_TASK", + }: + return self._task_id is not None + return False + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def get_project_name(self): + """Selected project name. + + Returns: + Union[str, None]: Selected project name. + + """ + return self._project_name + + def get_folder_id(self): + """Selected folder id. + + Returns: + Union[str, None]: Selected folder id. + + """ + return self._folder_id + + def get_folder_path(self): + """Selected folder path. + + Returns: + Union[str, None]: Selected folder path. + + """ + if self._folder_id is None: + return None + if self._folder_path is None: + self._folder_path = self.folder_entity["path"] + return self._folder_path + + def get_task_id(self): + """Selected task id. + + Returns: + Union[str, None]: Selected task id. + + """ + return self._task_id + + def get_task_name(self): + """Selected task name. + + Returns: + Union[str, None]: Selected task name. + + """ + if self._task_id is None: + return None + if self._task_name is None: + self._task_name = self.task_entity["name"] + return self._task_name + + def get_project_entity(self): + """Project entity for the selection. + + Returns: + Union[dict[str, Any], None]: Project entity. + + """ + if self._project_name is None: + return None + if self._project_entity is None: + self._project_entity = ayon_api.get_project(self._project_name) + return self._project_entity + + def get_folder_entity(self): + """Folder entity for the selection. + + Returns: + Union[dict[str, Any], None]: Folder entity. + + """ + if self._project_name is None or self._folder_id is None: + return None + if self._folder_entity is None: + self._folder_entity = ayon_api.get_folder_by_id( + self._project_name, self._folder_id + ) + return self._folder_entity + + def get_task_entity(self): + """Task entity for the selection. + + Returns: + Union[dict[str, Any], None]: Task entity. + + """ + if ( + self._project_name is None + or self._task_id is None + ): + return None + if self._task_entity is None: + self._task_entity = ayon_api.get_task_by_id( + self._project_name, self._task_id + ) + return self._task_entity + + @property + def is_project_selected(self): + """Return whether a project is selected. + + Returns: + bool: Whether a project is selected. + + """ + return self._project_name is not None + + @property + def is_folder_selected(self): + return self._folder_id is not None + + @property + def is_task_selected(self): + return self._task_id is not None + + project_name = property(get_project_name) + folder_id = property(get_folder_id) + task_id = property(get_task_id) + folder_path = property(get_folder_path) + task_name = property(get_task_name) + + project_entity = property(get_project_entity) + folder_entity = property(get_folder_entity) + task_entity = property(get_task_entity) + + class LauncherAction(object): """A custom action available""" name = None @@ -21,17 +242,23 @@ class LauncherAction(object): log = logging.getLogger("LauncherAction") log.propagate = True - def is_compatible(self, session): + def is_compatible(self, selection): """Return whether the class is compatible with the Session. Args: - session (dict[str, Union[str, None]]): Session data with - AYON_PROJECT_NAME, AYON_FOLDER_PATH and AYON_TASK_NAME. - """ + selection (LauncherActionSelection): Data with selection. + """ return True - def process(self, session, **kwargs): + def process(self, selection, **kwargs): + """Process the action. + + Args: + selection (LauncherActionSelection): Data with selection. + **kwargs: Additional arguments. + + """ pass diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 97943e6ad7..6da34151b6 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -5,6 +5,7 @@ from ayon_core.lib import Logger, AYONSettingsRegistry from ayon_core.pipeline.actions import ( discover_launcher_actions, LauncherAction, + LauncherActionSelection, ) from ayon_core.pipeline.workfile import should_use_last_workfile_on_launch @@ -69,11 +70,6 @@ class ApplicationAction(LauncherAction): project_entities = {} _log = None - required_session_keys = ( - "AYON_PROJECT_NAME", - "AYON_FOLDER_PATH", - "AYON_TASK_NAME" - ) @property def log(self): @@ -81,18 +77,16 @@ class ApplicationAction(LauncherAction): self._log = Logger.get_logger(self.__class__.__name__) return self._log - def is_compatible(self, session): - for key in self.required_session_keys: - if not session.get(key): - return False + def is_compatible(self, selection): + if not selection.is_task_selected: + return False - project_name = session["AYON_PROJECT_NAME"] - project_entity = self.project_entities[project_name] + project_entity = self.project_entities[selection.project_name] apps = project_entity["attrib"].get("applications") if not apps or self.application.full_name not in apps: return False - project_settings = self.project_settings[project_name] + project_settings = self.project_settings[selection.project_name] only_available = project_settings["applications"]["only_available"] if only_available and not self.application.find_executable(): return False @@ -112,7 +106,7 @@ class ApplicationAction(LauncherAction): dialog.setDetailedText(details) dialog.exec_() - def process(self, session, **kwargs): + def process(self, selection, **kwargs): """Process the full Application action""" from ayon_core.lib import ( @@ -120,14 +114,11 @@ class ApplicationAction(LauncherAction): ApplicationLaunchFailed, ) - project_name = session["AYON_PROJECT_NAME"] - folder_path = session["AYON_FOLDER_PATH"] - task_name = session["AYON_TASK_NAME"] try: self.application.launch( - project_name=project_name, - folder_path=folder_path, - task_name=task_name, + project_name=selection.project_name, + folder_path=selection.folder_path, + task_name=selection.task_name, **self.data ) @@ -335,11 +326,11 @@ class ActionsModel: """ not_open_workfile_actions = self._get_no_last_workfile_for_context( project_name, folder_id, task_id) - session = self._prepare_session(project_name, folder_id, task_id) + selection = self._prepare_selection(project_name, folder_id, task_id) output = [] action_items = self._get_action_items(project_name) for identifier, action in self._get_action_objects().items(): - if not action.is_compatible(session): + if not action.is_compatible(selection): continue action_item = action_items[identifier] @@ -374,7 +365,7 @@ class ActionsModel: ) def trigger_action(self, project_name, folder_id, task_id, identifier): - session = self._prepare_session(project_name, folder_id, task_id) + selection = self._prepare_selection(project_name, folder_id, task_id) failed = False error_message = None action_label = identifier @@ -403,7 +394,7 @@ class ActionsModel: ) action.data["start_last_workfile"] = start_last_workfile - action.process(session) + action.process(selection) except Exception as exc: self.log.warning("Action trigger failed.", exc_info=True) failed = True @@ -440,29 +431,8 @@ class ActionsModel: .get(task_id, {}) ) - def _prepare_session(self, project_name, folder_id, task_id): - folder_path = None - if folder_id: - folder = self._controller.get_folder_entity( - project_name, folder_id) - if folder: - folder_path = folder["path"] - - task_name = None - if task_id: - task = self._controller.get_task_entity(project_name, task_id) - if task: - task_name = task["name"] - - return { - "AYON_PROJECT_NAME": project_name, - "AYON_FOLDER_PATH": folder_path, - "AYON_TASK_NAME": task_name, - # Deprecated - kept for backwards compatibility - "AVALON_PROJECT": project_name, - "AVALON_ASSET": folder_path, - "AVALON_TASK": task_name, - } + def _prepare_selection(self, project_name, folder_id, task_id): + return LauncherActionSelection(project_name, folder_id, task_id) def _get_discovered_action_classes(self): if self._discovered_actions is None: From 93b9541268e0885052126ab647441db56be19a14 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 28 Mar 2024 16:07:13 +0100 Subject: [PATCH 123/130] use new options of selection object --- .../launcher_actions/ClockifyStart.py | 14 ++++----- .../clockify/launcher_actions/ClockifySync.py | 12 ++++---- .../plugins/actions/open_file_explorer.py | 29 +++++++++---------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py b/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py index 61c5eac2f5..8381c7d73e 100644 --- a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py +++ b/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py @@ -11,19 +11,17 @@ class ClockifyStart(LauncherAction): order = 500 clockify_api = ClockifyAPI() - def is_compatible(self, session): + def is_compatible(self, selection): """Return whether the action is compatible with the session""" - if "AYON_TASK_NAME" in session: - return True - return False + return selection.is_task_selected - def process(self, session, **kwargs): + def process(self, selection, **kwargs): self.clockify_api.set_api() user_id = self.clockify_api.user_id workspace_id = self.clockify_api.workspace_id - project_name = session["AYON_PROJECT_NAME"] - folder_path = session["AYON_FOLDER_PATH"] - task_name = session["AYON_TASK_NAME"] + project_name = selection.project_name + folder_path = selection.folder_path + task_name = selection.task_name description = "/".join([folder_path.lstrip("/"), task_name]) # fetch folder entity diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py b/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py index 72187c6d28..5388f47c98 100644 --- a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py +++ b/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py @@ -19,15 +19,18 @@ class ClockifySync(LauncherAction): order = 500 clockify_api = ClockifyAPI() - def is_compatible(self, session): + def is_compatible(self, selection): """Check if there's some projects to sync""" + if selection.is_project_selected: + return True + try: next(ayon_api.get_projects()) return True except StopIteration: return False - def process(self, session, **kwargs): + def process(self, selection, **kwargs): self.clockify_api.set_api() workspace_id = self.clockify_api.workspace_id user_id = self.clockify_api.user_id @@ -37,10 +40,9 @@ class ClockifySync(LauncherAction): raise ClockifyPermissionsCheckFailed( "Current CLockify user is missing permissions for this action!" ) - project_name = session.get("AYON_PROJECT_NAME") or "" - if project_name.strip(): - projects_to_sync = [ayon_api.get_project(project_name)] + if selection.is_project_selected: + projects_to_sync = [selection.project_entity] else: projects_to_sync = ayon_api.get_projects() diff --git a/client/ayon_core/plugins/actions/open_file_explorer.py b/client/ayon_core/plugins/actions/open_file_explorer.py index 69375a7859..6a456c75c1 100644 --- a/client/ayon_core/plugins/actions/open_file_explorer.py +++ b/client/ayon_core/plugins/actions/open_file_explorer.py @@ -18,18 +18,14 @@ class OpenTaskPath(LauncherAction): icon = "folder-open" order = 500 - def is_compatible(self, session): + def is_compatible(self, selection): """Return whether the action is compatible with the session""" - return bool(session.get("AYON_FOLDER_PATH")) + return selection.is_folder_selected - def process(self, session, **kwargs): + def process(self, selection, **kwargs): from qtpy import QtCore, QtWidgets - project_name = session["AYON_PROJECT_NAME"] - folder_path = session["AYON_FOLDER_PATH"] - task_name = session.get("AYON_TASK_NAME", None) - - path = self._get_workdir(project_name, folder_path, task_name) + path = self._get_workdir(selection) if not path: return @@ -60,16 +56,17 @@ class OpenTaskPath(LauncherAction): path = path.split(field, 1)[0] return path - def _get_workdir(self, project_name, folder_path, task_name): - project_entity = ayon_api.get_project(project_name) - folder_entity = ayon_api.get_folder_by_path(project_name, folder_path) - task_entity = ayon_api.get_task_by_name( - project_name, folder_entity["id"], task_name + def _get_workdir(self, selection): + data = get_template_data( + selection.project_entity, + selection.folder_entity, + selection.task_entity ) - data = get_template_data(project_entity, folder_entity, task_entity) - - anatomy = Anatomy(project_name) + anatomy = Anatomy( + selection.project_name, + project_entity=selection.project_entity + ) workdir = anatomy.get_template_item( "work", "default", "folder" ).format(data) From b6269b176516426a53bb227d8a390464623ec5a0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:34:10 +0100 Subject: [PATCH 124/130] Add missing docstrings. Co-authored-by: Roy Nieterau --- client/ayon_core/pipeline/actions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index d6e589c0ef..52d68800d1 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -214,10 +214,22 @@ class LauncherActionSelection: @property def is_folder_selected(self): + """Return whether a folder is selected. + + Returns: + bool: Whether a folder is selected. + + """ return self._folder_id is not None @property def is_task_selected(self): + """Return whether a task is selected. + + Returns: + bool: Whether a task is selected. + + """ return self._task_id is not None project_name = property(get_project_name) From 24d6280ae68ebf1c3168635f472ed76fab21e882 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 28 Mar 2024 16:36:21 +0100 Subject: [PATCH 125/130] Houdini: Fix creating instances from tab menu --- client/ayon_core/hosts/houdini/api/creator_node_shelves.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py index 6e48cb375b..72c157f187 100644 --- a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py +++ b/client/ayon_core/hosts/houdini/api/creator_node_shelves.py @@ -91,7 +91,7 @@ def create_interactive(creator_identifier, **kwargs): pane = stateutils.activePane(kwargs) if isinstance(pane, hou.NetworkEditor): pwd = pane.pwd() - project_name = context.get_current_project_name(), + project_name = context.get_current_project_name() folder_path = context.get_current_folder_path() task_name = context.get_current_task_name() folder_entity = ayon_api.get_folder_by_path( From 66f3b4cbf6573d87f0dccd0a2f427aaf6f6cfef0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 28 Mar 2024 16:39:42 +0100 Subject: [PATCH 126/130] keep dict like access only for backwards compatible keys --- client/ayon_core/pipeline/actions.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index 52d68800d1..9ccc82e4a9 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -53,22 +53,12 @@ class LauncherActionSelection: self._task_entity = task_entity def __getitem__(self, key): - if key in {"project_name", "AYON_PROJECT_NAME", "AVALON_PROJECT"}: + if key in {"AYON_PROJECT_NAME", "AVALON_PROJECT"}: return self.project_name - if key == {"folder_path", "AYON_FOLDER_PATH", "AVALON_ASSET"}: + if key == {"AYON_FOLDER_PATH", "AVALON_ASSET"}: return self.folder_path - if key == {"task_name", "AYON_TASK_NAME", "AVALON_TASK"}: + if key == {"AYON_TASK_NAME", "AVALON_TASK"}: return self.task_name - if key == "folder_id": - return self.folder_id - if key == "task_id": - return self.task_id - if key == "project_entity": - return self.project_entity - if key == "folder_entity": - return self.folder_entity - if key == "task_entity": - return self.task_entity raise KeyError(f"Key: {key} not found") def __contains__(self, key): @@ -76,22 +66,15 @@ class LauncherActionSelection: if key in { "AYON_PROJECT_NAME", "AVALON_PROJECT", - "project_entity", }: return self._project_name is not None if key in { "AYON_FOLDER_PATH", - "folder_id", - "folder_path", - "folder_entity", "AVALON_ASSET", }: return self._folder_id is not None if key in { "AYON_TASK_NAME", - "task_id", - "task_name", - "task_entity", "AVALON_TASK", }: return self._task_id is not None From 8fab7139694d74ab958f5eefeb9426e8d8576c23 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 28 Mar 2024 16:43:42 +0100 Subject: [PATCH 127/130] added deprecation warnings --- client/ayon_core/pipeline/actions.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index 9ccc82e4a9..9b5234d811 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -1,4 +1,5 @@ import logging +import warnings import ayon_api @@ -53,6 +54,14 @@ class LauncherActionSelection: self._task_entity = task_entity def __getitem__(self, key): + warnings.warn( + ( + "Using deprecated access to selection data. Please use" + " attributes and methods" + " defined by 'LauncherActionSelection'." + ), + category=DeprecationWarning + ) if key in {"AYON_PROJECT_NAME", "AVALON_PROJECT"}: return self.project_name if key == {"AYON_FOLDER_PATH", "AVALON_ASSET"}: @@ -62,6 +71,14 @@ class LauncherActionSelection: raise KeyError(f"Key: {key} not found") def __contains__(self, key): + warnings.warn( + ( + "Using deprecated access to selection data. Please use" + " attributes and methods" + " defined by 'LauncherActionSelection'." + ), + category=DeprecationWarning + ) # Fake missing keys check for backwards compatibility if key in { "AYON_PROJECT_NAME", @@ -81,6 +98,14 @@ class LauncherActionSelection: return False def get(self, key, default=None): + warnings.warn( + ( + "Using deprecated access to selection data. Please use" + " attributes and methods" + " defined by 'LauncherActionSelection'." + ), + category=DeprecationWarning + ) try: return self[key] except KeyError: From b75a3bca4f1dc0d159e265066a73a481f757997c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 28 Mar 2024 16:57:10 +0100 Subject: [PATCH 128/130] fix in conditions --- client/ayon_core/pipeline/actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index 9b5234d811..0d2a05f7cd 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -64,9 +64,9 @@ class LauncherActionSelection: ) if key in {"AYON_PROJECT_NAME", "AVALON_PROJECT"}: return self.project_name - if key == {"AYON_FOLDER_PATH", "AVALON_ASSET"}: + if key in {"AYON_FOLDER_PATH", "AVALON_ASSET"}: return self.folder_path - if key == {"AYON_TASK_NAME", "AVALON_TASK"}: + if key in {"AYON_TASK_NAME", "AVALON_TASK"}: return self.task_name raise KeyError(f"Key: {key} not found") From 5b5ab5df8b93e07ce19dc5e5c3654bcb184c57cf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 28 Mar 2024 17:00:24 +0100 Subject: [PATCH 129/130] implemented rest of dict-like methods --- client/ayon_core/pipeline/actions.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index 0d2a05f7cd..a2eb8e7eee 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -70,6 +70,10 @@ class LauncherActionSelection: return self.task_name raise KeyError(f"Key: {key} not found") + def __iter__(self): + for key in self.keys(): + yield key + def __contains__(self, key): warnings.warn( ( @@ -111,6 +115,23 @@ class LauncherActionSelection: except KeyError: return default + def items(self): + for key, value in ( + ("AYON_PROJECT_NAME", self.project_name), + ("AYON_FOLDER_PATH", self.folder_path), + ("AYON_TASK_NAME", self.task_name), + ): + if value is not None: + yield (key, value) + + def keys(self): + for key, _ in self.items(): + yield key + + def values(self): + for _, value in self.items(): + yield value + def get_project_name(self): """Selected project name. From 2b149b50ee3a6e019b4cdeb75c446a460af60a54 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 28 Mar 2024 17:28:16 +0100 Subject: [PATCH 130/130] added deprecation to method docstrings --- client/ayon_core/pipeline/actions.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/client/ayon_core/pipeline/actions.py b/client/ayon_core/pipeline/actions.py index a2eb8e7eee..eae2fc94b5 100644 --- a/client/ayon_core/pipeline/actions.py +++ b/client/ayon_core/pipeline/actions.py @@ -102,6 +102,12 @@ class LauncherActionSelection: return False def get(self, key, default=None): + """ + + Deprecated: + Added for backwards compatibility with older actions. + + """ warnings.warn( ( "Using deprecated access to selection data. Please use" @@ -116,6 +122,12 @@ class LauncherActionSelection: return default def items(self): + """ + + Deprecated: + Added for backwards compatibility with older actions. + + """ for key, value in ( ("AYON_PROJECT_NAME", self.project_name), ("AYON_FOLDER_PATH", self.folder_path), @@ -125,10 +137,22 @@ class LauncherActionSelection: yield (key, value) def keys(self): + """ + + Deprecated: + Added for backwards compatibility with older actions. + + """ for key, _ in self.items(): yield key def values(self): + """ + + Deprecated: + Added for backwards compatibility with older actions. + + """ for _, value in self.items(): yield value