From d2503fae073113374da76bde5d11a902a055cf7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Tue, 26 Mar 2024 11:01:08 +0100 Subject: [PATCH 001/192] :recycle: remove explicit list --- client/ayon_core/plugins/publish/integrate.py | 64 +------------------ 1 file changed, 1 insertion(+), 63 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index ce34f2e88b..5b91d9afb8 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -42,7 +42,7 @@ def prepare_changes(old_entity, new_entity): Returns: dict[str, Any]: Changes that have new entity. - + """ changes = {} for key in set(new_entity.keys()): @@ -108,68 +108,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): label = "Integrate Asset" order = pyblish.api.IntegratorOrder - families = ["workfile", - "pointcache", - "pointcloud", - "proxyAbc", - "camera", - "animation", - "model", - "maxScene", - "mayaAscii", - "mayaScene", - "setdress", - "layout", - "ass", - "vdbcache", - "scene", - "vrayproxy", - "vrayscene_layer", - "render", - "prerender", - "imagesequence", - "review", - "rendersetup", - "rig", - "plate", - "look", - "ociolook", - "audio", - "yetiRig", - "yeticache", - "nukenodes", - "gizmo", - "source", - "matchmove", - "image", - "assembly", - "fbx", - "gltf", - "textures", - "action", - "harmony.template", - "harmony.palette", - "editorial", - "background", - "camerarig", - "redshiftproxy", - "effect", - "xgen", - "hda", - "usd", - "staticMesh", - "skeletalMesh", - "mvLook", - "mvUsd", - "mvUsdComposition", - "mvUsdOverride", - "online", - "uasset", - "blendScene", - "yeticacheUE", - "tycache" - ] - default_template_name = "publish" # Representation context keys that should always be written to From 941e80dd952864adcd4ba4386d5883434f5cd338 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 15:25:41 +0200 Subject: [PATCH 002/192] Use a general family for houdini farm rendering --- .../plugins/publish/collect_farm_instances.py | 23 +++++++++++++++++++ .../plugins/publish/increment_current_file.py | 4 ++-- .../deadline/plugins/publish/collect_pools.py | 2 +- .../publish/submit_houdini_render_deadline.py | 13 +++++++---- .../plugins/publish/submit_publish_job.py | 2 +- 5 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py new file mode 100644 index 0000000000..9a05ff75dd --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -0,0 +1,23 @@ +import pyblish.api + + +class CollectFarmInstances(pyblish.api.InstancePlugin): + """Collect instances for farm render.""" + + order = pyblish.api.CollectorOrder + families = ["mantra_rop"] + + hosts = ["houdini"] + targets = ["local", "remote"] + label = "Collect farm instances" + + def process(self, instance): + creator_attribute = instance.data["creator_attributes"] + farm_enabled = creator_attribute["farm"] + instance.data["farm"] = farm_enabled + if not farm_enabled: + self.log.debug("Render on farm is disabled. " + "Skipping farm collecting.") + return + + instance.data["families"].append("render.farm.hou") diff --git a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py index 73145b211a..f94d1f10ed 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py @@ -3,7 +3,7 @@ import pyblish.api from ayon_core.lib import version_up from ayon_core.pipeline import registered_host from ayon_core.pipeline.publish import get_errored_plugins_from_context -from ayon_core.hosts.houdini.api import HoudiniHost + from ayon_core.pipeline.publish import KnownPublishError @@ -20,9 +20,9 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin): families = ["workfile", "redshift_rop", "arnold_rop", - "mantra_rop", "karma_rop", "usdrender", + "render.farm.hou", "publish.hou"] optional = True diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index 6923c2b16b..62d997eb2c 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -43,9 +43,9 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, "usdrender", "redshift_rop", "arnold_rop", - "mantra_rop", "karma_rop", "vray_rop", + "render.farm.hou", "publish.hou"] primary_pool = None diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 6952604293..d91fd895ad 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -73,9 +73,9 @@ class HoudiniSubmitDeadline( families = ["usdrender", "redshift_rop", "arnold_rop", - "mantra_rop", "karma_rop", - "vray_rop"] + "vray_rop", + "render.farm.hou"] targets = ["local"] use_published = True @@ -86,7 +86,7 @@ class HoudiniSubmitDeadline( priority = 50 chunk_size = 1 group = "" - + @classmethod def get_attribute_defs(cls): return [ @@ -194,7 +194,7 @@ class HoudiniSubmitDeadline( job_info.Pool = instance.data.get("primaryPool") job_info.SecondaryPool = instance.data.get("secondaryPool") - + if split_render_job and is_export_job: job_info.Priority = attribute_values.get( "export_priority", self.export_priority @@ -265,11 +265,14 @@ class HoudiniSubmitDeadline( # Output driver to render if job_type == "render": product_type = instance.data.get("productType") + rop_node = hou.node(instance.data.get("instance_node")) + node_type = rop_node.type().name() + if product_type == "arnold_rop": plugin_info = ArnoldRenderDeadlinePluginInfo( InputFile=instance.data["ifdFile"] ) - elif product_type == "mantra_rop": + elif node_type == "ifd": plugin_info = MantraRenderDeadlinePluginInfo( SceneFile=instance.data["ifdFile"], Version=hou_major_minor, diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 84bac6d017..82232c70c0 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -92,7 +92,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "prerender.farm", "prerender.frames_farm", "renderlayer", "imagesequence", "vrayscene", "maxrender", - "arnold_rop", "mantra_rop", + "arnold_rop", "render.farm.hou", "karma_rop", "vray_rop", "redshift_rop"] From 37cecde8869dc273b1e783efe529b82b570a7794 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 16:08:03 +0200 Subject: [PATCH 003/192] add few keywords related to Houdini --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ee124ddc2d..5bc11031e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,7 @@ line-ending = "auto" [tool.codespell] # Ignore words that are not in the dictionary. -ignore-words-list = "ayon,ynput" +ignore-words-list = "ayon,ynput,hda,parms" skip = "./.*,./package/*,*/vendor/*,*/unreal/integration/*,*/aftereffects/api/extension/js/libs/*" count = true quiet-level = 3 From 71bf18910c8955523313a947f9be5b8adc5b9ad4 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 16:09:45 +0200 Subject: [PATCH 004/192] make codespell happy about integrate.py --- client/ayon_core/plugins/publish/integrate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index ce34f2e88b..38169ca2c3 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -42,7 +42,7 @@ def prepare_changes(old_entity, new_entity): Returns: dict[str, Any]: Changes that have new entity. - + """ changes = {} for key in set(new_entity.keys()): @@ -358,7 +358,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Compute the resource file infos once (files belonging to the # version instance instead of an individual representation) so - # we can re-use those file infos per representation + # we can reuse those file infos per representation resource_file_infos = self.get_files_info( resource_destinations, anatomy ) From 620538330c10e83ec95a90ec0a59b587ba09b7df Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 16:10:48 +0200 Subject: [PATCH 005/192] support local rendering for mantra_rop and add dedicated plugins --- .../plugins/create/create_mantra_rop.py | 43 +++++---- .../publish/collect_local_render_instances.py | 88 +++++++++++++++++++ .../publish/extract_mantra_local_render.py | 29 ++++++ .../plugins/publish/increment_current_file.py | 1 + .../validate_split_render_is_disabled.py | 65 ++++++++++++++ client/ayon_core/plugins/publish/integrate.py | 3 +- 6 files changed, 213 insertions(+), 16 deletions(-) create mode 100644 client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py create mode 100644 client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py create mode 100644 client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index f15f49f463..6dac3ff02a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin to create Mantra ROP.""" from ayon_core.hosts.houdini.api import plugin -from ayon_core.pipeline import CreatedInstance -from ayon_core.lib import EnumDef, BoolDef +from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef class CreateMantraROP(plugin.HoudiniCreator): @@ -23,12 +22,14 @@ class CreateMantraROP(plugin.HoudiniCreator): # Add chunk size attribute instance_data["chunkSize"] = 10 # Submit for job publishing - instance_data["farm"] = pre_create_data.get("farm") + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["farm"] = pre_create_data.get("farm") instance = super(CreateMantraROP, self).create( product_name, instance_data, - pre_create_data) # type: CreatedInstance + pre_create_data) instance_node = hou.node(instance.get("instance_node")) @@ -78,21 +79,14 @@ class CreateMantraROP(plugin.HoudiniCreator): to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) - def get_pre_create_attr_defs(self): - attrs = super(CreateMantraROP, self).get_pre_create_attr_defs() - + def get_instance_attr_defs(self): image_format_enum = [ "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] - return attrs + [ - BoolDef("farm", - label="Submitting to Farm", - default=True), - BoolDef("export_job", - label="Split export and render jobs", - default=self.export_job), + return [ + UILabelDef(label="Mantra Render Settings:"), EnumDef("image_format", image_format_enum, default="exr", @@ -101,5 +95,24 @@ class CreateMantraROP(plugin.HoudiniCreator): label="Override Camera Resolution", tooltip="Override the current camera " "resolution, recommended for IPR.", - default=False) + default=False), + UISeparatorDef(key="1"), + UILabelDef(label="Farm Render Options:"), + BoolDef("farm", + label="Submitting to Farm", + default=True), + BoolDef("export_job", + label="Split export and render jobs", + default=self.export_job), + UISeparatorDef(key="2"), + UILabelDef(label="Local Render Options:"), + BoolDef("skip_render", + label="Skip Render", + tooltip="Enable this option to skip render which publish existing frames.", + default=False), ] + + def get_pre_create_attr_defs(self): + attrs = super(CreateMantraROP, self).get_pre_create_attr_defs() + + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py new file mode 100644 index 0000000000..6b55bfffa4 --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -0,0 +1,88 @@ +import os +import pyblish.api + + +class CollectLocalRenderInstances(pyblish.api.InstancePlugin): + """Collect instances for local render. + + Agnostic Local Render Collector. + """ + + # this plugin runs after Collect Render Products + order = pyblish.api.CollectorOrder + 0.12 + families = ["mantra_rop"] + + hosts = ["houdini"] + targets = ["local", "remote"] + label = "Collect local render instances" + + def process(self, instance): + creator_attribute = instance.data["creator_attributes"] + farm_enabled = creator_attribute["farm"] + instance.data["farm"] = farm_enabled + if farm_enabled: + self.log.debug("Render on farm is enabled. " + "Skipping local render collecting.") + return + + # Create Instance for each AOV. + context = instance.context + expectedFiles = next(iter(instance.data["expectedFiles"]), {}) + + product_type = "render" # is always render + product_group = "render{Task}{productName}".format( + Task=self._capitalize(instance.data["task"]), + productName=self._capitalize(instance.data["productName"]) + ) # is always the group + + for aov_name, aov_filepaths in expectedFiles.items(): + # Some AOV instance data + # label = "{productName}_{AOV}".format( + # AOV=aov_name, + # productName=instance.data["productName"] + # ) + product_name = "render{Task}{productName}_{AOV}".format( + Task=self._capitalize(instance.data["task"]), + productName=self._capitalize(instance.data["productName"]), + AOV=aov_name + ) + + # Create instance for each AOV + aov_instance = context.create_instance(product_name) + + # Prepare Representation for each AOV + aov_filenames = [os.path.basename(path) for path in aov_filepaths] + staging_dir = os.path.dirname(aov_filepaths[0]) + ext = aov_filepaths[0].split(".")[-1] + + aov_instance.data.update({ + # 'label': label, + "task": instance.data["task"], + "folderPath": instance.data["folderPath"], + "frameStart": instance.data["frameStartHandle"], + "frameEnd": instance.data["frameEndHandle"], + "productType": product_type, + "productName": product_name, + "productGroup": product_group, + "tags": [], + "families": ["render.local.hou"], + "instance_node": instance.data["instance_node"], + "representations": [ + { + "stagingDir": staging_dir, + "ext": ext, + "name": ext, + "files": aov_filenames, + "frameStart": instance.data["frameStartHandle"], + "frameEnd": instance.data["frameEndHandle"] + } + ] + }) + + # Remove Mantra instance + # I can't remove it here as I still need it to trigger the render. + # context.remove(instance) + + @staticmethod + def _capitalize(word): + return word[:1].upper() + word[1:] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py new file mode 100644 index 0000000000..bb78f6b1ee --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py @@ -0,0 +1,29 @@ +import pyblish.api + +from ayon_core.pipeline import publish +from ayon_core.hosts.houdini.api.lib import render_rop +import hou + + +class ExtractMantraLocalRender(publish.Extractor): + + order = pyblish.api.ExtractorOrder + label = "Extract Mantra Local Render" + hosts = ["houdini"] + families = ["mantra_rop"] + targets = ["local", "remote"] + + def process(self, instance): + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return + + creator_attribute = instance.data["creator_attributes"] + skip_render = creator_attribute["skip_render"] + + if skip_render: + self.log.debug("Skip render is enabled, skipping rendering.") + return + + ropnode = hou.node(instance.data.get("instance_node")) + render_rop(ropnode) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py index f94d1f10ed..199843bc14 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py @@ -23,6 +23,7 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin): "karma_rop", "usdrender", "render.farm.hou", + "render.local.hou", "publish.hou"] optional = True diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py new file mode 100644 index 0000000000..2d7a95e817 --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +import pyblish.api +import hou +from ayon_core.pipeline import PublishValidationError +from ayon_core.pipeline.publish import RepairAction + + +class DisableSplitExportAction(RepairAction): + label = "Disable Split Export" + + +class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): + """Validate the Instance has no current cooking errors.""" + + order = pyblish.api.ValidatorOrder + hosts = ["houdini"] + families = ["mantra_rop"] + label = "Validate Split Export Is Disabled" + actions = [DisableSplitExportAction] + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + nodes = [n.path() for n in invalid] + raise PublishValidationError( + "See log for details. " + "Invalid nodes: {0}".format(nodes) + ) + + + @classmethod + def get_invalid(cls, instance): + + invalid = [] + rop_node = hou.node(instance.data["instance_node"]) + + creator_attribute = instance.data["creator_attributes"] + farm_enabled = creator_attribute["farm"] + if farm_enabled: + cls.log.debug( + "Farm is enabled, skipping validation." + ) + return + + + split_enabled = creator_attribute["export_job"] + if split_enabled: + invalid.append(rop_node) + cls.log.error( + "Split Export must be disabled in local render instances." + ) + + return invalid + + @classmethod + def repair(cls, instance): + + create_context = instance.context.data["create_context"] + created_instance = create_context.get_instance_by_id( + instance.data["instance_id"]) + creator_attributes = created_instance["creator_attributes"] + # Disable export_job + creator_attributes["export_job"] = False + create_context.save_changes() diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 38169ca2c3..ea24112831 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -167,7 +167,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "uasset", "blendScene", "yeticacheUE", - "tycache" + "tycache", + "render.local.hou" ] default_template_name = "publish" From b35bc8c9d55b7b064f9d9b81cb0184c37925705a Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 16:18:15 +0200 Subject: [PATCH 006/192] remove redundant code --- .../ayon_core/hosts/houdini/plugins/create/create_karma_rop.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index 9eb9d80cd3..e91ddbc0ac 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Creator plugin to create Karma ROP.""" from ayon_core.hosts.houdini.api import plugin -from ayon_core.pipeline import CreatedInstance from ayon_core.lib import BoolDef, EnumDef, NumberDef @@ -25,7 +24,7 @@ class CreateKarmaROP(plugin.HoudiniCreator): instance = super(CreateKarmaROP, self).create( product_name, instance_data, - pre_create_data) # type: CreatedInstance + pre_create_data) instance_node = hou.node(instance.get("instance_node")) From 068b9c3f4ec721d6404c5c26e78b6d03f5ae40aa Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 16:32:43 +0200 Subject: [PATCH 007/192] support local rendering for karma_rop --- .../plugins/create/create_karma_rop.py | 24 ++++++++++++------- .../plugins/publish/collect_farm_instances.py | 3 ++- .../publish/collect_local_render_instances.py | 3 ++- .../publish/extract_mantra_local_render.py | 7 +++--- .../plugins/publish/increment_current_file.py | 1 - .../deadline/plugins/publish/collect_pools.py | 1 - .../publish/submit_houdini_render_deadline.py | 1 - .../plugins/publish/submit_publish_job.py | 2 +- 8 files changed, 25 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index e91ddbc0ac..c791cfe647 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin to create Karma ROP.""" from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import BoolDef, EnumDef, NumberDef +from ayon_core.lib import BoolDef, EnumDef, NumberDef, UISeparatorDef, UILabelDef class CreateKarmaROP(plugin.HoudiniCreator): @@ -18,8 +18,6 @@ class CreateKarmaROP(plugin.HoudiniCreator): instance_data.update({"node_type": "karma"}) # Add chunk size attribute instance_data["chunkSize"] = 10 - # Submit for job publishing - instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateKarmaROP, self).create( product_name, @@ -86,15 +84,13 @@ class CreateKarmaROP(plugin.HoudiniCreator): to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) - def get_pre_create_attr_defs(self): - attrs = super(CreateKarmaROP, self).get_pre_create_attr_defs() - + def get_instance_attr_defs(self): image_format_enum = [ "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] - return attrs + [ + return [ BoolDef("farm", label="Submitting to Farm", default=True), @@ -112,5 +108,17 @@ class CreateKarmaROP(plugin.HoudiniCreator): decimals=0), BoolDef("cam_res", label="Camera Resolution", - default=False) + default=False), + UISeparatorDef(key="2"), + UILabelDef(label="Local Render Options:"), + BoolDef("skip_render", + label="Skip Render", + tooltip="Enable this option to skip render which publish existing frames.", + default=False), ] + + + def get_pre_create_attr_defs(self): + attrs = super(CreateKarmaROP, self).get_pre_create_attr_defs() + + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index 9a05ff75dd..61894da98e 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -5,7 +5,8 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): """Collect instances for farm render.""" order = pyblish.api.CollectorOrder - families = ["mantra_rop"] + families = ["mantra_rop", + "karma_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 6b55bfffa4..e94e1187d7 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -10,7 +10,8 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): # this plugin runs after Collect Render Products order = pyblish.api.CollectorOrder + 0.12 - families = ["mantra_rop"] + families = ["mantra_rop", + "karma_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py index bb78f6b1ee..a7967435c9 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py @@ -5,12 +5,13 @@ from ayon_core.hosts.houdini.api.lib import render_rop import hou -class ExtractMantraLocalRender(publish.Extractor): +class ExtractLocalRender(publish.Extractor): order = pyblish.api.ExtractorOrder - label = "Extract Mantra Local Render" + label = "Extract Local Render" hosts = ["houdini"] - families = ["mantra_rop"] + families = ["mantra_rop", + "karma_rop"] targets = ["local", "remote"] def process(self, instance): diff --git a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py index 199843bc14..5885fd8643 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py @@ -20,7 +20,6 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin): families = ["workfile", "redshift_rop", "arnold_rop", - "karma_rop", "usdrender", "render.farm.hou", "render.local.hou", diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index 62d997eb2c..bb556c2b9d 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -43,7 +43,6 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, "usdrender", "redshift_rop", "arnold_rop", - "karma_rop", "vray_rop", "render.farm.hou", "publish.hou"] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index d91fd895ad..b433151b34 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -73,7 +73,6 @@ class HoudiniSubmitDeadline( families = ["usdrender", "redshift_rop", "arnold_rop", - "karma_rop", "vray_rop", "render.farm.hou"] targets = ["local"] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 82232c70c0..69f626b602 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -93,7 +93,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "renderlayer", "imagesequence", "vrayscene", "maxrender", "arnold_rop", "render.farm.hou", - "karma_rop", "vray_rop", + "vray_rop", "redshift_rop"] aov_filter = [ From 5dd571dc2140601823f6fdf3c9da6a875eaebcce Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 16:33:48 +0200 Subject: [PATCH 008/192] algin file name to class name --- .../{extract_mantra_local_render.py => extract_local_render.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/ayon_core/hosts/houdini/plugins/publish/{extract_mantra_local_render.py => extract_local_render.py} (100%) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py similarity index 100% rename from client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_local_render.py rename to client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py From e8907b00c171a6c6d76ebff009335aeeb83ab026 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 17:02:13 +0200 Subject: [PATCH 009/192] add parm to cspell ignore list --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5bc11031e2..58f07f0fa9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,7 @@ line-ending = "auto" [tool.codespell] # Ignore words that are not in the dictionary. -ignore-words-list = "ayon,ynput,hda,parms" +ignore-words-list = "ayon,ynput,hda,parms,parm" skip = "./.*,./package/*,*/vendor/*,*/unreal/integration/*,*/aftereffects/api/extension/js/libs/*" count = true quiet-level = 3 From 313a7a2456a680b659999436924140afca18d2fc Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 17:02:26 +0200 Subject: [PATCH 010/192] support local rendering for redshift_rop --- .../plugins/create/create_redshift_rop.py | 38 ++++++++++++------- .../plugins/publish/collect_farm_instances.py | 3 +- .../publish/collect_local_render_instances.py | 3 +- .../plugins/publish/extract_local_render.py | 3 +- .../plugins/publish/increment_current_file.py | 1 - .../validate_split_render_is_disabled.py | 7 ++-- .../deadline/plugins/publish/collect_pools.py | 1 - .../publish/submit_houdini_render_deadline.py | 3 +- .../plugins/publish/submit_publish_job.py | 3 +- 9 files changed, 36 insertions(+), 26 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 1cd239e929..f6d42419f9 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 @@ -4,7 +4,7 @@ import hou # noqa from ayon_core.pipeline import CreatorError from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import EnumDef, BoolDef +from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef class CreateRedshiftROP(plugin.HoudiniCreator): @@ -26,8 +26,6 @@ class CreateRedshiftROP(plugin.HoudiniCreator): instance_data.update({"node_type": "Redshift_ROP"}) # Add chunk size attribute instance_data["chunkSize"] = 10 - # Submit for job publishing - instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateRedshiftROP, self).create( product_name, @@ -118,8 +116,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): return super(CreateRedshiftROP, self).remove_instances(instances) - def get_pre_create_attr_defs(self): - attrs = super(CreateRedshiftROP, self).get_pre_create_attr_defs() + def get_instance_attr_defs(self): image_format_enum = [ "exr", "tif", "jpg", "png", ] @@ -128,14 +125,8 @@ class CreateRedshiftROP(plugin.HoudiniCreator): "Full Multi-Layered EXR File" ] - - return attrs + [ - BoolDef("farm", - label="Submitting to Farm", - default=True), - BoolDef("split_render", - label="Split export and render jobs", - default=self.split_render), + return [ + UILabelDef(label="RedShift Render Settings:"), EnumDef("image_format", image_format_enum, default=self.ext, @@ -143,5 +134,24 @@ class CreateRedshiftROP(plugin.HoudiniCreator): EnumDef("multi_layered_mode", multi_layered_mode, default=self.multi_layered_mode, - label="Multi-Layered EXR") + label="Multi-Layered EXR"), + UISeparatorDef(key="1"), + UILabelDef(label="Farm Render Options:"), + BoolDef("farm", + label="Submitting to Farm", + default=True), + BoolDef("split_render", + label="Split export and render jobs", + default=self.split_render), + UISeparatorDef(key="2"), + UILabelDef(label="Local Render Options:"), + BoolDef("skip_render", + label="Skip Render", + tooltip="Enable this option to skip render which publish existing frames.", + default=False), ] + + def get_pre_create_attr_defs(self): + attrs = super(CreateRedshiftROP, self).get_pre_create_attr_defs() + + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index 61894da98e..ffdce1df32 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -6,7 +6,8 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder families = ["mantra_rop", - "karma_rop"] + "karma_rop", + "redshift_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index e94e1187d7..0b8e004873 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -11,7 +11,8 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): # this plugin runs after Collect Render Products order = pyblish.api.CollectorOrder + 0.12 families = ["mantra_rop", - "karma_rop"] + "karma_rop", + "redshift_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py index a7967435c9..e2f51d0dff 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py @@ -11,7 +11,8 @@ class ExtractLocalRender(publish.Extractor): label = "Extract Local Render" hosts = ["houdini"] families = ["mantra_rop", - "karma_rop"] + "karma_rop", + "redshift_rop"] targets = ["local", "remote"] def process(self, instance): diff --git a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py index 5885fd8643..b33b9cc344 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py @@ -18,7 +18,6 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin): order = pyblish.api.IntegratorOrder + 9.0 hosts = ["houdini"] families = ["workfile", - "redshift_rop", "arnold_rop", "usdrender", "render.farm.hou", diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py index 2d7a95e817..cbed59fa3f 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py @@ -14,7 +14,8 @@ class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder hosts = ["houdini"] - families = ["mantra_rop"] + families = ["mantra_rop", + "redshift_rop"] label = "Validate Split Export Is Disabled" actions = [DisableSplitExportAction] @@ -44,7 +45,7 @@ class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): return - split_enabled = creator_attribute["export_job"] + split_enabled = creator_attribute["split_render"] if split_enabled: invalid.append(rop_node) cls.log.error( @@ -61,5 +62,5 @@ class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): instance.data["instance_id"]) creator_attributes = created_instance["creator_attributes"] # Disable export_job - creator_attributes["export_job"] = False + creator_attributes["split_render"] = False create_context.save_changes() diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index bb556c2b9d..05b0e55548 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -41,7 +41,6 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, "renderlayer", "maxrender", "usdrender", - "redshift_rop", "arnold_rop", "vray_rop", "render.farm.hou", diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index b433151b34..39a150ab2d 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -71,7 +71,6 @@ class HoudiniSubmitDeadline( order = pyblish.api.IntegratorOrder hosts = ["houdini"] families = ["usdrender", - "redshift_rop", "arnold_rop", "vray_rop", "render.farm.hou"] @@ -280,7 +279,7 @@ class HoudiniSubmitDeadline( plugin_info = VrayRenderPluginInfo( InputFilename=instance.data["ifdFile"], ) - elif product_type == "redshift_rop": + elif node_type == "Redshift_ROP": plugin_info = RedshiftRenderPluginInfo( SceneFile=instance.data["ifdFile"] ) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 69f626b602..d70bc925ed 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -93,8 +93,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "renderlayer", "imagesequence", "vrayscene", "maxrender", "arnold_rop", "render.farm.hou", - "vray_rop", - "redshift_rop"] + "vray_rop"] aov_filter = [ { From 2228279a2d6cee48f7e0ed18ea07a993d1d4d077 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 17:09:36 +0200 Subject: [PATCH 011/192] refactor 'export_job' variable name into 'split_render' --- .../hosts/houdini/plugins/create/create_arnold_rop.py | 8 ++++---- .../hosts/houdini/plugins/create/create_mantra_rop.py | 8 ++++---- .../hosts/houdini/plugins/create/create_vray_rop.py | 10 +++++----- .../publish/validate_split_render_is_disabled.py | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index b7c5910a4f..68c68c5a16 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -14,7 +14,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): ext = "exr" # Default to split export and render jobs - export_job = True + split_render = True def create(self, product_name, instance_data, pre_create_data): import hou @@ -51,7 +51,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): "ar_exr_half_precision": 1 # half precision } - if pre_create_data.get("export_job"): + if pre_create_data.get("split_render"): ass_filepath = \ "{export_dir}{product_name}/{product_name}.$F4.ass".format( export_dir=hou.text.expandString("$HIP/pyblish/ass/"), @@ -78,9 +78,9 @@ class CreateArnoldRop(plugin.HoudiniCreator): BoolDef("farm", label="Submitting to Farm", default=True), - BoolDef("export_job", + BoolDef("split_render", label="Split export and render jobs", - default=self.export_job), + default=self.split_render), EnumDef("image_format", image_format_enum, default=self.ext, diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index 6dac3ff02a..58aadfd26c 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -12,7 +12,7 @@ class CreateMantraROP(plugin.HoudiniCreator): icon = "magic" # Default to split export and render jobs - export_job = True + split_render = True def create(self, product_name, instance_data, pre_create_data): import hou # noqa @@ -48,7 +48,7 @@ class CreateMantraROP(plugin.HoudiniCreator): "vm_picture": filepath, } - if pre_create_data.get("export_job"): + if pre_create_data.get("split_render"): ifd_filepath = \ "{export_dir}{product_name}/{product_name}.$F4.ifd".format( export_dir=hou.text.expandString("$HIP/pyblish/ifd/"), @@ -101,9 +101,9 @@ class CreateMantraROP(plugin.HoudiniCreator): BoolDef("farm", label="Submitting to Farm", default=True), - BoolDef("export_job", + BoolDef("split_render", label="Split export and render jobs", - default=self.export_job), + default=self.split_render), UISeparatorDef(key="2"), UILabelDef(label="Local Render Options:"), BoolDef("skip_render", diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index 6b2396bffb..f7779cc67c 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -3,7 +3,7 @@ import hou from ayon_core.hosts.houdini.api import plugin -from ayon_core.pipeline import CreatedInstance, CreatorError +from ayon_core.pipeline import CreatorError from ayon_core.lib import EnumDef, BoolDef @@ -17,7 +17,7 @@ class CreateVrayROP(plugin.HoudiniCreator): ext = "exr" # Default to split export and render jobs - export_job = True + split_render = True def create(self, product_name, instance_data, pre_create_data): @@ -55,7 +55,7 @@ class CreateVrayROP(plugin.HoudiniCreator): "SettingsEXR_bits_per_channel": "16" # half precision } - if pre_create_data.get("export_job"): + if pre_create_data.get("split_render"): scene_filepath = \ "{export_dir}{product_name}/{product_name}.$F4.vrscene".format( export_dir=hou.text.expandString("$HIP/pyblish/vrscene/"), @@ -154,9 +154,9 @@ class CreateVrayROP(plugin.HoudiniCreator): BoolDef("farm", label="Submitting to Farm", default=True), - BoolDef("export_job", + BoolDef("split_render", label="Split export and render jobs", - default=self.export_job), + default=self.split_render), EnumDef("image_format", image_format_enum, default=self.ext, diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py index cbed59fa3f..d54f10b29b 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py @@ -61,6 +61,6 @@ class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): created_instance = create_context.get_instance_by_id( instance.data["instance_id"]) creator_attributes = created_instance["creator_attributes"] - # Disable export_job + # Disable split_render creator_attributes["split_render"] = False create_context.save_changes() From 0ba5fee7e259543b0c8df66d9f2e898b46089ccc Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 17:23:56 +0200 Subject: [PATCH 012/192] support local rendering for arnold_rop --- .../plugins/create/create_arnold_rop.py | 23 ++++++++++++------- .../plugins/publish/collect_farm_instances.py | 3 ++- .../publish/collect_local_render_instances.py | 3 ++- .../plugins/publish/extract_local_render.py | 3 ++- .../plugins/publish/increment_current_file.py | 1 - .../validate_split_render_is_disabled.py | 3 ++- .../deadline/plugins/publish/collect_pools.py | 1 - .../publish/submit_houdini_render_deadline.py | 3 +-- .../plugins/publish/submit_publish_job.py | 2 +- 9 files changed, 25 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index 68c68c5a16..c65c425a45 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -1,5 +1,5 @@ from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import EnumDef, BoolDef +from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef class CreateArnoldRop(plugin.HoudiniCreator): @@ -25,8 +25,6 @@ class CreateArnoldRop(plugin.HoudiniCreator): # Add chunk size attribute instance_data["chunkSize"] = 1 - # Submit for job publishing - instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateArnoldRop, self).create( product_name, @@ -66,15 +64,13 @@ class CreateArnoldRop(plugin.HoudiniCreator): to_lock = ["productType", "id"] self.lock_parameters(instance_node, to_lock) - def get_pre_create_attr_defs(self): - attrs = super(CreateArnoldRop, self).get_pre_create_attr_defs() - + def get_instance_attr_defs(self): image_format_enum = [ "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] - return attrs + [ + return [ BoolDef("farm", label="Submitting to Farm", default=True), @@ -84,5 +80,16 @@ class CreateArnoldRop(plugin.HoudiniCreator): EnumDef("image_format", image_format_enum, default=self.ext, - label="Image Format Options") + label="Image Format Options"), + UISeparatorDef(key="2"), + UILabelDef(label="Local Render Options:"), + BoolDef("skip_render", + label="Skip Render", + tooltip="Enable this option to skip render which publish existing frames.", + default=False), ] + + def get_pre_create_attr_defs(self): + attrs = super(CreateArnoldRop, self).get_pre_create_attr_defs() + + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index ffdce1df32..afe67279e1 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -7,7 +7,8 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder families = ["mantra_rop", "karma_rop", - "redshift_rop"] + "redshift_rop", + "arnold_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 0b8e004873..9b121a6894 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -12,7 +12,8 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder + 0.12 families = ["mantra_rop", "karma_rop", - "redshift_rop"] + "redshift_rop", + "arnold_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py index e2f51d0dff..1fce9dc87f 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py @@ -12,7 +12,8 @@ class ExtractLocalRender(publish.Extractor): hosts = ["houdini"] families = ["mantra_rop", "karma_rop", - "redshift_rop"] + "redshift_rop", + "arnold_rop"] targets = ["local", "remote"] def process(self, instance): diff --git a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py index b33b9cc344..acb66afa4e 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py @@ -18,7 +18,6 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin): order = pyblish.api.IntegratorOrder + 9.0 hosts = ["houdini"] families = ["workfile", - "arnold_rop", "usdrender", "render.farm.hou", "render.local.hou", diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py index d54f10b29b..38912f434f 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py @@ -15,7 +15,8 @@ class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder hosts = ["houdini"] families = ["mantra_rop", - "redshift_rop"] + "redshift_rop", + "arnold_rop"] label = "Validate Split Export Is Disabled" actions = [DisableSplitExportAction] diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index 05b0e55548..c30c7fb0f2 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -41,7 +41,6 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, "renderlayer", "maxrender", "usdrender", - "arnold_rop", "vray_rop", "render.farm.hou", "publish.hou"] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 39a150ab2d..17a0b4ad32 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -71,7 +71,6 @@ class HoudiniSubmitDeadline( order = pyblish.api.IntegratorOrder hosts = ["houdini"] families = ["usdrender", - "arnold_rop", "vray_rop", "render.farm.hou"] targets = ["local"] @@ -266,7 +265,7 @@ class HoudiniSubmitDeadline( rop_node = hou.node(instance.data.get("instance_node")) node_type = rop_node.type().name() - if product_type == "arnold_rop": + if node_type == "arnold": plugin_info = ArnoldRenderDeadlinePluginInfo( InputFile=instance.data["ifdFile"] ) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index d70bc925ed..6171d7135f 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -92,7 +92,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "prerender.farm", "prerender.frames_farm", "renderlayer", "imagesequence", "vrayscene", "maxrender", - "arnold_rop", "render.farm.hou", + "render.farm.hou", "vray_rop"] aov_filter = [ From c7e0821ff57e046c8de87c37d0779d296c8ca407 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Mar 2024 17:42:49 +0200 Subject: [PATCH 013/192] support local rendering for vray_rop --- .../houdini/plugins/create/create_vray_rop.py | 36 ++++++++++++------- .../plugins/publish/collect_farm_instances.py | 3 +- .../publish/collect_local_render_instances.py | 3 +- .../plugins/publish/extract_local_render.py | 3 +- .../validate_split_render_is_disabled.py | 3 +- .../deadline/plugins/publish/collect_pools.py | 1 - .../publish/submit_houdini_render_deadline.py | 3 +- .../plugins/publish/submit_publish_job.py | 3 +- 8 files changed, 33 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index f7779cc67c..682eec379e 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -4,7 +4,7 @@ import hou from ayon_core.hosts.houdini.api import plugin from ayon_core.pipeline import CreatorError -from ayon_core.lib import EnumDef, BoolDef +from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef class CreateVrayROP(plugin.HoudiniCreator): @@ -25,8 +25,6 @@ class CreateVrayROP(plugin.HoudiniCreator): instance_data.update({"node_type": "vray_renderer"}) # Add chunk size attribute instance_data["chunkSize"] = 10 - # Submit for job publishing - instance_data["farm"] = pre_create_data.get("farm") instance = super(CreateVrayROP, self).create( product_name, @@ -143,20 +141,13 @@ class CreateVrayROP(plugin.HoudiniCreator): return super(CreateVrayROP, self).remove_instances(instances) - def get_pre_create_attr_defs(self): - attrs = super(CreateVrayROP, self).get_pre_create_attr_defs() + def get_instance_attr_defs(self): image_format_enum = [ "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] - return attrs + [ - BoolDef("farm", - label="Submitting to Farm", - default=True), - BoolDef("split_render", - label="Split export and render jobs", - default=self.split_render), + return [ EnumDef("image_format", image_format_enum, default=self.ext, @@ -170,5 +161,24 @@ class CreateVrayROP(plugin.HoudiniCreator): label="Render Element", tooltip="Create Render Element Node " "if enabled", - default=False) + default=False), + UISeparatorDef(key="1"), + UILabelDef(label="Farm Render Options:"), + BoolDef("farm", + label="Submitting to Farm", + default=True), + BoolDef("split_render", + label="Split export and render jobs", + default=self.split_render), + UISeparatorDef(key="2"), + UILabelDef(label="Local Render Options:"), + BoolDef("skip_render", + label="Skip Render", + tooltip="Enable this option to skip render which publish existing frames.", + default=False), ] + + def get_pre_create_attr_defs(self): + attrs = super(CreateVrayROP, self).get_pre_create_attr_defs() + + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index afe67279e1..37a979d94b 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -8,7 +8,8 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): families = ["mantra_rop", "karma_rop", "redshift_rop", - "arnold_rop"] + "arnold_rop", + "vray_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 9b121a6894..9ad44da978 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -13,7 +13,8 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): families = ["mantra_rop", "karma_rop", "redshift_rop", - "arnold_rop"] + "arnold_rop", + "vray_rop"] hosts = ["houdini"] targets = ["local", "remote"] diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py index 1fce9dc87f..5e89e760ab 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py @@ -13,7 +13,8 @@ class ExtractLocalRender(publish.Extractor): families = ["mantra_rop", "karma_rop", "redshift_rop", - "arnold_rop"] + "arnold_rop", + "vray_rop"] targets = ["local", "remote"] def process(self, instance): diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py index 38912f434f..72ccb90e86 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py @@ -16,7 +16,8 @@ class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): hosts = ["houdini"] families = ["mantra_rop", "redshift_rop", - "arnold_rop"] + "arnold_rop", + "vray_rop"] label = "Validate Split Export Is Disabled" actions = [DisableSplitExportAction] diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index c30c7fb0f2..76b397eee0 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -41,7 +41,6 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, "renderlayer", "maxrender", "usdrender", - "vray_rop", "render.farm.hou", "publish.hou"] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 17a0b4ad32..404c7ade04 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -71,7 +71,6 @@ class HoudiniSubmitDeadline( order = pyblish.api.IntegratorOrder hosts = ["houdini"] families = ["usdrender", - "vray_rop", "render.farm.hou"] targets = ["local"] use_published = True @@ -274,7 +273,7 @@ class HoudiniSubmitDeadline( SceneFile=instance.data["ifdFile"], Version=hou_major_minor, ) - elif product_type == "vray_rop": + elif node_type == "vray_renderer": plugin_info = VrayRenderPluginInfo( InputFilename=instance.data["ifdFile"], ) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 6171d7135f..f8df1b4d4c 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -92,8 +92,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "prerender.farm", "prerender.frames_farm", "renderlayer", "imagesequence", "vrayscene", "maxrender", - "render.farm.hou", - "vray_rop"] + "render.farm.hou"] aov_filter = [ { From bcb1c2a0ba3b49c43a08507ad52a9fdce07037ef Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 2 Apr 2024 21:42:56 +0200 Subject: [PATCH 014/192] Use render targets in a similar fashion to Nuke + set houdini parms according to render target value --- .../plugins/create/create_arnold_rop.py | 30 +++++++-------- .../plugins/create/create_karma_rop.py | 23 +++++++----- .../plugins/create/create_mantra_rop.py | 37 +++++++------------ .../plugins/create/create_redshift_rop.py | 33 +++++++---------- .../houdini/plugins/create/create_vray_rop.py | 32 +++++++--------- .../plugins/publish/collect_farm_instances.py | 34 +++++++++++++++-- .../publish/collect_local_render_instances.py | 14 ++++--- .../plugins/publish/extract_local_render.py | 21 ++++++++++- 8 files changed, 128 insertions(+), 96 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index c65c425a45..07c1c98a28 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -1,5 +1,5 @@ from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef +from ayon_core.lib import EnumDef class CreateArnoldRop(plugin.HoudiniCreator): @@ -13,8 +13,8 @@ class CreateArnoldRop(plugin.HoudiniCreator): # Default extension ext = "exr" - # Default to split export and render jobs - split_render = True + # Default render target + render_target = "farm_split" def create(self, product_name, instance_data, pre_create_data): import hou @@ -49,7 +49,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): "ar_exr_half_precision": 1 # half precision } - if pre_create_data.get("split_render"): + if pre_create_data.get("render_target") == "farm_split": ass_filepath = \ "{export_dir}{product_name}/{product_name}.$F4.ass".format( export_dir=hou.text.expandString("$HIP/pyblish/ass/"), @@ -69,24 +69,22 @@ class CreateArnoldRop(plugin.HoudiniCreator): "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + "farm_split": "Farm Rendering - Split export & render jobs", + } return [ - BoolDef("farm", - label="Submitting to Farm", - default=True), - BoolDef("split_render", - label="Split export and render jobs", - default=self.split_render), + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target), EnumDef("image_format", image_format_enum, default=self.ext, label="Image Format Options"), - UISeparatorDef(key="2"), - UILabelDef(label="Local Render Options:"), - BoolDef("skip_render", - label="Skip Render", - tooltip="Enable this option to skip render which publish existing frames.", - default=False), ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index c791cfe647..5d56150df9 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin to create Karma ROP.""" from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import BoolDef, EnumDef, NumberDef, UISeparatorDef, UILabelDef +from ayon_core.lib import BoolDef, EnumDef, NumberDef class CreateKarmaROP(plugin.HoudiniCreator): @@ -11,6 +11,9 @@ class CreateKarmaROP(plugin.HoudiniCreator): product_type = "karma_rop" icon = "magic" + # Default render target + render_target = "farm" + def create(self, product_name, instance_data, pre_create_data): import hou # noqa @@ -89,11 +92,17 @@ class CreateKarmaROP(plugin.HoudiniCreator): "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } return [ - BoolDef("farm", - label="Submitting to Farm", - default=True), + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target), EnumDef("image_format", image_format_enum, default="exr", @@ -109,12 +118,6 @@ class CreateKarmaROP(plugin.HoudiniCreator): BoolDef("cam_res", label="Camera Resolution", default=False), - UISeparatorDef(key="2"), - UILabelDef(label="Local Render Options:"), - BoolDef("skip_render", - label="Skip Render", - tooltip="Enable this option to skip render which publish existing frames.", - default=False), ] diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index 58aadfd26c..6705621f58 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin to create Mantra ROP.""" from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef +from ayon_core.lib import EnumDef, BoolDef class CreateMantraROP(plugin.HoudiniCreator): @@ -11,8 +11,8 @@ class CreateMantraROP(plugin.HoudiniCreator): product_type = "mantra_rop" icon = "magic" - # Default to split export and render jobs - split_render = True + # Default render target + render_target = "farm_split" def create(self, product_name, instance_data, pre_create_data): import hou # noqa @@ -21,10 +21,6 @@ class CreateMantraROP(plugin.HoudiniCreator): instance_data.update({"node_type": "ifd"}) # Add chunk size attribute instance_data["chunkSize"] = 10 - # Submit for job publishing - creator_attributes = instance_data.setdefault( - "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data.get("farm") instance = super(CreateMantraROP, self).create( product_name, @@ -48,7 +44,7 @@ class CreateMantraROP(plugin.HoudiniCreator): "vm_picture": filepath, } - if pre_create_data.get("split_render"): + if pre_create_data.get("render_target") == "farm_split": ifd_filepath = \ "{export_dir}{product_name}/{product_name}.$F4.ifd".format( export_dir=hou.text.expandString("$HIP/pyblish/ifd/"), @@ -84,9 +80,18 @@ class CreateMantraROP(plugin.HoudiniCreator): "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + "farm_split": "Farm Rendering - Split export & render jobs", + } return [ - UILabelDef(label="Mantra Render Settings:"), + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target), EnumDef("image_format", image_format_enum, default="exr", @@ -96,20 +101,6 @@ class CreateMantraROP(plugin.HoudiniCreator): tooltip="Override the current camera " "resolution, recommended for IPR.", default=False), - UISeparatorDef(key="1"), - UILabelDef(label="Farm Render Options:"), - BoolDef("farm", - label="Submitting to Farm", - default=True), - BoolDef("split_render", - label="Split export and render jobs", - default=self.split_render), - UISeparatorDef(key="2"), - UILabelDef(label="Local Render Options:"), - BoolDef("skip_render", - label="Skip Render", - tooltip="Enable this option to skip render which publish existing frames.", - default=False), ] def get_pre_create_attr_defs(self): 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 f6d42419f9..02c3ed2fc0 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 @@ -4,7 +4,7 @@ import hou # noqa from ayon_core.pipeline import CreatorError from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef +from ayon_core.lib import EnumDef class CreateRedshiftROP(plugin.HoudiniCreator): @@ -17,8 +17,8 @@ class CreateRedshiftROP(plugin.HoudiniCreator): ext = "exr" multi_layered_mode = "No Multi-Layered EXR File" - # Default to split export and render jobs - split_render = True + # Default render target + render_target = "farm_split" def create(self, product_name, instance_data, pre_create_data): @@ -97,7 +97,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): rs_filepath = f"{export_dir}{product_name}/{product_name}.$F4.rs" parms["RS_archive_file"] = rs_filepath - if pre_create_data.get("split_render", self.split_render): + if pre_create_data.get("render_target") == "farm_split": parms["RS_archive_enable"] = 1 instance_node.setParms(parms) @@ -124,9 +124,18 @@ class CreateRedshiftROP(plugin.HoudiniCreator): "No Multi-Layered EXR File", "Full Multi-Layered EXR File" ] + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + "farm_split": "Farm Rendering - Split export & render jobs", + } return [ - UILabelDef(label="RedShift Render Settings:"), + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target), EnumDef("image_format", image_format_enum, default=self.ext, @@ -135,20 +144,6 @@ class CreateRedshiftROP(plugin.HoudiniCreator): multi_layered_mode, default=self.multi_layered_mode, label="Multi-Layered EXR"), - UISeparatorDef(key="1"), - UILabelDef(label="Farm Render Options:"), - BoolDef("farm", - label="Submitting to Farm", - default=True), - BoolDef("split_render", - label="Split export and render jobs", - default=self.split_render), - UISeparatorDef(key="2"), - UILabelDef(label="Local Render Options:"), - BoolDef("skip_render", - label="Skip Render", - tooltip="Enable this option to skip render which publish existing frames.", - default=False), ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index 682eec379e..147a34191f 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -4,7 +4,7 @@ import hou from ayon_core.hosts.houdini.api import plugin from ayon_core.pipeline import CreatorError -from ayon_core.lib import EnumDef, BoolDef, UISeparatorDef, UILabelDef +from ayon_core.lib import EnumDef, BoolDef class CreateVrayROP(plugin.HoudiniCreator): @@ -16,8 +16,8 @@ class CreateVrayROP(plugin.HoudiniCreator): icon = "magic" ext = "exr" - # Default to split export and render jobs - split_render = True + # Default render target + render_target = "farm_split" def create(self, product_name, instance_data, pre_create_data): @@ -53,7 +53,7 @@ class CreateVrayROP(plugin.HoudiniCreator): "SettingsEXR_bits_per_channel": "16" # half precision } - if pre_create_data.get("split_render"): + if pre_create_data.get("render_target") == "farm_split": scene_filepath = \ "{export_dir}{product_name}/{product_name}.$F4.vrscene".format( export_dir=hou.text.expandString("$HIP/pyblish/vrscene/"), @@ -146,8 +146,18 @@ class CreateVrayROP(plugin.HoudiniCreator): "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", "rad", "rat", "rta", "sgi", "tga", "tif", ] + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + "farm_split": "Farm Rendering - Split export & render jobs", + } return [ + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target), EnumDef("image_format", image_format_enum, default=self.ext, @@ -162,20 +172,6 @@ class CreateVrayROP(plugin.HoudiniCreator): tooltip="Create Render Element Node " "if enabled", default=False), - UISeparatorDef(key="1"), - UILabelDef(label="Farm Render Options:"), - BoolDef("farm", - label="Submitting to Farm", - default=True), - BoolDef("split_render", - label="Split export and render jobs", - default=self.split_render), - UISeparatorDef(key="2"), - UILabelDef(label="Local Render Options:"), - BoolDef("skip_render", - label="Skip Render", - tooltip="Enable this option to skip render which publish existing frames.", - default=False), ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index 37a979d94b..56a2b42940 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -16,12 +16,40 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): label = "Collect farm instances" def process(self, instance): + import hou + creator_attribute = instance.data["creator_attributes"] - farm_enabled = creator_attribute["farm"] - instance.data["farm"] = farm_enabled - if not farm_enabled: + product_type = instance.data["productType"] + rop_node = hou.node(instance.data.get("instance_node")) + + # Align split parameter value on rop node to the render target. + if creator_attribute.get("render_target") == "farm_split": + if product_type == "arnold_rop": + rop_node.setParms({"ar_ass_export_enable": 1}) + elif product_type == "mantra_rop": + rop_node.setParms({"soho_outputmode": 1}) + elif product_type == "redshift_rop": + rop_node.setParms({"RS_archive_enable": 1}) + elif product_type == "vray_rop": + rop_node.setParms({"render_export_mode": "2"}) + else: + if product_type == "arnold_rop": + rop_node.setParms({"ar_ass_export_enable": 0}) + elif product_type == "mantra_rop": + rop_node.setParms({"soho_outputmode": 0}) + elif product_type == "redshift_rop": + rop_node.setParms({"RS_archive_enable": 0}) + elif product_type == "vray_rop": + rop_node.setParms({"render_export_mode": "1"}) + + # Collect Render Target + if creator_attribute.get("render_target") not in { + "farm_split", "farm" + }: + instance.data["farm"] = False self.log.debug("Render on farm is disabled. " "Skipping farm collecting.") return + instance.data["farm"] = True instance.data["families"].append("render.farm.hou") diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 9ad44da978..194a05f42d 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -21,10 +21,8 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): label = "Collect local render instances" def process(self, instance): - creator_attribute = instance.data["creator_attributes"] - farm_enabled = creator_attribute["farm"] - instance.data["farm"] = farm_enabled - if farm_enabled: + + if instance.data["farm"]: self.log.debug("Render on farm is enabled. " "Skipping local render collecting.") return @@ -45,7 +43,13 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): # AOV=aov_name, # productName=instance.data["productName"] # ) - product_name = "render{Task}{productName}_{AOV}".format( + name_template = "render{Task}{productName}_{AOV}" + if not aov_name: + # This is done to remove the trailing `_` + # if aov name is an empty string. + name_template = "render{Task}{productName}" + + product_name = name_template.format( Task=self._capitalize(instance.data["task"]), productName=self._capitalize(instance.data["productName"]), AOV=aov_name diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py index 5e89e760ab..120e5563e9 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py @@ -23,11 +23,28 @@ class ExtractLocalRender(publish.Extractor): return creator_attribute = instance.data["creator_attributes"] - skip_render = creator_attribute["skip_render"] - if skip_render: + if creator_attribute.get("render_target") == "local_no_render": self.log.debug("Skip render is enabled, skipping rendering.") return + # Make sure split parameter is turned off. + # Otherwise, render nodes will generate intermediate + # render files instead of render. + product_type = instance.data["productType"] + rop_node = hou.node(instance.data.get("instance_node")) + + if product_type == "arnold_rop": + rop_node.setParms({"ar_ass_export_enable": 0}) + elif product_type == "mantra_rop": + rop_node.setParms({"soho_outputmode": 0}) + elif product_type == "redshift_rop": + rop_node.setParms({"RS_archive_enable": 0}) + elif product_type == "vray_rop": + rop_node.setParms({"render_export_mode": "1"}) + ropnode = hou.node(instance.data.get("instance_node")) render_rop(ropnode) + + # TODO: Check for missing frames. + # self.log.debug(instance.data["expectedFiles"]) From bca10c7c7d0d4d8457a2ba25730dd37ebd5bbff9 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 2 Apr 2024 21:43:36 +0200 Subject: [PATCH 015/192] remove unnecessary validator --- .../validate_split_render_is_disabled.py | 68 ------------------- 1 file changed, 68 deletions(-) delete mode 100644 client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py deleted file mode 100644 index 72ccb90e86..0000000000 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_split_render_is_disabled.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -import pyblish.api -import hou -from ayon_core.pipeline import PublishValidationError -from ayon_core.pipeline.publish import RepairAction - - -class DisableSplitExportAction(RepairAction): - label = "Disable Split Export" - - -class ValidateSplitExportIsDisabled(pyblish.api.InstancePlugin): - """Validate the Instance has no current cooking errors.""" - - order = pyblish.api.ValidatorOrder - hosts = ["houdini"] - families = ["mantra_rop", - "redshift_rop", - "arnold_rop", - "vray_rop"] - label = "Validate Split Export Is Disabled" - actions = [DisableSplitExportAction] - - def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - nodes = [n.path() for n in invalid] - raise PublishValidationError( - "See log for details. " - "Invalid nodes: {0}".format(nodes) - ) - - - @classmethod - def get_invalid(cls, instance): - - invalid = [] - rop_node = hou.node(instance.data["instance_node"]) - - creator_attribute = instance.data["creator_attributes"] - farm_enabled = creator_attribute["farm"] - if farm_enabled: - cls.log.debug( - "Farm is enabled, skipping validation." - ) - return - - - split_enabled = creator_attribute["split_render"] - if split_enabled: - invalid.append(rop_node) - cls.log.error( - "Split Export must be disabled in local render instances." - ) - - return invalid - - @classmethod - def repair(cls, instance): - - create_context = instance.context.data["create_context"] - created_instance = create_context.get_instance_by_id( - instance.data["instance_id"]) - creator_attributes = created_instance["creator_attributes"] - # Disable split_render - creator_attributes["split_render"] = False - create_context.save_changes() From 4a5f0ebc92113f2dafd263ac3b771fb2f194563b Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 2 Apr 2024 21:44:54 +0200 Subject: [PATCH 016/192] set rsnode parms according to render target value before collecting expected files --- .../plugins/publish/collect_redshift_rop.py | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 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 55a55bb12a..191a9c1ebc 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,11 +60,27 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): instance.data["ifdFile"] = beauty_export_product instance.data["exportFiles"] = list(export_products) - 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 = "" + # Set MultiLayer Mode. + creator_attribute = instance.data["creator_attributes"] + ext = creator_attribute.get("image_format") + multi_layered_mode = creator_attribute.get("multi_layered_mode") + full_exr_mode = False + if ext == "exr": + if multi_layered_mode == "No Multi-Layered EXR File": + rop.setParms({ + "RS_outputMultilayerMode": "1", + "RS_aovMultipart": False + }) + full_exr_mode = True + # Ignore beauty suffix if full mode is enabled + # As this is what the rop does. + beauty_suffix = "" + + elif multi_layered_mode == "Full Multi-Layered EXR File": + rop.setParms({ + "RS_outputMultilayerMode": "2", + "RS_aovMultipart": True + }) # Default beauty/main layer AOV beauty_product = self.get_render_product_name( @@ -75,7 +91,7 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): beauty_suffix: self.generate_expected_files(instance, beauty_product) } - + aovs_rop = rop.parm("RS_aovGetFromNode").evalAsNode() if aovs_rop: rop = aovs_rop @@ -98,7 +114,7 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): 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 7f703585f12d47bc7d6044faf3641fc61cbdab55 Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Thu, 4 Apr 2024 10:51:51 +0200 Subject: [PATCH 017/192] remove targets class attribute. Co-authored-by: Kayla Man <64118225+moonyuet@users.noreply.github.com> --- .../houdini/plugins/publish/collect_local_render_instances.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 194a05f42d..1fd4129ee1 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -17,7 +17,6 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): "vray_rop"] hosts = ["houdini"] - targets = ["local", "remote"] label = "Collect local render instances" def process(self, instance): From 4e6bd3d3361708fa7cf84ef69e0e5715ed451df2 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 4 Apr 2024 12:32:57 +0200 Subject: [PATCH 018/192] remove the un-necessary 'render.farm.hou' intermidate family --- .../plugins/publish/collect_farm_instances.py | 1 - .../plugins/publish/increment_current_file.py | 6 +++++- .../deadline/plugins/publish/collect_pools.py | 6 +++++- .../publish/submit_houdini_render_deadline.py | 12 +++++++++++- .../deadline/plugins/publish/submit_publish_job.py | 6 +++++- 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index 56a2b42940..391afe7387 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -52,4 +52,3 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): return instance.data["farm"] = True - instance.data["families"].append("render.farm.hou") diff --git a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py index acb66afa4e..ffd9a75620 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py @@ -19,7 +19,11 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin): hosts = ["houdini"] families = ["workfile", "usdrender", - "render.farm.hou", + "mantra_rop", + "karma_rop", + "redshift_rop", + "arnold_rop", + "vray_rop", "render.local.hou", "publish.hou"] optional = True diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index 76b397eee0..6b7449b8f8 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -41,7 +41,11 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, "renderlayer", "maxrender", "usdrender", - "render.farm.hou", + "mantra_rop", + "karma_rop", + "redshift_rop", + "arnold_rop", + "vray_rop", "publish.hou"] primary_pool = None diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 404c7ade04..b562e2848b 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -71,7 +71,12 @@ class HoudiniSubmitDeadline( order = pyblish.api.IntegratorOrder hosts = ["houdini"] families = ["usdrender", - "render.farm.hou"] + "mantra_rop", + "karma_rop", + "redshift_rop", + "arnold_rop", + "vray_rop"] + targets = ["local"] use_published = True @@ -314,6 +319,11 @@ class HoudiniSubmitDeadline( return attr.asdict(plugin_info) def process(self, instance): + if not instance.data["farm"]: + self.log.debug("Render on farm is disabled. " + "Skipping deadline submission.") + return + super(HoudiniSubmitDeadline, self).process(instance) # TODO: Avoid the need for this logic here, needed for submit publish diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index f8df1b4d4c..773532e5c0 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -92,7 +92,11 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "prerender.farm", "prerender.frames_farm", "renderlayer", "imagesequence", "vrayscene", "maxrender", - "render.farm.hou"] + "mantra_rop", + "karma_rop", + "redshift_rop", + "arnold_rop", + "vray_rop"] aov_filter = [ { From 05bdbb2aa6ff99eed01848ce2e53a3fbe3ff9341 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 4 Apr 2024 12:58:29 +0200 Subject: [PATCH 019/192] Abort publishing if there are missing frames. --- .../plugins/publish/extract_local_render.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py index 120e5563e9..23a64945aa 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py @@ -3,6 +3,7 @@ import pyblish.api from ayon_core.pipeline import publish from ayon_core.hosts.houdini.api.lib import render_rop import hou +import os class ExtractLocalRender(publish.Extractor): @@ -46,5 +47,17 @@ class ExtractLocalRender(publish.Extractor): ropnode = hou.node(instance.data.get("instance_node")) render_rop(ropnode) - # TODO: Check for missing frames. - # self.log.debug(instance.data["expectedFiles"]) + # Check missing frames. + # Frames won't exist if user cancels the render. + expected_files = next(iter(instance.data["expectedFiles"]), {}) + expected_files = sum(expected_files.values(), []) + missing_frames = [ + frame + for frame in expected_files + if not os.path.exists(frame) + ] + if missing_frames: + # TODO: Use user friendly error reporting. + raise RuntimeError("Failed to complete render extraction. " + "Missing output files: {}".format( + missing_frames)) From fe6c1fc9f5efa2c0e5c73d397c8584443ae7cd94 Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Thu, 4 Apr 2024 15:54:38 +0200 Subject: [PATCH 020/192] remove targets class attribute. Co-authored-by: Kayla Man <64118225+moonyuet@users.noreply.github.com> --- .../hosts/houdini/plugins/publish/extract_local_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py index 23a64945aa..cf94019947 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py @@ -16,7 +16,6 @@ class ExtractLocalRender(publish.Extractor): "redshift_rop", "arnold_rop", "vray_rop"] - targets = ["local", "remote"] def process(self, instance): if instance.data.get("farm"): From a6ca1488997950044ebddc69b418f5605b9103ab Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 8 Apr 2024 14:24:06 +0200 Subject: [PATCH 021/192] Houdini local render: support single frame --- .../plugins/publish/collect_local_render_instances.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 1fd4129ee1..1dc26e1322 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -62,6 +62,13 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): staging_dir = os.path.dirname(aov_filepaths[0]) ext = aov_filepaths[0].split(".")[-1] + # Support Single frame. + # The integrator wants single files to be a single + # filename instead of a list. + # More info: https://github.com/ynput/ayon-core/issues/238 + if len(aov_filenames) == 1: + aov_filenames = aov_filenames[0] + aov_instance.data.update({ # 'label': label, "task": instance.data["task"], @@ -85,6 +92,7 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): } ] }) + self.log.debug(aov_instance.data) # Remove Mantra instance # I can't remove it here as I still need it to trigger the render. From d7b1f3a3f7a82be99e325201b14dcffbafab4614 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 8 Apr 2024 16:34:38 +0200 Subject: [PATCH 022/192] Houdini local render: support adding review family to the render --- .../publish/collect_local_render_instances.py | 5 ++--- .../plugins/publish/collect_review_data.py | 20 +++++++++++++++++-- .../houdini/plugins/publish/extract_opengl.py | 4 ++++ .../publish/validate_review_colorspace.py | 8 +++++++- .../plugins/publish/validate_scene_review.py | 4 ++++ 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 1dc26e1322..e221990f2b 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -78,21 +78,20 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): "productType": product_type, "productName": product_name, "productGroup": product_group, - "tags": [], - "families": ["render.local.hou"], + "families": ["render.local.hou", "review"], "instance_node": instance.data["instance_node"], "representations": [ { "stagingDir": staging_dir, "ext": ext, "name": ext, + "tags": ["review"], "files": aov_filenames, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"] } ] }) - self.log.debug(aov_instance.data) # Remove Mantra instance # I can't remove it here as I still need it to trigger the render. diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py index 9671945b9a..7714ed0954 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py @@ -8,7 +8,8 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin): label = "Collect Review Data" # This specific order value is used so that # this plugin runs after CollectRopFrameRange - order = pyblish.api.CollectorOrder + 0.1 + # Also after CollectLocalRenderInstances + order = pyblish.api.CollectorOrder + 0.13 hosts = ["houdini"] families = ["review"] @@ -28,7 +29,8 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin): ropnode_path = instance.data["instance_node"] ropnode = hou.node(ropnode_path) - camera_path = ropnode.parm("camera").eval() + # Get camera based on the instance_node type. + camera_path = self._get_camera_path(ropnode) camera_node = hou.node(camera_path) if not camera_node: self.log.warning("No valid camera node found on review node: " @@ -55,3 +57,17 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin): # Store focal length in `burninDataMembers` burnin_members = instance.data.setdefault("burninDataMembers", {}) burnin_members["focalLength"] = focal_length + + def _get_camera_path(self, ropnode): + if ropnode.type().name() in { + "opengl", "karma", "ifd", "arnold" + }: + return ropnode.parm("camera").eval() + + elif ropnode.type().name() == "Redshift_ROP": + return ropnode.parm("RS_renderCamera").eval() + + elif ropnode.type().name() == "vray_renderer": + return ropnode.parm("render_camera").eval() + + return "" diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py index fabdfd9a9d..69bbb22340 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py @@ -17,6 +17,10 @@ class ExtractOpenGL(publish.Extractor): def process(self, instance): ropnode = hou.node(instance.data.get("instance_node")) + if ropnode.type().name() != "opengl": + self.log.debug("Skipping OpenGl extraction. Rop node {} " + "is not an OpenGl node.".format(ropnode.path())) + return output = ropnode.evalParm("picture") staging_dir = os.path.normpath(os.path.dirname(output)) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py index 031138e21d..e02ce93f0d 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py @@ -33,6 +33,13 @@ class ValidateReviewColorspace(pyblish.api.InstancePlugin, def process(self, instance): + rop_node = hou.node(instance.data["instance_node"]) + + if rop_node.type().name() != "opengl": + self.log.debug("Skipping Validation. Rop node {} " + "is not an OpenGl node.".format(rop_node.path())) + return + if not self.is_active(instance.data): return @@ -43,7 +50,6 @@ class ValidateReviewColorspace(pyblish.api.InstancePlugin, ) return - rop_node = hou.node(instance.data["instance_node"]) if rop_node.evalParm("colorcorrect") != 2: # any colorspace settings other than default requires # 'Color Correct' parm to be set to 'OpenColorIO' diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py index b6007d3f0f..9b81f0f8ed 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py @@ -19,6 +19,10 @@ class ValidateSceneReview(pyblish.api.InstancePlugin): report = [] instance_node = hou.node(instance.data.get("instance_node")) + if instance_node.type().name() != "opengl": + self.log.debug("Skipping Validation. Rop node {} " + "is not an OpenGl node.".format(instance_node.path())) + return invalid = self.get_invalid_scene_path(instance_node) if invalid: From c5df561c970bc1cfc0498f8b1adf52392f5fe969 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 9 Apr 2024 20:20:33 +0800 Subject: [PATCH 023/192] Transfer settings from pre create to instance --- .../hosts/houdini/plugins/create/create_arnold_rop.py | 6 ++++++ .../hosts/houdini/plugins/create/create_karma_rop.py | 6 ++++++ .../hosts/houdini/plugins/create/create_mantra_rop.py | 6 ++++++ .../hosts/houdini/plugins/create/create_redshift_rop.py | 6 ++++++ .../hosts/houdini/plugins/create/create_vray_rop.py | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index 07c1c98a28..08ed1bc91a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -18,6 +18,12 @@ class CreateArnoldRop(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): import hou + # Transfer settings from pre create to instance + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + for key in ["render_target"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] # Remove the active, we are checking the bypass flag of the nodes instance_data.pop("active", None) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index 5d56150df9..a3a557791e 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -16,6 +16,12 @@ class CreateKarmaROP(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): import hou # noqa + # Transfer settings from pre create to instance + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + for key in ["render_target"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "karma"}) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index 6705621f58..1b177563bc 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -16,6 +16,12 @@ class CreateMantraROP(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): import hou # noqa + # Transfer settings from pre create to instance + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + for key in ["render_target"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "ifd"}) 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 02c3ed2fc0..942d321b92 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 @@ -21,6 +21,12 @@ class CreateRedshiftROP(plugin.HoudiniCreator): render_target = "farm_split" def create(self, product_name, instance_data, pre_create_data): + # Transfer settings from pre create to instance + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + for key in ["render_target"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "Redshift_ROP"}) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index 147a34191f..ad181e4f89 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -20,6 +20,12 @@ class CreateVrayROP(plugin.HoudiniCreator): render_target = "farm_split" def create(self, product_name, instance_data, pre_create_data): + # Transfer settings from pre create to instance + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + for key in ["render_target"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "vray_renderer"}) From 76e4b77845bb34737840680d0e5a15ff56733e30 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 9 Apr 2024 20:25:41 +0200 Subject: [PATCH 024/192] transfer all precreate settings to instance --- .../hosts/houdini/plugins/create/create_arnold_rop.py | 4 +--- .../hosts/houdini/plugins/create/create_karma_rop.py | 4 +--- .../hosts/houdini/plugins/create/create_mantra_rop.py | 4 +--- .../hosts/houdini/plugins/create/create_redshift_rop.py | 4 +--- .../ayon_core/hosts/houdini/plugins/create/create_vray_rop.py | 4 +--- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index 08ed1bc91a..0e25523123 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -21,9 +21,7 @@ class CreateArnoldRop(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - for key in ["render_target"]: - if key in pre_create_data: - creator_attributes[key] = pre_create_data[key] + creator_attributes.update(pre_create_data) # Remove the active, we are checking the bypass flag of the nodes instance_data.pop("active", None) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index a3a557791e..4ddf7af376 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -19,9 +19,7 @@ class CreateKarmaROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - for key in ["render_target"]: - if key in pre_create_data: - creator_attributes[key] = pre_create_data[key] + creator_attributes.update(pre_create_data) instance_data.pop("active", None) instance_data.update({"node_type": "karma"}) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index 1b177563bc..7d481d0dbf 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -19,9 +19,7 @@ class CreateMantraROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - for key in ["render_target"]: - if key in pre_create_data: - creator_attributes[key] = pre_create_data[key] + creator_attributes.update(pre_create_data) instance_data.pop("active", None) instance_data.update({"node_type": "ifd"}) 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 942d321b92..dd5325c23c 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 @@ -24,9 +24,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - for key in ["render_target"]: - if key in pre_create_data: - creator_attributes[key] = pre_create_data[key] + creator_attributes.update(pre_create_data) instance_data.pop("active", None) instance_data.update({"node_type": "Redshift_ROP"}) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index ad181e4f89..5587f0151b 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -23,9 +23,7 @@ class CreateVrayROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - for key in ["render_target"]: - if key in pre_create_data: - creator_attributes[key] = pre_create_data[key] + creator_attributes.update(pre_create_data) instance_data.pop("active", None) instance_data.update({"node_type": "vray_renderer"}) From 90e2c1f1b5235e0d6fd408588a74f69f9cb7ac51 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 9 Apr 2024 21:58:31 +0200 Subject: [PATCH 025/192] use get_product_name instead of hardcoded productname --- .../publish/collect_local_render_instances.py | 36 +++++++------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index e221990f2b..4622f2d9cd 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -1,5 +1,6 @@ import os import pyblish.api +from ayon_core.pipeline.create import get_product_name class CollectLocalRenderInstances(pyblish.api.InstancePlugin): @@ -28,31 +29,24 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): # Create Instance for each AOV. context = instance.context + self.log.debug(instance.data["expectedFiles"]) expectedFiles = next(iter(instance.data["expectedFiles"]), {}) product_type = "render" # is always render - product_group = "render{Task}{productName}".format( - Task=self._capitalize(instance.data["task"]), - productName=self._capitalize(instance.data["productName"]) - ) # is always the group + product_group = get_product_name( + context.data["projectName"], + context.data["taskEntity"]["name"], + context.data["taskEntity"]["taskType"], + context.data["hostName"], + product_type, + instance.data["productName"] + ) for aov_name, aov_filepaths in expectedFiles.items(): - # Some AOV instance data - # label = "{productName}_{AOV}".format( - # AOV=aov_name, - # productName=instance.data["productName"] - # ) - name_template = "render{Task}{productName}_{AOV}" - if not aov_name: - # This is done to remove the trailing `_` - # if aov name is an empty string. - name_template = "render{Task}{productName}" + product_name = product_group - product_name = name_template.format( - Task=self._capitalize(instance.data["task"]), - productName=self._capitalize(instance.data["productName"]), - AOV=aov_name - ) + if aov_name: + product_name = "{}_{}".format(product_name, aov_name) # Create instance for each AOV aov_instance = context.create_instance(product_name) @@ -96,7 +90,3 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): # Remove Mantra instance # I can't remove it here as I still need it to trigger the render. # context.remove(instance) - - @staticmethod - def _capitalize(word): - return word[:1].upper() + word[1:] From 95757c6b68776884d481f03e2890b9c0a2ee8107 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 9 Apr 2024 22:16:53 +0200 Subject: [PATCH 026/192] add doc string to _get_camera_path --- .../houdini/plugins/publish/collect_review_data.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py index 7714ed0954..ed2de785a2 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py @@ -59,6 +59,18 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin): burnin_members["focalLength"] = focal_length def _get_camera_path(self, ropnode): + """Get the camera path associated with the given rop node. + + This function evaluates the camera parameter according to the + type of the given rop node. + + Returns: + Union[str, None]: Camera path or None. + + This function can return empty string if the camera + path is empty i.e. no camera path. + """ + if ropnode.type().name() in { "opengl", "karma", "ifd", "arnold" }: @@ -70,4 +82,4 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin): elif ropnode.type().name() == "vray_renderer": return ropnode.parm("render_camera").eval() - return "" + return None From 75879f54be31e293cad18a1c4a8a60005d62ba70 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 9 Apr 2024 22:28:53 +0200 Subject: [PATCH 027/192] revert changes --- .../deadline/plugins/publish/collect_pools.py | 4 ++-- .../publish/submit_houdini_render_deadline.py | 21 ++++++------------- .../plugins/publish/submit_publish_job.py | 8 +++---- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py index 6b7449b8f8..6923c2b16b 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py @@ -41,10 +41,10 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, "renderlayer", "maxrender", "usdrender", - "mantra_rop", - "karma_rop", "redshift_rop", "arnold_rop", + "mantra_rop", + "karma_rop", "vray_rop", "publish.hou"] diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index b562e2848b..64a7423e8d 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -71,12 +71,11 @@ class HoudiniSubmitDeadline( order = pyblish.api.IntegratorOrder hosts = ["houdini"] families = ["usdrender", - "mantra_rop", - "karma_rop", "redshift_rop", "arnold_rop", + "mantra_rop", + "karma_rop", "vray_rop"] - targets = ["local"] use_published = True @@ -266,23 +265,20 @@ class HoudiniSubmitDeadline( # Output driver to render if job_type == "render": product_type = instance.data.get("productType") - rop_node = hou.node(instance.data.get("instance_node")) - node_type = rop_node.type().name() - - if node_type == "arnold": + if product_type == "arnold_rop": plugin_info = ArnoldRenderDeadlinePluginInfo( InputFile=instance.data["ifdFile"] ) - elif node_type == "ifd": + elif product_type == "mantra_rop": plugin_info = MantraRenderDeadlinePluginInfo( SceneFile=instance.data["ifdFile"], Version=hou_major_minor, ) - elif node_type == "vray_renderer": + elif product_type == "vray_rop": plugin_info = VrayRenderPluginInfo( InputFilename=instance.data["ifdFile"], ) - elif node_type == "Redshift_ROP": + elif product_type == "redshift_rop": plugin_info = RedshiftRenderPluginInfo( SceneFile=instance.data["ifdFile"] ) @@ -319,11 +315,6 @@ class HoudiniSubmitDeadline( return attr.asdict(plugin_info) def process(self, instance): - if not instance.data["farm"]: - self.log.debug("Render on farm is disabled. " - "Skipping deadline submission.") - return - super(HoudiniSubmitDeadline, self).process(instance) # TODO: Avoid the need for this logic here, needed for submit publish diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 87693522c3..8def9cc63c 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -92,11 +92,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, "prerender.farm", "prerender.frames_farm", "renderlayer", "imagesequence", "vrayscene", "maxrender", - "mantra_rop", - "karma_rop", - "redshift_rop", - "arnold_rop", - "vray_rop"] + "arnold_rop", "mantra_rop", + "karma_rop", "vray_rop", + "redshift_rop"] aov_filter = [ { From 6151ff57e2dc9f5574cb3ddbb8685d4ec69752f0 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 9 Apr 2024 22:30:01 +0200 Subject: [PATCH 028/192] skip submission if farm is disabled --- .../plugins/publish/submit_houdini_render_deadline.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 64a7423e8d..4c517d7848 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -315,6 +315,11 @@ class HoudiniSubmitDeadline( return attr.asdict(plugin_info) def process(self, instance): + if not instance.data["farm"]: + self.log.debug("Render on farm is disabled. " + "Skipping deadline submission.") + return + super(HoudiniSubmitDeadline, self).process(instance) # TODO: Avoid the need for this logic here, needed for submit publish From c49c9016bfdafa27a67f7693b72d0a340a909fa1 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 9 Apr 2024 22:35:06 +0200 Subject: [PATCH 029/192] add a TODO about enhancing code readability --- .../hosts/houdini/plugins/publish/extract_local_render.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py index cf94019947..3f332acc55 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py @@ -49,7 +49,8 @@ class ExtractLocalRender(publish.Extractor): # Check missing frames. # Frames won't exist if user cancels the render. expected_files = next(iter(instance.data["expectedFiles"]), {}) - expected_files = sum(expected_files.values(), []) + # TODO: enhance the readability. + expected_files = sum(expected_files.values(), []) missing_frames = [ frame for frame in expected_files From 3b6c3bb5e5fe61361dd2774b1aa07f9a80a0384b Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 9 Apr 2024 22:49:34 +0200 Subject: [PATCH 030/192] remove redundant attr_defs --- .../hosts/houdini/plugins/create/create_arnold_rop.py | 3 +-- .../ayon_core/hosts/houdini/plugins/create/create_karma_rop.py | 3 +-- .../hosts/houdini/plugins/create/create_mantra_rop.py | 3 +-- .../hosts/houdini/plugins/create/create_redshift_rop.py | 3 +-- .../ayon_core/hosts/houdini/plugins/create/create_vray_rop.py | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index 0e25523123..0965ee59ca 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -92,6 +92,5 @@ class CreateArnoldRop(plugin.HoudiniCreator): ] def get_pre_create_attr_defs(self): - attrs = super(CreateArnoldRop, self).get_pre_create_attr_defs() - return attrs + self.get_instance_attr_defs() + return self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index 4ddf7af376..c795512469 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -126,6 +126,5 @@ class CreateKarmaROP(plugin.HoudiniCreator): def get_pre_create_attr_defs(self): - attrs = super(CreateKarmaROP, self).get_pre_create_attr_defs() - return attrs + self.get_instance_attr_defs() + return self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index 7d481d0dbf..d0fc79f608 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -108,6 +108,5 @@ class CreateMantraROP(plugin.HoudiniCreator): ] def get_pre_create_attr_defs(self): - attrs = super(CreateMantraROP, self).get_pre_create_attr_defs() - return attrs + self.get_instance_attr_defs() + return self.get_instance_attr_defs() 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 dd5325c23c..0094269f47 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 @@ -151,6 +151,5 @@ class CreateRedshiftROP(plugin.HoudiniCreator): ] def get_pre_create_attr_defs(self): - attrs = super(CreateRedshiftROP, self).get_pre_create_attr_defs() - return attrs + self.get_instance_attr_defs() + return self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index 5587f0151b..8c4084cf9f 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -179,6 +179,5 @@ class CreateVrayROP(plugin.HoudiniCreator): ] def get_pre_create_attr_defs(self): - attrs = super(CreateVrayROP, self).get_pre_create_attr_defs() - return attrs + self.get_instance_attr_defs() + return self.get_instance_attr_defs() From a2b73014da7266280f65bc9c800ae264883da78e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 10 Apr 2024 11:14:08 +0200 Subject: [PATCH 031/192] mark known bare except handling with noqa --- client/ayon_core/hosts/blender/api/lib.py | 6 +++--- .../hosts/photoshop/plugins/create/create_image.py | 2 +- client/ayon_core/pipeline/create/context.py | 12 ++++++------ client/ayon_core/tools/adobe_webserver/app.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/lib.py b/client/ayon_core/hosts/blender/api/lib.py index 458a275b51..32137f0fcd 100644 --- a/client/ayon_core/hosts/blender/api/lib.py +++ b/client/ayon_core/hosts/blender/api/lib.py @@ -33,7 +33,7 @@ def load_scripts(paths): if register: try: register() - except: + except: # noqa E722 traceback.print_exc() else: print("\nWarning! '%s' has no register function, " @@ -45,7 +45,7 @@ def load_scripts(paths): if unregister: try: unregister() - except: + except: # noqa E722 traceback.print_exc() def test_reload(mod): @@ -57,7 +57,7 @@ def load_scripts(paths): try: return importlib.reload(mod) - except: + except: # noqa E722 traceback.print_exc() def test_register(mod): diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index 26f2469844..97543e96de 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -53,7 +53,7 @@ class ImageCreator(Creator): stub.select_layers(stub.get_layers()) try: group = stub.group_selected_layers(product_name_from_ui) - except: + except: # noqa E722 raise CreatorError("Cannot group locked Background layer!") groups_to_create.append(group) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index ca9896fb3f..c223b52d03 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -2053,7 +2053,7 @@ class CreateContext: exc_info = sys.exc_info() self.log.warning(error_message.format(identifier, exc_info[1])) - except: + except: # noqa: E722 add_traceback = True exc_info = sys.exc_info() self.log.warning( @@ -2163,7 +2163,7 @@ class CreateContext: exc_info = sys.exc_info() self.log.warning(error_message.format(identifier, exc_info[1])) - except: + except: # noqa: E722 failed = True add_traceback = True exc_info = sys.exc_info() @@ -2197,7 +2197,7 @@ class CreateContext: try: convertor.find_instances() - except: + except: # noqa: E722 failed_info.append( prepare_failed_convertor_operation_info( convertor.identifier, sys.exc_info() @@ -2373,7 +2373,7 @@ class CreateContext: exc_info = sys.exc_info() self.log.warning(error_message.format(identifier, exc_info[1])) - except: + except: # noqa: E722 failed = True add_traceback = True exc_info = sys.exc_info() @@ -2440,7 +2440,7 @@ class CreateContext: error_message.format(identifier, exc_info[1]) ) - except: + except: # noqa: E722 failed = True add_traceback = True exc_info = sys.exc_info() @@ -2546,7 +2546,7 @@ class CreateContext: try: self.run_convertor(convertor_identifier) - except: + except: # noqa: E722 failed_info.append( prepare_failed_convertor_operation_info( convertor_identifier, sys.exc_info() diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index 7d97d7d66d..819cfa8084 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -109,7 +109,7 @@ class WebServerTool: try: sock.bind((host_name, port)) result = False - except: + except: # noqa E722 print("Port is in use") return result From 78a895b720fda253a37725ce30814ca85abafdb9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:37:11 +0200 Subject: [PATCH 032/192] change port check logic --- client/ayon_core/tools/adobe_webserver/app.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index 819cfa8084..6ea9745c59 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -104,14 +104,11 @@ class WebServerTool: again. In that case, use existing running webserver. Check here is easier than capturing exception from thread. """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = True - try: - sock.bind((host_name, port)) - result = False - except: # noqa E722 - print("Port is in use") + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as con: + result = con.connect_ex((host_name, port)) == 0 + if result: + print("Port is in use") return result def call(self, func): From e587ef53440e18649cce4642517705d538011cf2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:03:34 +0200 Subject: [PATCH 033/192] log which port is in use Co-authored-by: Roy Nieterau --- client/ayon_core/tools/adobe_webserver/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index 6ea9745c59..26bf638c91 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -108,7 +108,7 @@ class WebServerTool: result = con.connect_ex((host_name, port)) == 0 if result: - print("Port is in use") + print(f"Port {port} is already in use") return result def call(self, func): From bedebd8f8e871665d6b117f5c13c8a20a63ad24a Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 15 Apr 2024 12:28:31 +0200 Subject: [PATCH 034/192] add 'Mark as reviewable' todo --- .../plugins/publish/collect_local_render_instances.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 4622f2d9cd..f3ad5862a6 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -63,6 +63,8 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): if len(aov_filenames) == 1: aov_filenames = aov_filenames[0] + # TODO: Add some option to allow users to mark + # aov_instances as reviewable. aov_instance.data.update({ # 'label': label, "task": instance.data["task"], @@ -72,14 +74,14 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): "productType": product_type, "productName": product_name, "productGroup": product_group, - "families": ["render.local.hou", "review"], + "families": ["render.local.hou"], "instance_node": instance.data["instance_node"], "representations": [ { "stagingDir": staging_dir, "ext": ext, "name": ext, - "tags": ["review"], + "tags": [], "files": aov_filenames, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"] From 16c8c859b32c186559e526cbb941842e1fb0b972 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 15 Apr 2024 12:29:58 +0200 Subject: [PATCH 035/192] update a comment --- .../houdini/plugins/publish/collect_local_render_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index f3ad5862a6..ae98e6ed87 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -89,6 +89,6 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): ] }) - # Remove Mantra instance + # Remove original render instance # I can't remove it here as I still need it to trigger the render. # context.remove(instance) From 6ef55adaf044a2c3ece228688fdae2cafcb7e5ec Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 15 Apr 2024 21:01:19 +0200 Subject: [PATCH 036/192] add missing key --- .../houdini/plugins/publish/collect_local_render_instances.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index ae98e6ed87..ea1eeb62af 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -72,6 +72,7 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"], "productType": product_type, + "family": product_type, "productName": product_name, "productGroup": product_group, "families": ["render.local.hou"], From 409c243516c498164cd115de8daea4cdf72d5206 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 15 Apr 2024 21:47:13 +0200 Subject: [PATCH 037/192] update node parameter in the extractor instead of the collector --- .../plugins/publish/collect_arnold_rop.py | 4 +- .../plugins/publish/collect_farm_instances.py | 26 +------ .../plugins/publish/collect_mantra_rop.py | 4 +- .../plugins/publish/collect_redshift_rop.py | 4 +- .../plugins/publish/collect_vray_rop.py | 4 +- .../plugins/publish/extract_local_render.py | 63 ----------------- .../houdini/plugins/publish/extract_render.py | 67 +++++++++++++++++++ 7 files changed, 74 insertions(+), 98 deletions(-) delete mode 100644 client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py create mode 100644 client/ayon_core/hosts/houdini/plugins/publish/extract_render.py diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py index 7fe38555a3..c373d94653 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py @@ -41,11 +41,9 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin): render_products = [] # Store whether we are splitting the render job (export + render) - split_render = bool(rop.parm("ar_ass_export_enable").eval()) - instance.data["splitRender"] = split_render export_prefix = None export_products = [] - if split_render: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "ar_ass_file", pad_character="0" ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index 391afe7387..c5a982996b 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -16,31 +16,8 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): label = "Collect farm instances" def process(self, instance): - import hou creator_attribute = instance.data["creator_attributes"] - product_type = instance.data["productType"] - rop_node = hou.node(instance.data.get("instance_node")) - - # Align split parameter value on rop node to the render target. - if creator_attribute.get("render_target") == "farm_split": - if product_type == "arnold_rop": - rop_node.setParms({"ar_ass_export_enable": 1}) - elif product_type == "mantra_rop": - rop_node.setParms({"soho_outputmode": 1}) - elif product_type == "redshift_rop": - rop_node.setParms({"RS_archive_enable": 1}) - elif product_type == "vray_rop": - rop_node.setParms({"render_export_mode": "2"}) - else: - if product_type == "arnold_rop": - rop_node.setParms({"ar_ass_export_enable": 0}) - elif product_type == "mantra_rop": - rop_node.setParms({"soho_outputmode": 0}) - elif product_type == "redshift_rop": - rop_node.setParms({"RS_archive_enable": 0}) - elif product_type == "vray_rop": - rop_node.setParms({"render_export_mode": "1"}) # Collect Render Target if creator_attribute.get("render_target") not in { @@ -52,3 +29,6 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): return instance.data["farm"] = True + instance.data["splitRender"] = ( + creator_attribute.get("render_target") == "farm_split" + ) 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 df9acc4b61..9894e2beda 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 @@ -45,11 +45,9 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): 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: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "soho_diskfile", pad_character="0" ) 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 191a9c1ebc..bd01f929c3 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 @@ -43,10 +43,8 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): default_prefix = evalParmNoFrame(rop, "RS_outputFileNamePrefix") beauty_suffix = rop.evalParm("RS_outputBeautyAOVSuffix") # Store whether we are splitting the render job (export + render) - split_render = bool(rop.parm("RS_archive_enable").eval()) - instance.data["splitRender"] = split_render export_products = [] - if split_render: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "RS_archive_file", pad_character="0" ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py index 62b7dcdd5d..63e16d541d 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py @@ -46,11 +46,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): # TODO: add render elements if render element # Store whether we are splitting the render job in an export + render - split_render = rop.parm("render_export_mode").eval() == "2" - instance.data["splitRender"] = split_render export_prefix = None export_products = [] - if split_render: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "render_export_filepath", pad_character="0" ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py deleted file mode 100644 index 3f332acc55..0000000000 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_local_render.py +++ /dev/null @@ -1,63 +0,0 @@ -import pyblish.api - -from ayon_core.pipeline import publish -from ayon_core.hosts.houdini.api.lib import render_rop -import hou -import os - - -class ExtractLocalRender(publish.Extractor): - - order = pyblish.api.ExtractorOrder - label = "Extract Local Render" - hosts = ["houdini"] - families = ["mantra_rop", - "karma_rop", - "redshift_rop", - "arnold_rop", - "vray_rop"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - - creator_attribute = instance.data["creator_attributes"] - - if creator_attribute.get("render_target") == "local_no_render": - self.log.debug("Skip render is enabled, skipping rendering.") - return - - # Make sure split parameter is turned off. - # Otherwise, render nodes will generate intermediate - # render files instead of render. - product_type = instance.data["productType"] - rop_node = hou.node(instance.data.get("instance_node")) - - if product_type == "arnold_rop": - rop_node.setParms({"ar_ass_export_enable": 0}) - elif product_type == "mantra_rop": - rop_node.setParms({"soho_outputmode": 0}) - elif product_type == "redshift_rop": - rop_node.setParms({"RS_archive_enable": 0}) - elif product_type == "vray_rop": - rop_node.setParms({"render_export_mode": "1"}) - - ropnode = hou.node(instance.data.get("instance_node")) - render_rop(ropnode) - - # Check missing frames. - # Frames won't exist if user cancels the render. - expected_files = next(iter(instance.data["expectedFiles"]), {}) - # TODO: enhance the readability. - expected_files = sum(expected_files.values(), []) - missing_frames = [ - frame - for frame in expected_files - if not os.path.exists(frame) - ] - if missing_frames: - # TODO: Use user friendly error reporting. - raise RuntimeError("Failed to complete render extraction. " - "Missing output files: {}".format( - missing_frames)) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py new file mode 100644 index 0000000000..7ea276a94d --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py @@ -0,0 +1,67 @@ +import pyblish.api + +from ayon_core.pipeline import publish +from ayon_core.hosts.houdini.api.lib import render_rop +import hou +import os + + +class ExtractRender(publish.Extractor): + + order = pyblish.api.ExtractorOrder + label = "Extract Render" + hosts = ["houdini"] + families = ["mantra_rop", + "karma_rop", + "redshift_rop", + "arnold_rop", + "vray_rop"] + + def process(self, instance): + creator_attribute = instance.data["creator_attributes"] + product_type = instance.data["productType"] + rop_node = hou.node(instance.data.get("instance_node")) + + # Align split parameter value on rop node to the render target. + if creator_attribute.get("render_target") == "farm_split": + if product_type == "arnold_rop": + rop_node.setParms({"ar_ass_export_enable": 1}) + elif product_type == "mantra_rop": + rop_node.setParms({"soho_outputmode": 1}) + elif product_type == "redshift_rop": + rop_node.setParms({"RS_archive_enable": 1}) + elif product_type == "vray_rop": + rop_node.setParms({"render_export_mode": "2"}) + else: + if product_type == "arnold_rop": + rop_node.setParms({"ar_ass_export_enable": 0}) + elif product_type == "mantra_rop": + rop_node.setParms({"soho_outputmode": 0}) + elif product_type == "redshift_rop": + rop_node.setParms({"RS_archive_enable": 0}) + elif product_type == "vray_rop": + rop_node.setParms({"render_export_mode": "1"}) + + if instance.data.get("farm"): + self.log.debug("Render should be processed on farm, skipping local render.") + return + + if creator_attribute.get("render_target") == "local": + ropnode = hou.node(instance.data.get("instance_node")) + render_rop(ropnode) + + # Check missing frames. + # Frames won't exist if user cancels the render. + expected_files = next(iter(instance.data["expectedFiles"]), {}) + # TODO: enhance the readability. + expected_files = sum(expected_files.values(), []) + missing_frames = [ + frame + for frame in expected_files + if not os.path.exists(frame) + ] + if missing_frames: + # TODO: Use user friendly error reporting. + raise RuntimeError("Failed to complete render extraction. " + "Missing output files: {}".format( + missing_frames)) From ca3f3910232fc4077574c8272b222c9014ecd2fe Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 16 Apr 2024 17:55:59 +0200 Subject: [PATCH 038/192] add review support on farm render --- .../plugins/create/create_arnold_rop.py | 6 ++++- .../plugins/create/create_karma_rop.py | 4 ++++ .../plugins/create/create_mantra_rop.py | 4 ++++ .../plugins/create/create_redshift_rop.py | 6 ++++- .../houdini/plugins/create/create_vray_rop.py | 4 ++++ .../plugins/publish/collect_arnold_rop.py | 11 ++++++++++ .../plugins/publish/collect_karma_rop.py | 6 +++++ .../plugins/publish/collect_mantra_rop.py | 10 +++++++++ .../plugins/publish/collect_redshift_rop.py | 11 ++++++++++ .../publish/collect_reviewable_instances.py | 22 +++++++++++++++++++ .../plugins/publish/collect_vray_rop.py | 10 +++++++++ 11 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 client/ayon_core/hosts/houdini/plugins/publish/collect_reviewable_instances.py diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index 0f2fc89764..d3254a28dd 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -1,5 +1,5 @@ from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import EnumDef +from ayon_core.lib import EnumDef, BoolDef class CreateArnoldRop(plugin.HoudiniCreator): @@ -81,6 +81,10 @@ class CreateArnoldRop(plugin.HoudiniCreator): } return [ + BoolDef("review", + label="Review", + tooltip="Mark as reviewable", + default=True), EnumDef("render_target", items=render_target_items, label="Render target", diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index c795512469..0af2fe8aeb 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -103,6 +103,10 @@ class CreateKarmaROP(plugin.HoudiniCreator): } return [ + BoolDef("review", + label="Review", + tooltip="Mark as reviewable", + default=True), EnumDef("render_target", items=render_target_items, label="Render target", diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index d0fc79f608..eac7f06b90 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -92,6 +92,10 @@ class CreateMantraROP(plugin.HoudiniCreator): } return [ + BoolDef("review", + label="Review", + tooltip="Mark as reviewable", + default=True), EnumDef("render_target", items=render_target_items, label="Render target", 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 0094269f47..2a87d2b35c 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 @@ -4,7 +4,7 @@ import hou # noqa from ayon_core.pipeline import CreatorError from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import EnumDef +from ayon_core.lib import EnumDef, BoolDef class CreateRedshiftROP(plugin.HoudiniCreator): @@ -136,6 +136,10 @@ class CreateRedshiftROP(plugin.HoudiniCreator): } return [ + BoolDef("review", + label="Review", + tooltip="Mark as reviewable", + default=True), EnumDef("render_target", items=render_target_items, label="Render target", diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index 8788af4748..cdaee7db06 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -158,6 +158,10 @@ class CreateVrayROP(plugin.HoudiniCreator): } return [ + BoolDef("review", + label="Review", + tooltip="Mark as reviewable", + default=True), EnumDef("render_target", items=render_target_items, label="Render target", diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py index c373d94653..fa9a1eea0f 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py @@ -66,6 +66,9 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin): "": self.generate_expected_files(instance, beauty_product) } + # Assume it's a multipartExr Render. + multipartExr = True + num_aovs = rop.evalParm("ar_aovs") for index in range(1, num_aovs + 1): # Skip disabled AOVs @@ -83,6 +86,14 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin): files_by_aov[label] = self.generate_expected_files(instance, aov_product) + # Set to False as soon as we have a separated aov. + multipartExr = False + + # Review Logic expects this key to exist and be True + # if render is a multipart Exr. + # As long as we have one AOV then multipartExr should be True. + instance.data["multipartExr"] = multipartExr + for product in render_products: self.log.debug("Found render product: {}".format(product)) 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 78651b0c69..662ed7ae30 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 @@ -55,6 +55,12 @@ class CollectKarmaROPRenderProducts(pyblish.api.InstancePlugin): beauty_product) } + # Review Logic expects this key to exist and be True + # if render is a multipart Exr. + # As long as we have one AOV then multipartExr should be True. + # By default karma render is a multipart Exr. + instance.data["multipartExr"] = True + filenames = list(render_products) instance.data["files"] = filenames instance.data["renderProducts"] = colorspace.ARenderProduct() 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 9894e2beda..e85751c08a 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 @@ -72,6 +72,8 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): beauty_product) } + # Assume it's a multipartExr Render. + multipartExr = True aov_numbers = rop.evalParm("vm_numaux") if aov_numbers > 0: # get the filenames of the AOVs @@ -83,6 +85,9 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): aov_enabled = rop.evalParm(aov_boolean) has_aov_path = rop.evalParm(aov_name) if has_aov_path and aov_enabled == 1: + # Set to False as soon as we have a separated aov. + multipartExr = False + aov_prefix = evalParmNoFrame(rop, aov_name) aov_product = self.get_render_product_name( prefix=aov_prefix, suffix=None @@ -91,6 +96,11 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): files_by_aov[var] = self.generate_expected_files(instance, aov_product) # noqa + # Review Logic expects this key to exist and be True + # if render is a multipart Exr. + # As long as we have one AOV then multipartExr should be True. + instance.data["multipartExr"] = multipartExr + for product in render_products: self.log.debug("Found render product: %s" % product) 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 bd01f929c3..aff9269fa5 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 @@ -80,6 +80,9 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): "RS_aovMultipart": True }) + # Assume it's a multipartExr Render. + multipartExr = True + # Default beauty/main layer AOV beauty_product = self.get_render_product_name( prefix=default_prefix, suffix=beauty_suffix @@ -119,6 +122,14 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): files_by_aov[aov_suffix] = self.generate_expected_files(instance, aov_product) # noqa + # Set to False as soon as we have a separated aov. + multipartExr = False + + # Review Logic expects this key to exist and be True + # if render is a multipart Exr. + # As long as we have one AOV then multipartExr should be True. + instance.data["multipartExr"] = multipartExr + for product in render_products: self.log.debug("Found render product: %s" % product) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_reviewable_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_reviewable_instances.py new file mode 100644 index 0000000000..78dc5fe11a --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_reviewable_instances.py @@ -0,0 +1,22 @@ +import pyblish.api + + +class CollectReviewableInstances(pyblish.api.InstancePlugin): + """Collect Reviewable Instances. + + Basically, all instances of the specified families + with creator_attribure["review"] + """ + + order = pyblish.api.CollectorOrder + label = "Collect Reviewable Instances" + families = ["mantra_rop", + "karma_rop", + "redshift_rop", + "arnold_rop", + "vray_rop"] + + def process(self, instance): + creator_attribute = instance.data["creator_attributes"] + + instance.data["review"] = creator_attribute.get("review", False) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py index 63e16d541d..2eb5e3164a 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py @@ -68,6 +68,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): "": self.generate_expected_files(instance, beauty_product)} + # Assume it's a multipartExr Render. + multipartExr = True + if instance.data.get("RenderElement", True): render_element = self.get_render_element_name(rop, default_prefix) if render_element: @@ -76,6 +79,13 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): files_by_aov[aov] = self.generate_expected_files( instance, renderpass) + # Set to False as soon as we have a separated aov. + multipartExr = False + + # Review Logic expects this key to exist and be True + # if render is a multipart Exr. + # As long as we have one AOV then multipartExr should be True. + instance.data["multipartExr"] = multipartExr for product in render_products: self.log.debug("Found render product: %s" % product) From 1ddf28c752f03752c22066a4714e752bcb5379c7 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 16 Apr 2024 18:18:52 +0200 Subject: [PATCH 039/192] add missing key --- .../hosts/houdini/plugins/publish/collect_farm_instances.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py index c5a982996b..586aa2da57 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_farm_instances.py @@ -24,6 +24,7 @@ class CollectFarmInstances(pyblish.api.InstancePlugin): "farm_split", "farm" }: instance.data["farm"] = False + instance.data["splitRender"] = False self.log.debug("Render on farm is disabled. " "Skipping farm collecting.") return From 205fc0ed21f6a2623d2cc38fc82b7dc1c7a3a216 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 16 Apr 2024 18:19:19 +0200 Subject: [PATCH 040/192] add review support on local render --- .../publish/collect_local_render_instances.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index ea1eeb62af..5918366f06 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -63,8 +63,18 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): if len(aov_filenames) == 1: aov_filenames = aov_filenames[0] - # TODO: Add some option to allow users to mark - # aov_instances as reviewable. + preview = False + if instance.data.get("multipartExr", False): + self.log.debug( + "Adding preview tag because its multipartExr" + ) + preview = True + else: + # TODO: set Preview to True if aov_name matched some regex. + # Also, I'm not sure where that regex is defined. + pass + + preview = preview and instance.data.get("review", False) aov_instance.data.update({ # 'label': label, "task": instance.data["task"], @@ -75,14 +85,14 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): "family": product_type, "productName": product_name, "productGroup": product_group, - "families": ["render.local.hou"], + "families": ["render.local.hou", "review"], "instance_node": instance.data["instance_node"], "representations": [ { "stagingDir": staging_dir, "ext": ext, "name": ext, - "tags": [], + "tags": ["review"] if preview else [], "files": aov_filenames, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"] From d7d91b62a9d91e72dde457ed0503692e37fb0445 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 17 Apr 2024 15:18:00 +0200 Subject: [PATCH 041/192] add settings for CollectLocalRenderInstances --- .../houdini/server/settings/publish.py | 36 ++++++++++++++++++- server_addon/houdini/server/version.py | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/server/settings/publish.py b/server_addon/houdini/server/settings/publish.py index 8e0e7f7795..0912ecd997 100644 --- a/server_addon/houdini/server/settings/publish.py +++ b/server_addon/houdini/server/settings/publish.py @@ -1,4 +1,7 @@ -from ayon_server.settings import BaseSettingsModel, SettingsField +from ayon_server.settings import ( + BaseSettingsModel, + SettingsField +) # Publish Plugins @@ -20,6 +23,25 @@ class CollectChunkSizeModel(BaseSettingsModel): title="Frames Per Task") +class AOVFilterSubmodel(BaseSettingsModel): + value: list[str] = SettingsField( + default_factory=list, + title="AOV regex" + ) + +class CollectLocalRenderInstancesModel(BaseSettingsModel): + + override_deadline_aov_filter: bool = SettingsField( + False, + title="Override Deadline AOV Filter" + ) + + aov_filter: AOVFilterSubmodel = SettingsField( + default_factory=AOVFilterSubmodel, + title="Reviewable products filter" + ) + + class ValidateWorkfilePathsModel(BaseSettingsModel): enabled: bool = SettingsField(title="Enabled") optional: bool = SettingsField(title="Optional") @@ -49,6 +71,10 @@ class PublishPluginsModel(BaseSettingsModel): default_factory=CollectChunkSizeModel, title="Collect Chunk Size." ) + CollectLocalRenderInstances: CollectLocalRenderInstancesModel = SettingsField( + default_factory=CollectLocalRenderInstancesModel, + title="Collect Local Render Instances." + ) ValidateContainers: BasicValidateModel = SettingsField( default_factory=BasicValidateModel, title="Validate Latest Containers.", @@ -82,6 +108,14 @@ DEFAULT_HOUDINI_PUBLISH_SETTINGS = { "optional": True, "chunk_size": 999999 }, + "CollectLocalRenderInstances": { + "override_deadline_aov_filter": False, + "aov_filter" : { + "value": [ + ".*([Bb]eauty).*" + ] + } + }, "ValidateContainers": { "enabled": True, "optional": True, diff --git a/server_addon/houdini/server/version.py b/server_addon/houdini/server/version.py index b5c9b6cb71..11ef092868 100644 --- a/server_addon/houdini/server/version.py +++ b/server_addon/houdini/server/version.py @@ -1 +1 @@ -__version__ = "0.2.12" +__version__ = "0.2.13" From 885cfcb203d9874b5ec267c0d8bdd7b228194266 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 17 Apr 2024 16:24:20 +0200 Subject: [PATCH 042/192] apply aov_filter in Houdini local render --- .../publish/collect_local_render_instances.py | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 5918366f06..073053188c 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -1,6 +1,11 @@ import os import pyblish.api from ayon_core.pipeline.create import get_product_name +from ayon_core.pipeline.farm.patterning import match_aov_pattern +from ayon_core.pipeline.publish import ( + get_plugin_settings, + apply_plugin_settings_automatically +) class CollectLocalRenderInstances(pyblish.api.InstancePlugin): @@ -20,6 +25,32 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): hosts = ["houdini"] label = "Collect local render instances" + override_deadline_aov_filter = False + aov_filter = {} + + @classmethod + def apply_settings(cls, project_settings): + # Preserve automatic settings applying logic + settings = get_plugin_settings(plugin=cls, + project_settings=project_settings, + log=cls.log, + category="houdini") + apply_plugin_settings_automatically(cls, settings, logger=cls.log) + + if not cls.override_deadline_aov_filter: + # get aov_filter from collector settings + # and restructure it as match_aov_pattern requires. + cls.aov_filter = { + "houdini": cls.aov_filter["value"] + } + else: + # get aov_filter from deadline settings + cls.aov_filter = project_settings["deadline"]["publish"]["ProcessSubmittedJobOnFarm"]["aov_filter"] + cls.aov_filter = { + item["name"]: item["value"] + for item in cls.aov_filter + } + def process(self, instance): if instance.data["farm"]: @@ -29,7 +60,6 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): # Create Instance for each AOV. context = instance.context - self.log.debug(instance.data["expectedFiles"]) expectedFiles = next(iter(instance.data["expectedFiles"]), {}) product_type = "render" # is always render @@ -56,6 +86,19 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): staging_dir = os.path.dirname(aov_filepaths[0]) ext = aov_filepaths[0].split(".")[-1] + # Decide if instance is reviewable + preview = False + if instance.data.get("multipartExr", False): + # Add preview tag because its multipartExr. + preview = True + else: + # Add Preview tag if the AOV matches the filter. + preview = match_aov_pattern( + "houdini", self.aov_filter, aov_filenames[0] + ) + + preview = preview and instance.data.get("review", False) + # Support Single frame. # The integrator wants single files to be a single # filename instead of a list. @@ -63,18 +106,6 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): if len(aov_filenames) == 1: aov_filenames = aov_filenames[0] - preview = False - if instance.data.get("multipartExr", False): - self.log.debug( - "Adding preview tag because its multipartExr" - ) - preview = True - else: - # TODO: set Preview to True if aov_name matched some regex. - # Also, I'm not sure where that regex is defined. - pass - - preview = preview and instance.data.get("review", False) aov_instance.data.update({ # 'label': label, "task": instance.data["task"], From d094f83efbb7e2f2d4c158dafc3ba6cee93a914e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 18 Apr 2024 17:22:39 +0800 Subject: [PATCH 043/192] make sure the bake animation is boolean option --- client/ayon_core/hosts/maya/api/fbx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/fbx.py b/client/ayon_core/hosts/maya/api/fbx.py index 939da4011b..3f1395cb40 100644 --- a/client/ayon_core/hosts/maya/api/fbx.py +++ b/client/ayon_core/hosts/maya/api/fbx.py @@ -47,7 +47,7 @@ class FBXExtractor: "smoothMesh": bool, "instances": bool, # "referencedContainersContent": bool, # deprecated in Maya 2016+ - "bakeComplexAnimation": int, + "bakeComplexAnimation": bool, "bakeComplexStart": int, "bakeComplexEnd": int, "bakeComplexStep": int, From 60468e4d7410ff5021d771a09b1e2e16494e6380 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 19 Apr 2024 16:09:18 +0200 Subject: [PATCH 044/192] enhance the readability of checking missing frames in expectedFiles --- .../houdini/plugins/publish/extract_render.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py index 7ea276a94d..8a666541cb 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py @@ -50,14 +50,21 @@ class ExtractRender(publish.Extractor): ropnode = hou.node(instance.data.get("instance_node")) render_rop(ropnode) + # `ExpectedFiles` is a list that includes one dict. + expected_files = instance.data["expectedFiles"][0] + # Each key in that dict is a list of files. + # Combine lists of files into one big list. + all_frames = [] + for value in expected_files.values(): + if isinstance(value, str): + all_frames.append(value) + elif isinstance(value, list): + all_frames.extend(value) # Check missing frames. # Frames won't exist if user cancels the render. - expected_files = next(iter(instance.data["expectedFiles"]), {}) - # TODO: enhance the readability. - expected_files = sum(expected_files.values(), []) missing_frames = [ frame - for frame in expected_files + for frame in all_frames if not os.path.exists(frame) ] if missing_frames: From 8e87ef674daba9f3bb7ae6edc1fd89e617ee304e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 19 Apr 2024 20:32:36 +0200 Subject: [PATCH 045/192] Maya: Implement workfile template run script placeholder --- .../plugins/template/script_placeholder.py | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 client/ayon_core/hosts/maya/plugins/template/script_placeholder.py diff --git a/client/ayon_core/hosts/maya/plugins/template/script_placeholder.py b/client/ayon_core/hosts/maya/plugins/template/script_placeholder.py new file mode 100644 index 0000000000..893e2ec0cb --- /dev/null +++ b/client/ayon_core/hosts/maya/plugins/template/script_placeholder.py @@ -0,0 +1,201 @@ +from maya import cmds + +from ayon_core.hosts.maya.api.workfile_template_builder import ( + MayaPlaceholderPlugin +) +from ayon_core.lib import NumberDef, TextDef, EnumDef +from ayon_core.lib.events import weakref_partial + + +EXAMPLE_SCRIPT = """ +# Access maya commands +from maya import cmds + +# Access the placeholder node +placeholder_node = placeholder.scene_identifier + +# Access the event callback +if event is None: + print(f"Populating {placeholder}") +else: + if event.topic == "template.depth_processed": + print(f"Processed depth: {event.get('depth')}") + elif event.topic == "template.finished": + print("Build finished.") +""".strip() + + +class MayaPlaceholderScriptPlugin(MayaPlaceholderPlugin): + """Execute a script at the given `order` during workfile build. + + This is a very low-level placeholder to run Python scripts at a given + point in time during the workfile template build. + + It can create either a locator or an objectSet as placeholder node. + It defaults to an objectSet, since allowing to run on e.g. other + placeholder node members can be useful, e.g. using: + + >>> members = cmds.sets(placeholder.scene_identifier, query=True) + + """ + + identifier = "maya.runscript" + label = "Run Python Script" + + use_selection_as_parent = False + + def get_placeholder_options(self, options=None): + options = options or {} + return [ + NumberDef( + "order", + label="Order", + default=options.get("order") or 0, + decimals=0, + minimum=0, + maximum=999, + tooltip=( + "Order" + "\nOrder defines asset loading priority (0 to 999)" + "\nPriority rule is : \"lowest is first to load\"." + ) + ), + TextDef( + "prepare_script", + label="Run at\nprepare", + tooltip="Run before populate at prepare order", + multiline=True, + default=options.get("prepare_script", "") + ), + TextDef( + "populate_script", + label="Run at\npopulate", + tooltip="Run script at populate node order
" + "This is the default behavior", + multiline=True, + default=options.get("populate_script", EXAMPLE_SCRIPT) + ), + TextDef( + "depth_processed_script", + label="Run after\ndepth\niteration", + tooltip="Run script after every build depth iteration", + multiline=True, + default=options.get("depth_processed_script", "") + ), + TextDef( + "finished_script", + label="Run after\nbuild", + tooltip=( + "Run script at build finished.
" + "Note: this even runs if other placeholders had " + "errors during the build" + ), + multiline=True, + default=options.get("finished_script", "") + ), + EnumDef( + "create_nodetype", + label="Nodetype", + items={ + "spaceLocator": "Locator", + "objectSet": "ObjectSet" + }, + tooltip=( + "The placeholder's node type to be created.
" + "Note this only works on create, not on update" + ), + default=options.get("create_nodetype", "objectSet") + ), + ] + + def create_placeholder(self, placeholder_data): + nodetype = placeholder_data.get("create_nodetype", "objectSet") + + if nodetype == "spaceLocator": + super(MayaPlaceholderScriptPlugin, self).create_placeholder( + placeholder_data + ) + elif nodetype == "objectSet": + placeholder_data["plugin_identifier"] = self.identifier + + # Create maya objectSet on selection + selection = cmds.ls(selection=True, long=True) + name = self._create_placeholder_name(placeholder_data) + node = cmds.sets(selection, name=name) + + self.imprint(node, placeholder_data) + + def prepare_placeholders(self, placeholders): + super(MayaPlaceholderScriptPlugin, self).prepare_placeholders( + placeholders + ) + for placeholder in placeholders: + prepare_script = placeholder.data.get("prepare_script") + if not prepare_script: + continue + + self.run_script(placeholder, prepare_script) + + def populate_placeholder(self, placeholder): + + populate_script = placeholder.data.get("populate_script") + depth_script = placeholder.data.get("depth_processed_script") + finished_script = placeholder.data.get("finished_script") + + # Run now + if populate_script: + self.run_script(placeholder, populate_script) + + if not any([depth_script, finished_script]): + # No callback scripts to run + if not placeholder.data.get("keep_placeholder", True): + self.delete_placeholder(placeholder) + return + + # Run at each depth processed + if depth_script: + callback = weakref_partial( + self.run_script, placeholder, depth_script) + self.builder.register_on_depth_processed_callback( + callback, order=placeholder.order) + + # Run at build finish + if finished_script: + callback = weakref_partial( + self.run_script, placeholder, finished_script) + self.builder.register_on_finished_callback( + callback, order=placeholder.order) + + # If placeholder should be deleted, delete it after finish so + # the scripts have access to it up to the last run + if not placeholder.data.get("keep_placeholder", True): + delete_callback = weakref_partial( + self.delete_placeholder, placeholder) + self.builder.register_on_finished_callback( + delete_callback, order=placeholder.order + 1) + + def run_script(self, placeholder, script, event=None): + """Run script + + Even though `placeholder` is an unused arguments by exposing it as + an input argument it means it makes it available through + globals()/locals() in the `exec` call, giving the script access + to the placeholder. + + For example: + >>> node = placeholder.scene_identifier + + In the case the script is running at a callback level (not during + populate) then it has access to the `event` as well, otherwise the + value is None if it runs during `populate_placeholder` directly. + + For example adding this as the callback script: + >>> if event is not None: + >>> if event.topic == "on_depth_processed": + >>> print(f"Processed depth: {event.get('depth')}") + >>> elif event.topic == "on_finished": + >>> print("Build finished.") + + """ + self.log.debug(f"Running script at event: {event}") + exec(script, locals()) From 7cf7e33452b22499db077876343af210913b8dd2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 19 Apr 2024 22:35:57 +0200 Subject: [PATCH 046/192] Refactor adding callbacks --- .../hosts/maya/plugins/workfile_build/script_placeholder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py b/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py index 893e2ec0cb..62e10ba023 100644 --- a/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py +++ b/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py @@ -156,14 +156,14 @@ class MayaPlaceholderScriptPlugin(MayaPlaceholderPlugin): if depth_script: callback = weakref_partial( self.run_script, placeholder, depth_script) - self.builder.register_on_depth_processed_callback( + self.builder.add_on_depth_processed_callback( callback, order=placeholder.order) # Run at build finish if finished_script: callback = weakref_partial( self.run_script, placeholder, finished_script) - self.builder.register_on_finished_callback( + self.builder.add_on_finished_callback( callback, order=placeholder.order) # If placeholder should be deleted, delete it after finish so @@ -171,7 +171,7 @@ class MayaPlaceholderScriptPlugin(MayaPlaceholderPlugin): if not placeholder.data.get("keep_placeholder", True): delete_callback = weakref_partial( self.delete_placeholder, placeholder) - self.builder.register_on_finished_callback( + self.builder.add_on_finished_callback( delete_callback, order=placeholder.order + 1) def run_script(self, placeholder, script, event=None): From 13fbc5b74f3bc83157bd927ab6d5ff0cd69b9c5f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:13:37 +0200 Subject: [PATCH 047/192] added folder type to template data --- client/ayon_core/pipeline/template_data.py | 5 ++-- .../publish/collect_anatomy_instance_data.py | 28 +++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index 526c7d35c5..02bccb5f49 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -73,8 +73,8 @@ def get_folder_template_data(folder_entity, project_name): - 'parent' - direct parent name, project name used if is under project - Required document fields: - Folder: 'path' -> Plan to require: 'folderType' + Required entity fields: + Folder: 'path', 'folderType' Args: folder_entity (Dict[str, Any]): Folder entity. @@ -101,6 +101,7 @@ def get_folder_template_data(folder_entity, project_name): return { "folder": { "name": folder_name, + "type": folder_entity["folderType"], }, "asset": folder_name, "hierarchy": hierarchy, 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 f8cc81e718..f0119ef42e 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -33,6 +33,7 @@ import collections import pyblish.api import ayon_api +from ayon_core.pipeline.template_data import get_folder_template_data from ayon_core.pipeline.version_start import get_versioning_start @@ -383,24 +384,11 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): # - 'folder', 'hierarchy', 'parent', 'folder' folder_entity = instance.data.get("folderEntity") if folder_entity: - folder_name = folder_entity["name"] - folder_path = folder_entity["path"] - hierarchy_parts = folder_path.split("/") - hierarchy_parts.pop(0) - hierarchy_parts.pop(-1) - parent_name = project_entity["name"] - if hierarchy_parts: - parent_name = hierarchy_parts[-1] - - hierarchy = "/".join(hierarchy_parts) - anatomy_data.update({ - "asset": folder_name, - "hierarchy": hierarchy, - "parent": parent_name, - "folder": { - "name": folder_name, - }, - }) + folder_data = get_folder_template_data( + folder_entity, + project_entity["name"] + ) + anatomy_data.update(folder_data) return if instance.data.get("newAssetPublishing"): @@ -418,6 +406,10 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): "parent": parent_name, "folder": { "name": folder_name, + # TODO get folder type from hierarchy + # Using 'Shot' is current default behavior of editorial + # (or 'newAssetPublishing') publishing. + "type": "Shot", }, }) From 6e548a83c9d40be1012201160bb794eec50da6d5 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 23 Apr 2024 21:21:30 +0200 Subject: [PATCH 048/192] expose 'render_target' and 'review' creator attributes in publish tab only --- .../plugins/create/create_arnold_rop.py | 28 ++++++++----- .../plugins/create/create_karma_rop.py | 35 +++++++++++------ .../plugins/create/create_mantra_rop.py | 32 ++++++++++----- .../plugins/create/create_redshift_rop.py | 39 ++++++++++++------- .../houdini/plugins/create/create_vray_rop.py | 35 +++++++++++------ 5 files changed, 115 insertions(+), 54 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py index d3254a28dd..1208cfc1ea 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py @@ -21,7 +21,9 @@ class CreateArnoldRop(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes.update(pre_create_data) + for key in ["render_target", "review"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] # Remove the active, we are checking the bypass flag of the nodes instance_data.pop("active", None) @@ -69,10 +71,12 @@ class CreateArnoldRop(plugin.HoudiniCreator): self.lock_parameters(instance_node, to_lock) def get_instance_attr_defs(self): - image_format_enum = [ - "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", - "rad", "rat", "rta", "sgi", "tga", "tif", - ] + """get instance attribute definitions. + + Attributes defined in this method are exposed in + publish tab in the publisher UI. + """ + render_target_items = { "local": "Local machine rendering", "local_no_render": "Use existing frames (local)", @@ -89,12 +93,18 @@ class CreateArnoldRop(plugin.HoudiniCreator): items=render_target_items, label="Render target", default=self.render_target), + ] + + def get_pre_create_attr_defs(self): + image_format_enum = [ + "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", + "rad", "rat", "rta", "sgi", "tga", "tif", + ] + + attrs = [ EnumDef("image_format", image_format_enum, default=self.ext, label="Image Format Options"), ] - - def get_pre_create_attr_defs(self): - - return self.get_instance_attr_defs() + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py index 0af2fe8aeb..48cf5057ab 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py @@ -19,7 +19,10 @@ class CreateKarmaROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes.update(pre_create_data) + + for key in ["render_target", "review"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "karma"}) @@ -92,10 +95,12 @@ class CreateKarmaROP(plugin.HoudiniCreator): self.lock_parameters(instance_node, to_lock) def get_instance_attr_defs(self): - image_format_enum = [ - "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", - "rad", "rat", "rta", "sgi", "tga", "tif", - ] + """get instance attribute definitions. + + Attributes defined in this method are exposed in + publish tab in the publisher UI. + """ + render_target_items = { "local": "Local machine rendering", "local_no_render": "Use existing frames (local)", @@ -110,7 +115,19 @@ class CreateKarmaROP(plugin.HoudiniCreator): EnumDef("render_target", items=render_target_items, label="Render target", - default=self.render_target), + default=self.render_target) + ] + + + def get_pre_create_attr_defs(self): + image_format_enum = [ + "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", + "rad", "rat", "rta", "sgi", "tga", "tif", + ] + + attrs = super(CreateKarmaROP, self).get_pre_create_attr_defs() + + attrs += [ EnumDef("image_format", image_format_enum, default="exr", @@ -127,8 +144,4 @@ class CreateKarmaROP(plugin.HoudiniCreator): label="Camera Resolution", default=False), ] - - - def get_pre_create_attr_defs(self): - - return self.get_instance_attr_defs() + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py index eac7f06b90..05b4431aba 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py @@ -19,7 +19,9 @@ class CreateMantraROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes.update(pre_create_data) + for key in ["render_target", "review"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "ifd"}) @@ -80,10 +82,12 @@ class CreateMantraROP(plugin.HoudiniCreator): self.lock_parameters(instance_node, to_lock) def get_instance_attr_defs(self): - image_format_enum = [ - "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", - "rad", "rat", "rta", "sgi", "tga", "tif", - ] + """get instance attribute definitions. + + Attributes defined in this method are exposed in + publish tab in the publisher UI. + """ + render_target_items = { "local": "Local machine rendering", "local_no_render": "Use existing frames (local)", @@ -99,7 +103,18 @@ class CreateMantraROP(plugin.HoudiniCreator): EnumDef("render_target", items=render_target_items, label="Render target", - default=self.render_target), + default=self.render_target) + ] + + def get_pre_create_attr_defs(self): + image_format_enum = [ + "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", + "rad", "rat", "rta", "sgi", "tga", "tif", + ] + + attrs = super(CreateMantraROP, self).get_pre_create_attr_defs() + + attrs += [ EnumDef("image_format", image_format_enum, default="exr", @@ -110,7 +125,4 @@ class CreateMantraROP(plugin.HoudiniCreator): "resolution, recommended for IPR.", default=False), ] - - def get_pre_create_attr_defs(self): - - return self.get_instance_attr_defs() + return attrs + self.get_instance_attr_defs() 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 2a87d2b35c..3ecb09ee9b 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 @@ -24,7 +24,9 @@ class CreateRedshiftROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes.update(pre_create_data) + for key in ["render_target", "review"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "Redshift_ROP"}) @@ -121,13 +123,12 @@ class CreateRedshiftROP(plugin.HoudiniCreator): return super(CreateRedshiftROP, self).remove_instances(instances) def get_instance_attr_defs(self): - image_format_enum = [ - "exr", "tif", "jpg", "png", - ] - multi_layered_mode = [ - "No Multi-Layered EXR File", - "Full Multi-Layered EXR File" - ] + """get instance attribute definitions. + + Attributes defined in this method are exposed in + publish tab in the publisher UI. + """ + render_target_items = { "local": "Local machine rendering", "local_no_render": "Use existing frames (local)", @@ -143,7 +144,22 @@ class CreateRedshiftROP(plugin.HoudiniCreator): EnumDef("render_target", items=render_target_items, label="Render target", - default=self.render_target), + default=self.render_target) + ] + + def get_pre_create_attr_defs(self): + + image_format_enum = [ + "exr", "tif", "jpg", "png", + ] + + multi_layered_mode = [ + "No Multi-Layered EXR File", + "Full Multi-Layered EXR File" + ] + + attrs = super(CreateRedshiftROP, self).get_pre_create_attr_defs() + attrs += [ EnumDef("image_format", image_format_enum, default=self.ext, @@ -153,7 +169,4 @@ class CreateRedshiftROP(plugin.HoudiniCreator): default=self.multi_layered_mode, label="Multi-Layered EXR"), ] - - def get_pre_create_attr_defs(self): - - return self.get_instance_attr_defs() + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py index cdaee7db06..9e4633e745 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py @@ -23,7 +23,9 @@ class CreateVrayROP(plugin.HoudiniCreator): # Transfer settings from pre create to instance creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes.update(pre_create_data) + for key in ["render_target", "review"]: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] instance_data.pop("active", None) instance_data.update({"node_type": "vray_renderer"}) @@ -146,10 +148,13 @@ class CreateVrayROP(plugin.HoudiniCreator): return super(CreateVrayROP, self).remove_instances(instances) def get_instance_attr_defs(self): - image_format_enum = [ - "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", - "rad", "rat", "rta", "sgi", "tga", "tif", - ] + """get instance attribute definitions. + + Attributes defined in this method are exposed in + publish tab in the publisher UI. + """ + + render_target_items = { "local": "Local machine rendering", "local_no_render": "Use existing frames (local)", @@ -165,7 +170,18 @@ class CreateVrayROP(plugin.HoudiniCreator): EnumDef("render_target", items=render_target_items, label="Render target", - default=self.render_target), + default=self.render_target) + ] + + def get_pre_create_attr_defs(self): + image_format_enum = [ + "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", + "rad", "rat", "rta", "sgi", "tga", "tif", + ] + + attrs = super(CreateVrayROP, self).get_pre_create_attr_defs() + + attrs += [ EnumDef("image_format", image_format_enum, default=self.ext, @@ -179,9 +195,6 @@ class CreateVrayROP(plugin.HoudiniCreator): label="Render Element", tooltip="Create Render Element Node " "if enabled", - default=False), + default=False) ] - - def get_pre_create_attr_defs(self): - - return self.get_instance_attr_defs() + return attrs + self.get_instance_attr_defs() From 74fe21a2b3b6db779390966c3041fe6aef0b6bfa Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 23 Apr 2024 21:30:38 +0200 Subject: [PATCH 049/192] revert changes - add only 'multipartExr' flag --- .../plugins/publish/collect_arnold_rop.py | 6 +++- .../plugins/publish/collect_mantra_rop.py | 13 +++++--- .../plugins/publish/collect_redshift_rop.py | 30 +++++-------------- .../plugins/publish/collect_vray_rop.py | 5 ++-- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py index fa9a1eea0f..3a65b8d026 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py @@ -41,9 +41,11 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin): render_products = [] # Store whether we are splitting the render job (export + render) + split_render = bool(rop.parm("ar_ass_export_enable").eval()) + instance.data["splitRender"] = split_render export_prefix = None export_products = [] - if instance.data["splitRender"]: + if split_render: export_prefix = evalParmNoFrame( rop, "ar_ass_file", pad_character="0" ) @@ -70,6 +72,8 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin): multipartExr = True num_aovs = rop.evalParm("ar_aovs") + # TODO: Check the following logic. + # as it always assumes that all AOV are not merged. for index in range(1, num_aovs + 1): # Skip disabled AOVs if not rop.evalParm("ar_enable_aov{}".format(index)): 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 e85751c08a..6112f0a581 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 @@ -45,9 +45,11 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): 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 instance.data["splitRender"]: + if split_render: export_prefix = evalParmNoFrame( rop, "soho_diskfile", pad_character="0" ) @@ -74,6 +76,9 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): # Assume it's a multipartExr Render. multipartExr = True + + # TODO: This logic doesn't take into considerations + # cryptomatte defined in 'Images > Cryptomatte' aov_numbers = rop.evalParm("vm_numaux") if aov_numbers > 0: # get the filenames of the AOVs @@ -85,9 +90,6 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): aov_enabled = rop.evalParm(aov_boolean) has_aov_path = rop.evalParm(aov_name) if has_aov_path and aov_enabled == 1: - # Set to False as soon as we have a separated aov. - multipartExr = False - aov_prefix = evalParmNoFrame(rop, aov_name) aov_product = self.get_render_product_name( prefix=aov_prefix, suffix=None @@ -96,6 +98,9 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): files_by_aov[var] = self.generate_expected_files(instance, aov_product) # noqa + # Set to False as soon as we have a separated aov. + multipartExr = False + # Review Logic expects this key to exist and be True # if render is a multipart Exr. # As long as we have one AOV then multipartExr should be True. 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 aff9269fa5..89868b1c33 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 @@ -43,8 +43,10 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): default_prefix = evalParmNoFrame(rop, "RS_outputFileNamePrefix") beauty_suffix = rop.evalParm("RS_outputBeautyAOVSuffix") # Store whether we are splitting the render job (export + render) + split_render = bool(rop.parm("RS_archive_enable").eval()) + instance.data["splitRender"] = split_render export_products = [] - if instance.data["splitRender"]: + if split_render: export_prefix = evalParmNoFrame( rop, "RS_archive_file", pad_character="0" ) @@ -58,27 +60,11 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): instance.data["ifdFile"] = beauty_export_product instance.data["exportFiles"] = list(export_products) - # Set MultiLayer Mode. - creator_attribute = instance.data["creator_attributes"] - ext = creator_attribute.get("image_format") - multi_layered_mode = creator_attribute.get("multi_layered_mode") - full_exr_mode = False - if ext == "exr": - if multi_layered_mode == "No Multi-Layered EXR File": - rop.setParms({ - "RS_outputMultilayerMode": "1", - "RS_aovMultipart": False - }) - full_exr_mode = True - # Ignore beauty suffix if full mode is enabled - # As this is what the rop does. - beauty_suffix = "" - - elif multi_layered_mode == "Full Multi-Layered EXR File": - rop.setParms({ - "RS_outputMultilayerMode": "2", - "RS_aovMultipart": True - }) + 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 = "" # Assume it's a multipartExr Render. multipartExr = True diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py index 2eb5e3164a..13478a9d2b 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py @@ -46,9 +46,11 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): # TODO: add render elements if render element # Store whether we are splitting the render job in an export + render + split_render = rop.parm("render_export_mode").eval() == "2" + instance.data["splitRender"] = split_render export_prefix = None export_products = [] - if instance.data["splitRender"]: + if split_render: export_prefix = evalParmNoFrame( rop, "render_export_filepath", pad_character="0" ) @@ -78,7 +80,6 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): render_products.append(renderpass) files_by_aov[aov] = self.generate_expected_files( instance, renderpass) - # Set to False as soon as we have a separated aov. multipartExr = False From effedd82c8ddb511d844dcdef8724c4c63c2355f Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 23 Apr 2024 21:30:58 +0200 Subject: [PATCH 050/192] fix bug with aov_filter --- .../plugins/publish/collect_local_render_instances.py | 9 +++++---- server_addon/houdini/server/settings/publish.py | 9 ++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py index 073053188c..5a446fa0d3 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_local_render_instances.py @@ -25,8 +25,9 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): hosts = ["houdini"] label = "Collect local render instances" - override_deadline_aov_filter = False - aov_filter = {} + use_deadline_aov_filter = False + aov_filter = {"host_name": "houdini", + "value": [".*([Bb]eauty).*"]} @classmethod def apply_settings(cls, project_settings): @@ -37,11 +38,11 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin): category="houdini") apply_plugin_settings_automatically(cls, settings, logger=cls.log) - if not cls.override_deadline_aov_filter: + if not cls.use_deadline_aov_filter: # get aov_filter from collector settings # and restructure it as match_aov_pattern requires. cls.aov_filter = { - "houdini": cls.aov_filter["value"] + cls.aov_filter["host_name"]: cls.aov_filter["value"] } else: # get aov_filter from deadline settings diff --git a/server_addon/houdini/server/settings/publish.py b/server_addon/houdini/server/settings/publish.py index 0912ecd997..9e8e796aff 100644 --- a/server_addon/houdini/server/settings/publish.py +++ b/server_addon/houdini/server/settings/publish.py @@ -24,6 +24,8 @@ class CollectChunkSizeModel(BaseSettingsModel): class AOVFilterSubmodel(BaseSettingsModel): + """You should use the same host name you are using for Houdini.""" + host_name: str = SettingsField("", title="Houdini Host name") value: list[str] = SettingsField( default_factory=list, title="AOV regex" @@ -31,9 +33,9 @@ class AOVFilterSubmodel(BaseSettingsModel): class CollectLocalRenderInstancesModel(BaseSettingsModel): - override_deadline_aov_filter: bool = SettingsField( + use_deadline_aov_filter: bool = SettingsField( False, - title="Override Deadline AOV Filter" + title="Use Deadline AOV Filter" ) aov_filter: AOVFilterSubmodel = SettingsField( @@ -109,8 +111,9 @@ DEFAULT_HOUDINI_PUBLISH_SETTINGS = { "chunk_size": 999999 }, "CollectLocalRenderInstances": { - "override_deadline_aov_filter": False, + "use_deadline_aov_filter": False, "aov_filter" : { + "host_name": "houdini", "value": [ ".*([Bb]eauty).*" ] From 47b27ce009ee2c8c5b50e10ac3ee32c91f292a8e Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 23 Apr 2024 22:16:24 +0200 Subject: [PATCH 051/192] use 'creator_attributes' as the source of truth - use extract render to adjust parameters accordingly --- .../hosts/houdini/plugins/publish/collect_arnold_rop.py | 5 +---- .../hosts/houdini/plugins/publish/collect_mantra_rop.py | 5 +---- .../hosts/houdini/plugins/publish/collect_redshift_rop.py | 6 ++---- .../hosts/houdini/plugins/publish/collect_vray_rop.py | 5 +---- .../hosts/houdini/plugins/publish/extract_render.py | 2 +- 5 files changed, 6 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py index 3a65b8d026..53a3e52717 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py @@ -40,12 +40,9 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin): default_prefix = evalParmNoFrame(rop, "ar_picture") render_products = [] - # Store whether we are splitting the render job (export + render) - split_render = bool(rop.parm("ar_ass_export_enable").eval()) - instance.data["splitRender"] = split_render export_prefix = None export_products = [] - if split_render: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "ar_ass_file", pad_character="0" ) 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 6112f0a581..7b247768fc 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 @@ -44,12 +44,9 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin): 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: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "soho_diskfile", pad_character="0" ) 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 89868b1c33..ce90ae2413 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 @@ -42,11 +42,9 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin): default_prefix = evalParmNoFrame(rop, "RS_outputFileNamePrefix") beauty_suffix = rop.evalParm("RS_outputBeautyAOVSuffix") - # Store whether we are splitting the render job (export + render) - split_render = bool(rop.parm("RS_archive_enable").eval()) - instance.data["splitRender"] = split_render + export_products = [] - if split_render: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "RS_archive_file", pad_character="0" ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py index 13478a9d2b..c39b1db103 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py @@ -45,12 +45,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin): render_products = [] # TODO: add render elements if render element - # Store whether we are splitting the render job in an export + render - split_render = rop.parm("render_export_mode").eval() == "2" - instance.data["splitRender"] = split_render export_prefix = None export_products = [] - if split_render: + if instance.data["splitRender"]: export_prefix = evalParmNoFrame( rop, "render_export_filepath", pad_character="0" ) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py index 8a666541cb..7b4762a25f 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_render.py @@ -23,7 +23,7 @@ class ExtractRender(publish.Extractor): rop_node = hou.node(instance.data.get("instance_node")) # Align split parameter value on rop node to the render target. - if creator_attribute.get("render_target") == "farm_split": + if instance.data["splitRender"]: if product_type == "arnold_rop": rop_node.setParms({"ar_ass_export_enable": 1}) elif product_type == "mantra_rop": From e04c9285f1a1dcc4eaa3560ad964b057d9dc3478 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 23 Apr 2024 22:32:04 +0200 Subject: [PATCH 052/192] add a TODO about running plugins over wrong isntances --- .../hosts/houdini/plugins/publish/extract_opengl.py | 6 ++++++ .../houdini/plugins/publish/validate_review_colorspace.py | 5 +++++ .../hosts/houdini/plugins/publish/validate_scene_review.py | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py index 69bbb22340..d3b4b094b2 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py @@ -17,6 +17,12 @@ class ExtractOpenGL(publish.Extractor): def process(self, instance): ropnode = hou.node(instance.data.get("instance_node")) + + # This plugin is triggered when marking render as reviewable. + # Therefore, this plugin will run on over wrong instances. + # TODO: Don't run this plugin on wrong instances. + # This plugin should run only on review product type + # with instance node of opengl type. if ropnode.type().name() != "opengl": self.log.debug("Skipping OpenGl extraction. Rop node {} " "is not an OpenGl node.".format(ropnode.path())) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py index e02ce93f0d..691b54ac05 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py @@ -35,6 +35,11 @@ class ValidateReviewColorspace(pyblish.api.InstancePlugin, rop_node = hou.node(instance.data["instance_node"]) + # This plugin is triggered when marking render as reviewable. + # Therefore, this plugin will run on over wrong instances. + # TODO: Don't run this plugin on wrong instances. + # This plugin should run only on review product type + # with instance node of opengl type. if rop_node.type().name() != "opengl": self.log.debug("Skipping Validation. Rop node {} " "is not an OpenGl node.".format(rop_node.path())) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py index 9b81f0f8ed..0b09306b0d 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py @@ -19,6 +19,12 @@ class ValidateSceneReview(pyblish.api.InstancePlugin): report = [] instance_node = hou.node(instance.data.get("instance_node")) + + # This plugin is triggered when marking render as reviewable. + # Therefore, this plugin will run on over wrong instances. + # TODO: Don't run this plugin on wrong instances. + # This plugin should run only on review product type + # with instance node of opengl type. if instance_node.type().name() != "opengl": self.log.debug("Skipping Validation. Rop node {} " "is not an OpenGl node.".format(instance_node.path())) From e194652f65d122f769c028e3df5aca539f186018 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:59:16 +0200 Subject: [PATCH 053/192] add path to folder keys --- client/ayon_core/pipeline/template_data.py | 1 + .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 1 + 2 files changed, 2 insertions(+) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index 02bccb5f49..d5f06d6a59 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -102,6 +102,7 @@ def get_folder_template_data(folder_entity, project_name): "folder": { "name": folder_name, "type": folder_entity["folderType"], + "path": path, }, "asset": folder_name, "hierarchy": hierarchy, 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 f0119ef42e..ad5a5d43fc 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -406,6 +406,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): "parent": parent_name, "folder": { "name": folder_name, + "path": instance.data["folderPath"], # TODO get folder type from hierarchy # Using 'Shot' is current default behavior of editorial # (or 'newAssetPublishing') publishing. From 8110a601bbc0d114c1bcd22be8122cc591ef51a7 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 25 Apr 2024 11:02:31 +0200 Subject: [PATCH 054/192] add CollectLocalRenderInstances setting --- server_addon/houdini/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 4b72af2a89..4e441c76ae 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,3 +1,3 @@ name = "houdini" title = "Houdini" -version = "0.2.12" +version = "0.2.13" From 767bbf070fb1a389f217d9e8b37d3c07a5e85f5f Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 29 Apr 2024 10:39:16 +0300 Subject: [PATCH 055/192] add CollectLocalRenderInstances setting --- server_addon/houdini/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 4e441c76ae..6c81eba439 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,3 +1,3 @@ name = "houdini" title = "Houdini" -version = "0.2.13" +version = "0.2.14" From cf9707db85f637b3549a3b84ec30aca2ddeea400 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Apr 2024 11:25:04 +0200 Subject: [PATCH 056/192] Refactor OCIO config settings and profiles Added new OCIO config profile types and paths for built-in and custom configurations. Updated default values accordingly. --- server/settings/main.py | 138 +++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 43 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 28a69e182d..85c1779999 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -54,9 +54,66 @@ class CoreImageIOFileRulesModel(BaseSettingsModel): return value -class CoreImageIOConfigModel(BaseSettingsModel): - filepath: list[str] = SettingsField( - default_factory=list, title="Config path" +_ocio_config_profile_types = [ + {"value": "buildin_path", "label": "Ayon built-in OCIO config"}, + {"value": "custom_path", "label": "Path to OCIO config"}, + {"value": "product", "label": "Published product"}, +] + + +def _ocio_build_in_paths(): + return [ + { + "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", + "label": "ACES 1.2", + "description": "Aces 1.2 OCIO config file." + }, + { + "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", + "label": "Nuke default", + }, + ] + + +class CoreImageIOConfigProfilesModel(BaseSettingsModel): + host_names: list[str] = SettingsField( + default_factory=list, + title="Host names" + ) + task_types: list[str] = SettingsField( + default_factory=list, + title="Task types", + enum_resolver=task_types_enum + ) + task_names: list[str] = SettingsField( + default_factory=list, + title="Task names" + ) + type: str = SettingsField( + title="Profile type", + enum_resolver=lambda: _ocio_config_profile_types, + conditionalEnum=True, + default="buildin_path", + section="---", + ) + + buildin_path: str = SettingsField( + "ACES 1.2", + title="Built-in OCIO config", + enum_resolver=_ocio_build_in_paths, + ) + custom_path: str = SettingsField( + "", + title="OCIO config path", + description="Path to OCIO config. Anatomy formatting is supported.", + ) + product: str = SettingsField( + "", + title="Product name", + description=( + "Published product name to get OCIO config from. " + "Partial match is supported." + ), ) @@ -65,9 +122,8 @@ class CoreImageIOBaseModel(BaseSettingsModel): False, title="Enable Color Management" ) - ocio_config: CoreImageIOConfigModel = SettingsField( - default_factory=CoreImageIOConfigModel, - title="OCIO config" + ocio_config_profiles: list[CoreImageIOConfigProfilesModel] = SettingsField( + default_factory=list, title="OCIO config profiles" ) file_rules: CoreImageIOFileRulesModel = SettingsField( default_factory=CoreImageIOFileRulesModel, @@ -186,12 +242,17 @@ class CoreSettings(BaseSettingsModel): DEFAULT_VALUES = { "imageio": { "activate_global_color_management": False, - "ocio_config": { - "filepath": [ - "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", - "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio" - ] - }, + "ocio_config_profiles": [ + { + "host_names": [], + "task_types": [], + "task_names": [], + "type": "buildin_path", + "buildin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", + "custom_path": "", + "product": "", + } + ], "file_rules": { "activate_global_file_rules": False, "rules": [ @@ -199,42 +260,33 @@ DEFAULT_VALUES = { "name": "example", "pattern": ".*(beauty).*", "colorspace": "ACES - ACEScg", - "ext": "exr" + "ext": "exr", } - ] - } + ], + }, }, "studio_name": "", "studio_code": "", - "environments": "{\n\"STUDIO_SW\": {\n \"darwin\": \"/mnt/REPO_SW\",\n \"linux\": \"/mnt/REPO_SW\",\n \"windows\": \"P:/REPO_SW\"\n }\n}", + "environments": '{\n"STUDIO_SW": {\n "darwin": "/mnt/REPO_SW",\n "linux": "/mnt/REPO_SW",\n "windows": "P:/REPO_SW"\n }\n}', "tools": DEFAULT_TOOLS_VALUES, - "version_start_category": { - "profiles": [] - }, + "version_start_category": {"profiles": []}, "publish": DEFAULT_PUBLISH_VALUES, - "project_folder_structure": json.dumps({ - "__project_root__": { - "prod": {}, - "resources": { - "footage": { - "plates": {}, - "offline": {} + "project_folder_structure": json.dumps( + { + "__project_root__": { + "prod": {}, + "resources": { + "footage": {"plates": {}, "offline": {}}, + "audio": {}, + "art_dept": {}, }, - "audio": {}, - "art_dept": {} - }, - "editorial": {}, - "assets": { - "characters": {}, - "locations": {} - }, - "shots": {} - } - }, indent=4), - "project_plugins": { - "windows": [], - "darwin": [], - "linux": [] - }, - "project_environments": "{}" + "editorial": {}, + "assets": {"characters": {}, "locations": {}}, + "shots": {}, + } + }, + indent=4, + ), + "project_plugins": {"windows": [], "darwin": [], "linux": []}, + "project_environments": "{}", } From 7b86da34ae86b0f866f610b0eb048511ddf1c743 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:59:04 +0200 Subject: [PATCH 057/192] fix buildin to builtin --- server/settings/main.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 85c1779999..8bbe13da30 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -55,13 +55,13 @@ class CoreImageIOFileRulesModel(BaseSettingsModel): _ocio_config_profile_types = [ - {"value": "buildin_path", "label": "Ayon built-in OCIO config"}, + {"value": "builtin_path", "label": "Ayon built-in OCIO config"}, {"value": "custom_path", "label": "Path to OCIO config"}, {"value": "product", "label": "Published product"}, ] -def _ocio_build_in_paths(): +def _ocio_built_in_paths(): return [ { "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", @@ -93,14 +93,14 @@ class CoreImageIOConfigProfilesModel(BaseSettingsModel): title="Profile type", enum_resolver=lambda: _ocio_config_profile_types, conditionalEnum=True, - default="buildin_path", + default="builtin_path", section="---", ) - buildin_path: str = SettingsField( + builtin_path: str = SettingsField( "ACES 1.2", title="Built-in OCIO config", - enum_resolver=_ocio_build_in_paths, + enum_resolver=_ocio_built_in_paths, ) custom_path: str = SettingsField( "", @@ -247,8 +247,8 @@ DEFAULT_VALUES = { "host_names": [], "task_types": [], "task_names": [], - "type": "buildin_path", - "buildin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", + "type": "builtin_path", + "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "custom_path": "", "product": "", } From 8c525987e58991d770208f93bedcc6ed1bcd3bde Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:59:16 +0200 Subject: [PATCH 058/192] change Ayon to AYON --- server/settings/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/main.py b/server/settings/main.py index 8bbe13da30..d6a38a90e6 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -55,7 +55,7 @@ class CoreImageIOFileRulesModel(BaseSettingsModel): _ocio_config_profile_types = [ - {"value": "builtin_path", "label": "Ayon built-in OCIO config"}, + {"value": "builtin_path", "label": "AYON built-in OCIO config"}, {"value": "custom_path", "label": "Path to OCIO config"}, {"value": "product", "label": "Published product"}, ] From c0d9edad758559c15790c4cfd09f7c357ca84362 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:59:33 +0200 Subject: [PATCH 059/192] _ocio_config_profile_types is function --- server/settings/main.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index d6a38a90e6..230414ffe7 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -54,11 +54,12 @@ class CoreImageIOFileRulesModel(BaseSettingsModel): return value -_ocio_config_profile_types = [ - {"value": "builtin_path", "label": "AYON built-in OCIO config"}, - {"value": "custom_path", "label": "Path to OCIO config"}, - {"value": "product", "label": "Published product"}, -] +def _ocio_config_profile_types(): + return [ + {"value": "builtin_path", "label": "AYON built-in OCIO config"}, + {"value": "custom_path", "label": "Path to OCIO config"}, + {"value": "product", "label": "Published product"}, + ] def _ocio_built_in_paths(): @@ -91,7 +92,7 @@ class CoreImageIOConfigProfilesModel(BaseSettingsModel): ) type: str = SettingsField( title="Profile type", - enum_resolver=lambda: _ocio_config_profile_types, + enum_resolver=_ocio_config_profile_types, conditionalEnum=True, default="builtin_path", section="---", From feb73842c377c401f2acdd1dc18c6852b3b23de7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:07:17 +0200 Subject: [PATCH 060/192] rename 'product' attribute to 'product_name' --- server/settings/main.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 230414ffe7..c9c86bdd0d 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -97,7 +97,6 @@ class CoreImageIOConfigProfilesModel(BaseSettingsModel): default="builtin_path", section="---", ) - builtin_path: str = SettingsField( "ACES 1.2", title="Built-in OCIO config", @@ -108,7 +107,7 @@ class CoreImageIOConfigProfilesModel(BaseSettingsModel): title="OCIO config path", description="Path to OCIO config. Anatomy formatting is supported.", ) - product: str = SettingsField( + product_name: str = SettingsField( "", title="Product name", description=( @@ -251,7 +250,7 @@ DEFAULT_VALUES = { "type": "builtin_path", "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "custom_path": "", - "product": "", + "product_name": "", } ], "file_rules": { From 9be28b8051124fd8d699b665299c6eeb98cbc63b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:00:16 +0200 Subject: [PATCH 061/192] Fix `publish_attributes` access --- .../plugins/publish/validate_alembic_options_defaults.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 5197100406..a9de510e67 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -29,13 +29,7 @@ class ValidateAlembicDefaultsPointcache( @classmethod def _get_publish_attributes(cls, instance): - attributes = instance.data["publish_attributes"][ - cls.plugin_name( - instance.data["publish_attributes"] - ) - ] - - return attributes + return instance.data["publish_attributes"][cls.plugin_name] def process(self, instance): if not self.is_active(instance.data): From b05cac07b8b9cb596aa090c77b0425a216cadd65 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:01:52 +0200 Subject: [PATCH 062/192] Fix repair logic --- .../maya/plugins/publish/validate_alembic_options_defaults.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index a9de510e67..940e4f3869 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -69,13 +69,11 @@ class ValidateAlembicDefaultsPointcache( ) # Set the settings values on the create context then save to workfile. - publish_attributes = instance.data["publish_attributes"] - plugin_name = cls.plugin_name(publish_attributes) attributes = cls._get_publish_attributes(instance) settings = cls._get_settings(instance.context) create_publish_attributes = create_instance.data["publish_attributes"] for key in attributes: - create_publish_attributes[plugin_name][key] = settings[key] + create_publish_attributes[cls.plugin_name][key] = settings[key] create_context.save_changes() From 8567e54bb45547bd6b6a31d05123d5c80af626fe Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:04:33 +0200 Subject: [PATCH 063/192] Fix label --- .../maya/plugins/publish/validate_alembic_options_defaults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 940e4f3869..1045ef3c70 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -85,6 +85,6 @@ class ValidateAlembicDefaultsAnimation( The defaults are defined in the project settings. """ - label = "Validate Alembic Options Defaults" + label = "Validate Alembic Options Defaults" families = ["animation"] plugin_name = "ExtractAnimation" From 665a4e226a6cc42039afe938b57ea33d269592d4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:12:17 +0200 Subject: [PATCH 064/192] Fix `writeCreases` support --- .../hosts/maya/plugins/publish/extract_pointcache.py | 8 ++++++++ server_addon/maya/server/settings/publishers.py | 5 +++++ 2 files changed, 13 insertions(+) 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 d7f9594374..d34634bff8 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -63,6 +63,7 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): wholeFrameGeo = False worldSpace = True writeColorSets = False + writeCreases = False writeFaceSets = False writeNormals = True writeUVSets = False @@ -354,6 +355,13 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): default=cls.writeColorSets, tooltip="Write vertex colors with the geometry." ), + "writeCreases": BoolDef( + "writeCreases", + label="Write Creases", + default=cls.writeCreases, + tooltip="Write the geometry's edge and vertex crease " + "information." + ), "writeFaceSets": BoolDef( "writeFaceSets", label="Write Face Sets", diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 8dcffbb59a..ee74eaa553 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -406,6 +406,10 @@ class ExtractAlembicModel(BaseSettingsModel): title="Write Color Sets", description="Write vertex colors with the geometry." ) + writeCreases: bool = SettingsField( + title="Write Creases", + description="Write the geometry's edge and vertex crease information." + ) writeFaceSets: bool = SettingsField( title="Write Face Sets", description="Write face sets with the geometry." @@ -1643,6 +1647,7 @@ DEFAULT_PUBLISH_SETTINGS = { "wholeFrameGeo": False, "worldSpace": True, "writeColorSets": False, + "writeCreases": False, "writeFaceSets": False, "writeNormals": True, "writeUVSets": False, From 141767ef717cf8b15baec850de71f0dc2f013871 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:13:49 +0200 Subject: [PATCH 065/192] Cosmetics --- .../maya/plugins/publish/validate_alembic_options_defaults.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 1045ef3c70..476f837135 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -36,7 +36,6 @@ class ValidateAlembicDefaultsPointcache( return settings = self._get_settings(instance.context) - attributes = self._get_publish_attributes(instance) msg = ( From 0352c17a09108cfcf768aac58da2fef5c67cf12a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:18:20 +0200 Subject: [PATCH 066/192] Add support for extracting the user attributes --- client/ayon_core/hosts/maya/api/alembic.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index bf887df4c7..954c0a0888 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -63,6 +63,8 @@ def extract_alembic( startFrame=None, step=1.0, stripNamespaces=True, + userAttr=None, + userAttrPrefix=None, uvWrite=True, verbose=False, wholeFrameGeo=False, @@ -137,6 +139,12 @@ def extract_alembic( object with the namespace taco:foo:bar appears as bar in the Alembic file. + userAttr (list of str, optional): A specific user defined attribute to + write out. Defaults to []. + + userAttrPrefix (list of str, optional): Prefix filter for determining + which user defined attributes to write out. Defaults to []. + uvWrite (bool): When on, UV data from polygon meshes and subdivision objects are written to the Alembic file. Only the current UV map is included. @@ -183,6 +191,8 @@ def extract_alembic( # Ensure list arguments are valid. attr = attr or [] attrPrefix = attrPrefix or [] + userAttr = userAttr or [] + userAttrPrefix = userAttrPrefix or [] root = root or [] # Pass the start and end frame on as `frameRange` so that it @@ -226,6 +236,8 @@ def extract_alembic( "step": step, "attr": attr, "attrPrefix": attrPrefix, + "userAttr": userAttr, + "userAttrPrefix": userAttrPrefix, "stripNamespaces": stripNamespaces, "verbose": verbose, "preRollStartFrame": preRollStartFrame From 367eba0f493d196525a308bac3085d87772553d9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:25:00 +0200 Subject: [PATCH 067/192] Remove `autoSubd` in favor of legacy `writeCreases` usage across the codebase and existing instances --- .../plugins/publish/extract_pointcache.py | 20 +------------------ .../maya/server/settings/publishers.py | 13 ------------ 2 files changed, 1 insertion(+), 32 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 d34634bff8..cff32ebb67 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -40,7 +40,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): # From settings attr = [] attrPrefix = [] - autoSubd = False bake_attributes = [] bake_attribute_prefixes = [] dataFormat = "ogawa" @@ -174,9 +173,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): "writeVisibility": attribute_values.get( "writeVisibility", self.writeVisibility ), - "autoSubd": attribute_values.get( - "autoSubd", self.autoSubd - ), "uvsOnly": attribute_values.get( "uvsOnly", self.uvsOnly ), @@ -250,7 +246,7 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): with maintained_selection(): cmds.select(instance.data["proxy"]) extract_alembic(**kwargs) - + raise RuntimeError("FAIL") representation = { "name": "proxy", "ext": "abc", @@ -269,20 +265,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): return [] override_defs = OrderedDict({ - "autoSubd": BoolDef( - "autoSubd", - label="Auto Subd", - default=cls.autoSubd, - tooltip=( - "If this flag is present and the mesh has crease edges, " - "crease vertices or holes, the mesh (OPolyMesh) would now " - "be written out as an OSubD and crease info will be stored" - " in the Alembic file. Otherwise, creases info won't be " - "preserved in Alembic file unless a custom Boolean " - "attribute SubDivisionMesh has been added to mesh node and" - " its value is true." - ) - ), "eulerFilter": BoolDef( "eulerFilter", label="Euler Filter", diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index ee74eaa553..3a82d649e1 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -46,7 +46,6 @@ def extract_alembic_overrides_enum(): return [ {"label": "Custom Attributes", "value": "attr"}, {"label": "Custom Attributes Prefix", "value": "attrPrefix"}, - {"label": "Auto Subd", "value": "autoSubd"}, {"label": "Data Format", "value": "dataFormat"}, {"label": "Euler Filter", "value": "eulerFilter"}, {"label": "Mel Per Frame Callback", "value": "melPerFrameCallback"}, @@ -344,17 +343,6 @@ class ExtractAlembicModel(BaseSettingsModel): families: list[str] = SettingsField( default_factory=list, title="Families") - autoSubd: bool = SettingsField( - title="Auto Subd", - description=( - "If this flag is present and the mesh has crease edges, crease " - "vertices or holes, the mesh (OPolyMesh) would now be written out " - "as an OSubD and crease info will be stored in the Alembic file. " - "Otherwise, creases info won't be preserved in Alembic file unless" - " a custom Boolean attribute SubDivisionMesh has been added to " - "mesh node and its value is true." - ) - ) eulerFilter: bool = SettingsField( title="Euler Filter", description="Apply Euler filter while sampling rotations." @@ -1615,7 +1603,6 @@ DEFAULT_PUBLISH_SETTINGS = { ], "attr": "", "attrPrefix": "", - "autoSubd": False, "bake_attributes": [], "bake_attribute_prefixes": [], "dataFormat": "ogawa", From 26eb91781365c44036b02e8644f3fb2774e78c5a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:26:53 +0200 Subject: [PATCH 068/192] Add `uvsOnly` argument --- client/ayon_core/hosts/maya/api/alembic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index 954c0a0888..a815dc7465 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -65,6 +65,7 @@ def extract_alembic( stripNamespaces=True, userAttr=None, userAttrPrefix=None, + uvsOnly=False, uvWrite=True, verbose=False, wholeFrameGeo=False, @@ -145,6 +146,9 @@ def extract_alembic( userAttrPrefix (list of str, optional): Prefix filter for determining which user defined attributes to write out. Defaults to []. + uvsOnly (bool): When on, only uv data for PolyMesh and SubD shapes + will be written to the Alembic file. + uvWrite (bool): When on, UV data from polygon meshes and subdivision objects are written to the Alembic file. Only the current UV map is included. @@ -225,6 +229,7 @@ def extract_alembic( "preRoll": preRoll, "renderableOnly": renderableOnly, "uvWrite": uvWrite, + "uvsOnly": uvsOnly, "writeColorSets": writeColorSets, "writeFaceSets": writeFaceSets, "wholeFrameGeo": wholeFrameGeo, From e43a4ba481e7f307ec114cae2b735dfbdbfae704 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:28:27 +0200 Subject: [PATCH 069/192] Fix usage of `root` argument --- client/ayon_core/hosts/maya/api/alembic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index a815dc7465..3722774bba 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -227,6 +227,7 @@ def extract_alembic( "eulerFilter": eulerFilter, "noNormals": noNormals, "preRoll": preRoll, + "root": root, "renderableOnly": renderableOnly, "uvWrite": uvWrite, "uvsOnly": uvsOnly, From 828f7bbce2038a1a38a6fbde66d71fc8143083ae Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:37:42 +0200 Subject: [PATCH 070/192] Fix `writeNormals` usage --- .../hosts/maya/plugins/publish/extract_pointcache.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 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 cff32ebb67..3c0350ec65 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -176,9 +176,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): "uvsOnly": attribute_values.get( "uvsOnly", self.uvsOnly ), - "writeNormals": attribute_values.get( - "writeNormals", self.writeNormals - ), "melPerFrameCallback": attribute_values.get( "melPerFrameCallback", self.melPerFrameCallback ), @@ -190,7 +187,12 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): ), "pythonPostJobCallback": attribute_values.get( "pythonPostJobCallback", self.pythonPostJobCallback - ) + ), + # Note that this converts `writeNormals` to `noNormals` for the + # `AbcExport` equivalent in `extract_alembic` + "noNormals": not attribute_values.get( + "writeNormals", self.writeNormals + ), } if instance.data.get("visibleOnly", False): From 5193d2664fed856399690869572f6a33dbb9f94a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:38:01 +0200 Subject: [PATCH 071/192] Fix passing of callbacks --- client/ayon_core/hosts/maya/api/alembic.py | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index 3722774bba..e9bf6d71ca 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -54,9 +54,13 @@ def extract_alembic( endFrame=None, eulerFilter=True, frameRange="", + melPerFrameCallback=None, + melPostJobCallback=None, noNormals=False, preRoll=False, preRollStartFrame=0, + pythonPerFrameCallback=None, + pythonPostJobCallback=None, renderableOnly=False, root=None, selection=True, @@ -105,6 +109,11 @@ def extract_alembic( string formatted as: "startFrame endFrame". This argument overrides `startFrame` and `endFrame` arguments. + melPerFrameCallback (Optional[str]): MEL callback run per frame. + + melPostJobCallback (Optional[str]): MEL callback after last frame is + written. + noNormals (bool): When on, normal data from the original polygon objects is not included in the exported Alembic cache file. @@ -116,6 +125,11 @@ def extract_alembic( dependent translations and can be used to evaluate run-up that isn't actually translated. Defaults to 0. + pythonPerFrameCallback (Optional[str]): Python callback run per frame. + + pythonPostJobCallback (Optional[str]): Python callback after last frame + is written. + renderableOnly (bool): When on, any non-renderable nodes or hierarchy, such as hidden objects, are not included in the Alembic file. Defaults to False. @@ -282,6 +296,17 @@ def extract_alembic( if maya_version >= 2018: options['autoSubd'] = options.pop('writeCreases', False) + # Only add callbacks if they are set so that we're not passing `None` + callbacks = { + "melPerFrameCallback": melPerFrameCallback, + "melPostJobCallback": melPostJobCallback, + "pythonPerFrameCallback": pythonPerFrameCallback, + "pythonPostJobCallback": pythonPostJobCallback, + } + for key, callback in callbacks.items(): + if callback: + options[key] = str(callback) + # Format the job string from options job_args = list() for key, value in options.items(): From ba50a64a3842930ef77cf985093f139aa4cc1961 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:39:45 +0200 Subject: [PATCH 072/192] Remove debugging error --- .../ayon_core/hosts/maya/plugins/publish/extract_pointcache.py | 1 - 1 file changed, 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 3c0350ec65..05deaa0834 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -248,7 +248,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): with maintained_selection(): cmds.select(instance.data["proxy"]) extract_alembic(**kwargs) - raise RuntimeError("FAIL") representation = { "name": "proxy", "ext": "abc", From 02ccceaf24caaa8ca52c447a21065001029fc57f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:48:36 +0200 Subject: [PATCH 073/192] Bump Maya addon version --- server_addon/maya/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/package.py b/server_addon/maya/package.py index 5c6ce923aa..fe3e3039f5 100644 --- a/server_addon/maya/package.py +++ b/server_addon/maya/package.py @@ -1,3 +1,3 @@ name = "maya" title = "Maya" -version = "0.1.17" +version = "0.1.18" From 2bf6ed71156d912aa2ce4329e6091761fc47fd5b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 17:18:23 +0200 Subject: [PATCH 074/192] Do not modify `roots` in-place so that `roots` remains the roots and doesn't contain the descendendants + ignore intermediate objects since they would not be exported anyway --- .../hosts/maya/plugins/publish/extract_pointcache.py | 7 +++---- 1 file changed, 3 insertions(+), 4 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 05deaa0834..cc930e49cc 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -6,6 +6,7 @@ from maya import cmds from ayon_core.pipeline import publish from ayon_core.hosts.maya.api.alembic import extract_alembic from ayon_core.hosts.maya.api.lib import ( + get_all_children, suspended_refresh, maintained_selection, iter_visible_nodes_in_range @@ -518,9 +519,7 @@ class ExtractAnimation(ExtractAlembic): roots = cmds.sets(out_set, query=True) or [] # Include all descendants - nodes = roots - nodes += cmds.listRelatives( - roots, allDescendents=True, fullPath=True - ) or [] + nodes = roots.copy() + nodes.extend(get_all_children(roots, ignore_intermediate_objects=True)) return nodes, roots From 1513660b7e842f31bc33bacf464e44ea0edc62b4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 22:51:36 +0200 Subject: [PATCH 075/192] Pass `preRollStartFrame` as separate argument --- client/ayon_core/hosts/maya/api/alembic.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index e9bf6d71ca..6bd00e1cb1 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -22,7 +22,6 @@ ALEMBIC_ARGS = { "melPostJobCallback": str, "noNormals": bool, "preRoll": bool, - "preRollStartFrame": int, "pythonPerFrameCallback": str, "pythonPostJobCallback": str, "renderableOnly": bool, @@ -259,8 +258,7 @@ def extract_alembic( "userAttr": userAttr, "userAttrPrefix": userAttrPrefix, "stripNamespaces": stripNamespaces, - "verbose": verbose, - "preRollStartFrame": preRollStartFrame + "verbose": verbose } # Validate options @@ -340,7 +338,11 @@ def extract_alembic( # exports are made. (PLN-31) # TODO: Make sure this actually fixes the issues with evaluation("off"): - cmds.AbcExport(j=job_str, verbose=verbose) + cmds.AbcExport( + j=job_str, + verbose=verbose, + preRollStartFrame=preRollStartFrame + ) if verbose: log.debug("Extracted Alembic to: %s", file) From 0e23615cbd037af3638972bf9694ebaa674989bd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 22:54:45 +0200 Subject: [PATCH 076/192] Enable uvWrite in settings by default --- server_addon/maya/server/settings/publishers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 3a82d649e1..c3983e0067 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -1626,7 +1626,7 @@ DEFAULT_PUBLISH_SETTINGS = { "renderableOnly": False, "stripNamespaces": True, "uvsOnly": False, - "uvWrite": False, + "uvWrite": True, "userAttr": "", "userAttrPrefix": "", "verbose": False, From 3812f229240f7dc7eb4c71979973a355c831a0fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 2 May 2024 10:33:32 +0200 Subject: [PATCH 077/192] implemented 'get_imageio_config_preset' with new profiles handling --- client/ayon_core/pipeline/colorspace.py | 317 ++++++++++++++++++++---- 1 file changed, 272 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index efa3bbf968..8dcbaeb877 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -8,14 +8,19 @@ import tempfile import warnings from copy import deepcopy +import ayon_api + from ayon_core import AYON_CORE_ROOT from ayon_core.settings import get_project_settings from ayon_core.lib import ( + filter_profiles, StringTemplate, run_ayon_launcher_process, - Logger + Logger, ) from ayon_core.pipeline import Anatomy +from ayon_core.pipeline.template_data import get_template_data +from ayon_core.pipeline.load import get_representation_path_with_anatomy from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS @@ -758,6 +763,9 @@ def get_imageio_config( Config path is formatted in `path` key and original settings input is saved into `template` key. + Deprecated: + Deprecated since '0.3.1' . Use `get_imageio_config_preset` instead. + Args: project_name (str): project name host_name (str): host name @@ -768,88 +776,307 @@ def get_imageio_config( Returns: dict: config path data or empty dict - """ - project_settings = project_settings or get_project_settings(project_name) - anatomy = anatomy or Anatomy(project_name) + """ if not anatomy_data: from ayon_core.pipeline.context_tools import ( get_current_context_template_data) anatomy_data = get_current_context_template_data() - formatting_data = deepcopy(anatomy_data) + task_name = anatomy_data["task"]["name"] + folder_path = anatomy_data["folder"]["path"] + return get_imageio_config_preset( + project_name, + folder_path, + task_name, + host_name, + anatomy=anatomy, + project_settings=project_settings, + template_data=anatomy_data, + env=env, + ) - # Add project roots to anatomy data - formatting_data["root"] = anatomy.roots - formatting_data["platform"] = platform.system().lower() + +def _get_global_config_data( + project_name, + host_name, + anatomy, + template_data, + imageio_global, + folder_id, + log, +): + """Get global config data. + + Global config from core settings is using profiles that are based on + host name, task name and task type. The filtered profile can define 3 + types of config sources: + 1. AYON ocio addon configs. + 2. Custom path to ocio config. + 3. Path to 'ocioconfig' representation on product. Name of product can be + defined in settings. Product name can be regex but exact match is + always preferred. + + None is returned when no profile is found, when path + + Args: + project_name (str): Project name. + host_name (str): Host name. + anatomy (Anatomy): Project anatomy object. + template_data (dict[str, Any]): Template data. + imageio_global (dict[str, Any]): Core imagio settings. + folder_id (Union[dict[str, Any], None]): Folder id. + log (logging.Logger): Logger object. + + Returns: + Union[dict[str, str], None]: Config data with path and template + or None. + + """ + task_name = task_type = None + task_data = template_data.get("task") + if task_data: + task_name = task_data["name"] + task_type = task_data["type"] + + filter_values = { + "task_names": task_name, + "task_types": task_type, + "host_names": host_name, + } + profile = filter_profiles( + imageio_global["ocio_config_profiles"], filter_values + ) + if profile is None: + log.info(f"No config profile matched filters {str(filter_values)}") + return None + + profile_type = profile["type"] + if profile_type in ("builtin_path", "custom_path"): + template = profile[profile_type] + result = StringTemplate.format_strict_template( + template, template_data + ) + normalized_path = str(result.normalized()) + if not os.path.exists(normalized_path): + log.warning(f"Path was not found '{normalized_path}'.") + return None + + return { + "path": normalized_path, + "template": template + } + + # TODO decide if this is the right name for representation + repre_name = "ocioconfig" + + folder_info = template_data.get("folder") + if not folder_info: + log.warning("Folder info is missing.") + return None + folder_path = folder_info["path"] + + product_name = profile["product_name"] + if folder_id is None: + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path, fields={"id"} + ) + if not folder_entity: + log.warning(f"Folder entity '{folder_path}' was not found..") + return None + folder_id = folder_entity["id"] + + product_entities_by_name = { + product_entity["name"]: product_entity + for product_entity in ayon_api.get_products( + project_name, + folder_ids={folder_id}, + product_name_regex=product_name, + fields={"id", "name"} + ) + } + if not product_entities_by_name: + log.debug( + f"No product entities were found for folder '{folder_path}' with" + f" product name filter '{product_name}'." + ) + return None + + # Try to use exact match first, otherwise use first available product + product_entity = product_entities_by_name.get(product_name) + if product_entity is None: + product_entity = next(iter(product_entities_by_name.values())) + + product_name = product_entity["name"] + # Find last product version + version_entity = ayon_api.get_last_version_by_product_id( + project_name, + product_id=product_entity["id"], + fields={"id"} + ) + if not version_entity: + log.info( + f"Product '{product_name}' does not have available any versions." + ) + return None + + # Find 'ocioconfig' representation entity + repre_entity = ayon_api.get_representation_by_name( + project_name, + representation_name=repre_name, + version_id=version_entity["id"], + ) + if not repre_entity: + log.debug( + f"Representation '{repre_name}'" + f" not found on product '{product_name}'." + ) + return None + + path = get_representation_path_with_anatomy(repre_entity, anatomy) + template = repre_entity["attrib"]["template"] + return { + "path": path, + "template": template, + } + + +def get_imageio_config_preset( + project_name, + folder_path, + task_name, + host_name, + anatomy=None, + project_settings=None, + template_data=None, + env=None, + folder_id=None, +): + """Returns config data from settings + + Output contains 'path' key and 'template' key holds its template. + + Template data can be prepared with 'get_template_data'. + + Args: + project_name (str): Project name. + folder_path (str): Folder path. + task_name (str): Task name. + host_name (str): Host name. + anatomy (Optional[Anatomy]): Project anatomy object. + project_settings (Optional[dict]): Project settings. + template_data (Optional[dict]): Template data used for + template formatting. + env (Optional[dict]): Environment variables. Environments are used + for template formatting too. Values from 'os.environ' are used + when not provided. + folder_id (Optional[str]): Folder id. Is used only when config path + is received from published representation. Is autofilled when + not provided. + + Returns: + dict: config path data or empty dict + + """ + if not project_settings: + project_settings = get_project_settings(project_name) # Get colorspace settings imageio_global, imageio_host = _get_imageio_settings( - project_settings, host_name) + project_settings, host_name + ) + # Global color management must be enabled to be able to use host settings + if not imageio_global["activate_global_color_management"]: + log.info("Colorspace management is disabled globally.") + return {} # Host 'ocio_config' is optional host_ocio_config = imageio_host.get("ocio_config") or {} - - # Global color management must be enabled to be able to use host settings - activate_color_management = imageio_global.get( - "activate_global_color_management") - # TODO: remove this in future - backward compatibility - # For already saved overrides from previous version look for 'enabled' - # on host settings. - if activate_color_management is None: - activate_color_management = host_ocio_config.get("enabled", False) - - if not activate_color_management: - # if global settings are disabled return empty dict because - # it is expected that no colorspace management is needed - log.info("Colorspace management is disabled globally.") - return {} + # TODO remove + # - backward compatibility when host settings had only 'enabled' flag + # the flag was split into 'activate_global_color_management' + # and 'override_global_config' + host_ocio_config_enabled = host_ocio_config.get("enabled", False) # Check if host settings group is having 'activate_host_color_management' # - if it does not have activation key then default it to True so it uses # global settings - # This is for backward compatibility. - # TODO: in future rewrite this to be more explicit activate_host_color_management = imageio_host.get( - "activate_host_color_management") - - # TODO: remove this in future - backward compatibility + "activate_host_color_management" + ) if activate_host_color_management is None: - activate_host_color_management = host_ocio_config.get("enabled", False) + activate_host_color_management = host_ocio_config_enabled if not activate_host_color_management: # if host settings are disabled return False because # it is expected that no colorspace management is needed log.info( - "Colorspace management for host '{}' is disabled.".format( - host_name) + f"Colorspace management for host '{host_name}' is disabled." ) return {} - # get config path from either global or host settings - # depending on override flag + project_entity = None + if anatomy is None: + project_entity = ayon_api.get_project(project_name) + anatomy = Anatomy(project_name, project_entity) + + if env is None: + env = dict(os.environ.items()) + + if template_data: + template_data = deepcopy(template_data) + else: + if not project_entity: + project_entity = ayon_api.get_project(project_name) + + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + folder_id = folder_entity["id"] + task_entity = ayon_api.get_task_by_name( + project_name, folder_id, task_name + ) + template_data = get_template_data( + project_entity, + folder_entity, + task_entity, + host_name, + project_settings, + ) + + # Add project roots to anatomy data + template_data["root"] = anatomy.roots + template_data["platform"] = platform.system().lower() + + # Add environment variables to template data + template_data.update(env) + + # Get config path from core or host settings + # - based on override flag in host settings # TODO: in future rewrite this to be more explicit override_global_config = host_ocio_config.get("override_global_config") if override_global_config is None: - # for already saved overrides from previous version - # TODO: remove this in future - backward compatibility - override_global_config = host_ocio_config.get("enabled") + override_global_config = host_ocio_config_enabled - if override_global_config: - config_data = _get_config_data( - host_ocio_config["filepath"], formatting_data, env + if not override_global_config: + config_data = _get_global_config_data( + project_name, + host_name, + anatomy, + template_data, + imageio_global, + folder_id, + log, ) else: - # get config path from global - config_global = imageio_global["ocio_config"] config_data = _get_config_data( - config_global["filepath"], formatting_data, env + host_ocio_config["filepath"], template_data, env ) if not config_data: raise FileExistsError( - "No OCIO config found in settings. It is " - "either missing or there is typo in path inputs" + "No OCIO config found in settings. It is" + " either missing or there is typo in path inputs" ) return config_data From 71f2c074c55938672025f6ed7af450a32d3584b8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 2 May 2024 10:58:51 +0200 Subject: [PATCH 078/192] implemented conversion of settings overrides --- server/__init__.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/server/__init__.py b/server/__init__.py index 152cc77218..f6f89f2049 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -1,3 +1,5 @@ +from typing import Any + from ayon_server.addons import BaseServerAddon from .settings import CoreSettings, DEFAULT_VALUES @@ -9,3 +11,46 @@ class CoreAddon(BaseServerAddon): async def get_default_settings(self): settings_model_cls = self.get_settings_model() return settings_model_cls(**DEFAULT_VALUES) + + async def convert_settings_overrides( + self, + source_version: str, + overrides: dict[str, Any], + ) -> dict[str, Any]: + self._convert_imagio_configs_0_3_1(overrides) + # Use super conversion + return await super().convert_settings_overrides( + source_version, overrides + ) + + def _convert_imagio_configs_0_3_1(self, overrides): + """Imageio config settings did change to profiles since 0.3.1. .""" + imageio_overrides = overrides.get("imageio") or {} + if "ocio_config" not in imageio_overrides: + return + + ocio_config = imageio_overrides.pop("ocio_config") + filepath = ocio_config["filepath"] + if not filepath: + return + first_filepath = filepath[0] + ocio_config_profiles = imageio_overrides.setdefault( + "ocio_config_profiles", [] + ) + base_value = { + "type": "builtin_path", + "product": "", + "host_names": [], + "task_names": [], + "task_types": [], + "custom_path": "", + "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio" + } + if first_filepath not in ( + "{BUILTIN_OCIO_ROOT}/aces_1.2/config.oci", + "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", + ): + base_value["type"] = "custom_path" + base_value["custom_path"] = first_filepath + + ocio_config_profiles.append(base_value) From 230611e91c6a89f73b8fc9c80a9414107018a0ac Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 May 2024 15:58:26 +0200 Subject: [PATCH 079/192] AY-4801 - new creator for editorial_pckg Should publish folder with otio file and (.mov) resources. --- .../create/create_editorial_package.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py new file mode 100644 index 0000000000..6a581b59d1 --- /dev/null +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py @@ -0,0 +1,66 @@ +from pathlib import Path + +from ayon_core.pipeline import ( + CreatedInstance, +) + +from ayon_core.lib.attribute_definitions import FileDef +from ayon_core.hosts.traypublisher.api.plugin import TrayPublishCreator + + +class EditorialPackageCreator(TrayPublishCreator): + """Creates instance for OTIO file from published folder. + + Folder contains OTIO file and exported .mov files. Process should publish + whole folder as single `editorial_pckg` product type and (possibly) convert + .mov files into different format and copy them into `publish` `resources` + subfolder. + """ + identifier = "editorial_pckg" + label = "Editorial package" + product_type = "editorial_pckg" + description = "Publish folder with OTIO file and resources" + + # Position batch creator after simple creators + order = 120 + + + def get_icon(self): + return "fa.folder" + + def create(self, product_name, instance_data, pre_create_data): + folder_path = pre_create_data.get("folder_path") + if not folder_path: + return + + instance_data["creator_attributes"] = { + "path": (Path(folder_path["directory"]) / + Path(folder_path["filenames"][0])).as_posix() + } + + # Create new instance + new_instance = CreatedInstance(self.product_type, product_name, + instance_data, self) + self._store_new_instance(new_instance) + + def get_pre_create_attr_defs(self): + # Use same attributes as for instance attributes + return [ + FileDef( + "folder_path", + folders=True, + single_item=True, + extensions=[], + allow_sequences=False, + label="Folder path" + ) + ] + + def get_detail_description(self): + return """# Publish folder with OTIO file and video clips + + Folder contains OTIO file and exported .mov files. Process should + publish whole folder as single `editorial_pckg` product type and + (possibly) convert .mov files into different format and copy them into + `publish` `resources` subfolder. + """ From 4318218881c6d4c0ad0f83bd23395b1c8936f5b7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 May 2024 16:02:56 +0200 Subject: [PATCH 080/192] AY-4801 - new collector for editorial_pckg Collects otio_path and resource_paths from folder. Doesn't parse and collect otio_data yet, to not carry too much data over.(Might be changed) --- .../publish/collect_editorial_package.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py new file mode 100644 index 0000000000..101f58b6d1 --- /dev/null +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py @@ -0,0 +1,58 @@ +"""Produces instance.data["editorial_pckg"] data used during integration. + +Requires: + instance.data["creator_attributes"]["path"] - from creator + +Provides: + instance -> editorial_pckg (dict): + folder_path (str) + otio_path (str) - from dragged folder + resource_paths (list) + +""" +import os + +import pyblish.api + +from ayon_core.lib.transcoding import VIDEO_EXTENSIONS + + +class CollectEditorialPackage(pyblish.api.InstancePlugin): + """Collects path to OTIO file and resources""" + + label = "Collect Editorial Package" + order = pyblish.api.CollectorOrder - 0.1 + + hosts = ["traypublisher"] + families = ["editorial_pckg"] + + def process(self, instance): + folder_path = instance.data["creator_attributes"].get("path") + if not folder_path or not os.path.exists(folder_path): + self.log.info(( + "Instance doesn't contain collected existing folder path." + )) + return + + instance.data["editorial_pckg"] = {} + instance.data["editorial_pckg"]["folder_path"] = folder_path + + otio_path, resource_paths = ( + self._get_otio_and_resource_paths(folder_path)) + + instance.data["editorial_pckg"]["otio_path"] = otio_path + instance.data["editorial_pckg"]["resource_paths"] = resource_paths + + def _get_otio_and_resource_paths(self, folder_path): + otio_path = None + resource_paths = [] + + file_names = os.listdir(folder_path) + for filename in file_names: + _, ext = os.path.splitext(filename) + file_path = os.path.join(folder_path, filename) + if ext == ".otio": + otio_path = file_path + elif ext in VIDEO_EXTENSIONS: + resource_paths.append(file_path) + return otio_path, resource_paths From 1ff4d63091fbcbdcabc434317181fd19f00a916d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 May 2024 16:11:12 +0200 Subject: [PATCH 081/192] AY-4801 - new validator for editorial_pckg Currently checks only by file names and expects flat structure. It ignores path to resources in otio file as folder might be dragged in and published from different location than it was created. --- .../publish/validate_editorial_package.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py new file mode 100644 index 0000000000..869dc73811 --- /dev/null +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py @@ -0,0 +1,70 @@ +import os +import opentimelineio + +import pyblish.api +from ayon_core.pipeline import PublishValidationError + + +class ValidateEditorialPackage(pyblish.api.InstancePlugin): + """Checks that published folder contains all resources from otio + + Currently checks only by file names and expects flat structure. + It ignores path to resources in otio file as folder might be dragged in and + published from different location than it was created. + """ + + label = "Validate Editorial Package" + order = pyblish.api.ValidatorOrder - 0.49 + + hosts = ["traypublisher"] + families = ["editorial_pckg"] + + def process(self, instance): + editorial_pckg_data = instance.data.get("editorial_pckg") + if not editorial_pckg_data: + raise PublishValidationError( + f"Editorial package not collected") + + folder_path = editorial_pckg_data["folder_path"] + + otio_path = editorial_pckg_data["otio_path"] + if not otio_path: + raise PublishValidationError( + f"Folder {folder_path} missing otio file") + + resource_paths = editorial_pckg_data["resource_paths"] + + resource_file_names = {os.path.basename(path) + for path in resource_paths} + + otio_data = opentimelineio.adapters.read_from_file(otio_path) + + target_urls = self._get_all_target_urls(otio_data) + missing_files = set() + for target_url in target_urls: + target_basename = os.path.basename(target_url) + if target_basename not in resource_file_names: + missing_files.add(target_basename) + + if missing_files: + raise PublishValidationError("Otio file contains missing files " + f"'{missing_files}'.") + + instance.data["editorial_pckg"]["otio_data"] = otio_data + + def _get_all_target_urls(self, otio_data): + target_urls = [] + + # Iterate through tracks, clips, or other elements + for track in otio_data.tracks: + for clip in track: + # Check if the clip has a media reference + if clip.media_reference is not None: + # Access the target_url from the media reference + target_url = clip.media_reference.target_url + if target_url: + target_urls.append(target_url) + + return target_urls + + From 99e5cf99602c3b2321445776bf63e6cefe85a19c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 May 2024 16:49:23 +0200 Subject: [PATCH 082/192] Tweak the validation report --- .../validate_alembic_options_defaults.py | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 476f837135..c19bc6a0f4 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -1,3 +1,4 @@ +import inspect import pyblish.api from ayon_core.pipeline import OptionalPyblishPluginMixin @@ -39,11 +40,11 @@ class ValidateAlembicDefaultsPointcache( attributes = self._get_publish_attributes(instance) msg = ( - "Alembic Extract setting \"{}\" is not the default value:" - "\nCurrent: {}" - "\nDefault Value: {}\n" + "Alembic extract setting \"{}\" is not the default value:" + "\n- Current: {}" + "\n- Default: {}\n" ) - errors = [] + invalid = False for key, value in attributes.items(): default_value = settings[key] @@ -54,10 +55,32 @@ class ValidateAlembicDefaultsPointcache( default_value = sorted(default_value) if value != default_value: - errors.append(msg.format(key, value, default_value)) + self.log.error( + msg.format(key, value, default_value) + ) + invalid = True - if errors: - raise PublishValidationError("\n".join(errors)) + if invalid: + raise PublishValidationError( + "Detected alembic options that differ from the default value.", + description=self.get_description() + ) + + @staticmethod + def get_description(): + return inspect.cleandoc( + """### Alembic Extract settings differ from defaults + + The alembic export options differ from the project default values. + + If this is intentional you can disable this validation by + disabling **Validate Alembic Options Default**. + + If not you may use the "Repair" action to revert all the options to + their default values. + + """ + ) @classmethod def repair(cls, instance): From 4ac8123cb92c0220781b5023b0616d01ce68e042 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 May 2024 16:57:17 +0200 Subject: [PATCH 083/192] Simplify messaging --- .../validate_alembic_options_defaults.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index c19bc6a0f4..800c05c8a6 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -39,12 +39,7 @@ class ValidateAlembicDefaultsPointcache( settings = self._get_settings(instance.context) attributes = self._get_publish_attributes(instance) - msg = ( - "Alembic extract setting \"{}\" is not the default value:" - "\n- Current: {}" - "\n- Default: {}\n" - ) - invalid = False + invalid = {} for key, value in attributes.items(): default_value = settings[key] @@ -55,14 +50,17 @@ class ValidateAlembicDefaultsPointcache( default_value = sorted(default_value) if value != default_value: - self.log.error( - msg.format(key, value, default_value) - ) - invalid = True + invalid[key] = value, default_value if invalid: + non_defaults = "\n".join( + f"- {key}: {value} \t(default: {default_value})" + for key, (value, default_value) in invalid.items() + ) + raise PublishValidationError( - "Detected alembic options that differ from the default value.", + "Alembic extract options differ from default values:\n" + f"{non_defaults}", description=self.get_description() ) From b4b1e2af4cab5b442ce84592f97b9c27b51b16b3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 May 2024 17:02:26 +0200 Subject: [PATCH 084/192] Ignore attributes not found in settings with a warning --- .../publish/validate_alembic_options_defaults.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 800c05c8a6..0abb734c9b 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -41,6 +41,17 @@ class ValidateAlembicDefaultsPointcache( invalid = {} for key, value in attributes.items(): + if key not in settings: + # This may occur if attributes have changed over time and an + # existing instance has older legacy attributes that do not + # match the current settings definition. + self.log.warning( + "Publish attribute %s not found in Alembic Export " + "default settings. Ignoring validation for attribute.", + key + ) + continue + default_value = settings[key] # Lists are best to compared sorted since we cant rely on the order From 20dad59947e4fb13d026670baa73820e9e378ecd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 May 2024 18:31:40 +0200 Subject: [PATCH 085/192] AY-4801 - added editorial_pckg to integrate --- client/ayon_core/plugins/publish/integrate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 764168edd3..5a9d8eae2b 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -169,6 +169,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "yeticacheUE", "tycache", "csv_ingest_file", + "editorial_pckg" ] default_template_name = "publish" From 345f5f31f1a395c4f4d468166bc343933be9974e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 May 2024 18:44:48 +0200 Subject: [PATCH 086/192] AY-4801 - added editorial_pckg extractor Modifies otio file with rootless publish paths, prepares for integration. --- .../plugins/publish/extract_editorial_pckg.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py new file mode 100644 index 0000000000..dc8163e1ff --- /dev/null +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -0,0 +1,122 @@ +import os.path +import opentimelineio + +import pyblish.api + +from ayon_core.pipeline import publish + + +class ExtractEditorialPackage(publish.Extractor): + """Replaces movie paths in otio file with publish rootless + + Prepares movie resources for integration. + TODO introduce conversion to .mp4 + """ + + label = "Extract Editorial Package" + order = pyblish.api.ExtractorOrder - 0.45 + hosts = ["traypublisher"] + families = ["editorial_pckg"] + + def process(self, instance): + editorial_pckg_data = instance.data.get("editorial_pckg") + + otio_path = editorial_pckg_data["otio_path"] + otio_basename = os.path.basename(otio_path) + staging_dir = self.staging_dir(instance) + + editorial_pckg_repre = { + 'name': "editorial_pckg", + 'ext': "otio", + 'files': otio_basename, + "stagingDir": staging_dir, + } + otio_staging_path = os.path.join(staging_dir, otio_basename) + + instance.data["representations"].append(editorial_pckg_repre) + + publish_path = self._get_published_path(instance) + publish_folder = os.path.dirname(publish_path) + publish_resource_folder = os.path.join(publish_folder, "resources") + + resource_paths = editorial_pckg_data["resource_paths"] + transfers = self._get_transfers(resource_paths, + publish_resource_folder) + if not "transfers" in instance.data: + instance.data["transfers"] = [] + instance.data["transfers"] = transfers + + source_to_rootless = self._get_resource_path_mapping(instance, + transfers) + + otio_data = editorial_pckg_data["otio_data"] + otio_data = self._replace_target_urls(otio_data, source_to_rootless) + + opentimelineio.adapters.write_to_file(otio_data, otio_staging_path) + + self.log.info("Added Editorial Package representation: {}".format( + editorial_pckg_repre)) + + def _get_resource_path_mapping(self, instance, transfers): + """Returns dict of {source_mov_path: rootless_published_path}.""" + replace_paths = {} + anatomy = instance.context.data["anatomy"] + for source, destination in transfers: + rootless_path = self._get_rootless(anatomy, destination) + source_file_name = os.path.basename(source) + replace_paths[source_file_name] = rootless_path + return replace_paths + + def _get_transfers(self, resource_paths, publish_resource_folder): + """Returns list of tuples (source, destination) movie paths.""" + transfers = [] + for res_path in resource_paths: + res_basename = os.path.basename(res_path) + pub_res_path = os.path.join(publish_resource_folder, res_basename) + transfers.append((res_path, pub_res_path)) + return transfers + + def _replace_target_urls(self, otio_data, replace_paths): + """Replace original movie paths with published rootles ones.""" + for track in otio_data.tracks: + for clip in track: + # Check if the clip has a media reference + if clip.media_reference is not None: + # Access the target_url from the media reference + target_url = clip.media_reference.target_url + if not target_url: + continue + file_name = os.path.basename(target_url) + replace_value = replace_paths.get(file_name) + if replace_value: + clip.media_reference.target_url = replace_value + + return otio_data + + def _get_rootless(self, anatomy, path): + """Try to find rootless {root[work]} path from `path`""" + success, rootless_path = anatomy.find_root_template_from_path( + path) + if not success: + # `rootless_path` is not set to `output_dir` if none of roots match + self.log.warning( + f"Could not find root path for remapping '{path}'." + ) + rootless_path = path + + return rootless_path + + def _get_published_path(self, instance): + """Calculates expected `publish` folder""" + # determine published path from Anatomy. + template_data = instance.data.get("anatomyData") + rep = instance.data["representations"][0] + template_data["representation"] = rep.get("name") + template_data["ext"] = rep.get("ext") + template_data["comment"] = None + + anatomy = instance.context.data["anatomy"] + template_data["root"] = anatomy.roots + template = anatomy.get_template_item("publish", "default", "path") + template_filled = template.format_strict(template_data) + return os.path.normpath(template_filled) From 5e0ab289293b37a195c3c59bf42221c4e04e823d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:04:36 +0200 Subject: [PATCH 087/192] renamed 'tools__get_config_data_name' to '_get_host_config_data' --- client/ayon_core/pipeline/colorspace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 8dcbaeb877..f17e9d5f7b 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1069,7 +1069,7 @@ def get_imageio_config_preset( log, ) else: - config_data = _get_config_data( + config_data = _get_host_config_data( host_ocio_config["filepath"], template_data, env ) @@ -1082,7 +1082,7 @@ def get_imageio_config_preset( return config_data -def _get_config_data(path_list, anatomy_data, env=None): +def _get_host_config_data(path_list, anatomy_data, env=None): """Return first existing path in path list. If template is used in path inputs, From 1589ee5c0e9ccf35e723d7f703fad4118470c8bc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:05:01 +0200 Subject: [PATCH 088/192] simplified '_get_host_config_data' --- client/ayon_core/pipeline/colorspace.py | 43 ++++++++++--------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index f17e9d5f7b..595c50606c 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1082,39 +1082,30 @@ def get_imageio_config_preset( return config_data -def _get_host_config_data(path_list, anatomy_data, env=None): +def _get_host_config_data(templates, template_data): """Return first existing path in path list. - If template is used in path inputs, - then it is formatted by anatomy data - and environment variables + Use template data to fill possible formatting in paths. Args: - path_list (list[str]): list of abs paths - anatomy_data (dict): formatting data - env (Optional[dict]): Environment variables. + templates (list[str]): List of templates to config paths. + template_data (dict): Template data used to format templates. Returns: - dict: config data + Union[dict, None]: Config data or 'None' if templates are empty + or any path exists. + """ - formatting_data = deepcopy(anatomy_data) - - environment_vars = env or dict(**os.environ) - - # format the path for potential env vars - formatting_data.update(environment_vars) - - # first try host config paths - for path_ in path_list: - formatted_path = _format_path(path_, formatting_data) - - if not os.path.exists(formatted_path): - continue - - return { - "path": os.path.normpath(formatted_path), - "template": path_ - } + for template in templates: + formatted_path = StringTemplate.format_strict_template( + template, template_data + ) + path = os.path.abspath(formatted_path) + if os.path.exists(path): + return { + "path": os.path.normpath(path), + "template": template + } def _format_path(template_path, formatting_data): From aaeaa1e7f0c6c977716b5e240b3610090d32190c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:10:14 +0200 Subject: [PATCH 089/192] removed unused '_format_path' --- client/ayon_core/pipeline/colorspace.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 595c50606c..363012dad5 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1108,24 +1108,6 @@ def _get_host_config_data(templates, template_data): } -def _format_path(template_path, formatting_data): - """Single template path formatting. - - Args: - template_path (str): template string - formatting_data (dict): data to be used for - template formatting - - Returns: - str: absolute formatted path - """ - # format path for anatomy keys - formatted_path = StringTemplate(template_path).format( - formatting_data) - - return os.path.abspath(formatted_path) - - def get_imageio_file_rules(project_name, host_name, project_settings=None): """Get ImageIO File rules from project settings From c6b6ca1e3cfbb3d400b65ec3ead9d5bd5518650b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:10:36 +0200 Subject: [PATCH 090/192] use safer option to format template path --- client/ayon_core/pipeline/colorspace.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 363012dad5..1ab93c7844 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1097,9 +1097,12 @@ def _get_host_config_data(templates, template_data): """ for template in templates: - formatted_path = StringTemplate.format_strict_template( + formatted_path = StringTemplate.format_template( template, template_data ) + if not formatted_path.solved: + continue + path = os.path.abspath(formatted_path) if os.path.exists(path): return { From bc01bf08f2a45339b476af61094904551a73a12b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:50:00 +0200 Subject: [PATCH 091/192] updated 'get_colorspace_settings_from_publish_context' to use new function --- client/ayon_core/pipeline/colorspace.py | 30 ++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 1ab93c7844..5793fd1143 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1236,27 +1236,41 @@ def get_colorspace_settings_from_publish_context(context_data): Returns: tuple | bool: config, file rules or None + """ if "imageioSettings" in context_data and context_data["imageioSettings"]: return context_data["imageioSettings"] project_name = context_data["projectName"] + folder_path = context_data["folderPath"] + task_name = context_data["task"] host_name = context_data["hostName"] - anatomy_data = context_data["anatomyData"] - project_settings_ = context_data["project_settings"] + anatomy = context_data["anatomy"] + template_data = context_data["anatomyData"] + project_settings = context_data["project_settings"] + folder_id = None + folder_entity = context_data.get("folderEntity") + if folder_entity: + folder_id = folder_entity["id"] - config_data = get_imageio_config( - project_name, host_name, - project_settings=project_settings_, - anatomy_data=anatomy_data + config_data = get_imageio_config_preset( + project_name, + folder_path, + task_name, + host_name, + anatomy=anatomy, + project_settings=project_settings, + template_data=template_data, + folder_id=folder_id, ) # caching invalid state, so it's not recalculated all the time file_rules = None if config_data: file_rules = get_imageio_file_rules( - project_name, host_name, - project_settings=project_settings_ + project_name, + host_name, + project_settings=project_settings ) # caching settings for future instance processing From 178e30d8e7fdba1012908f1f7574e824ab03055a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:52:31 +0200 Subject: [PATCH 092/192] fix call of '_get_host_config_data' --- 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 5793fd1143..906f9f96fa 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1070,7 +1070,7 @@ def get_imageio_config_preset( ) else: config_data = _get_host_config_data( - host_ocio_config["filepath"], template_data, env + host_ocio_config["filepath"], template_data ) if not config_data: From 571658b1290c69b9444ecaabc35247722dd15aca Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:53:12 +0200 Subject: [PATCH 093/192] make context optional --- client/ayon_core/pipeline/colorspace.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 906f9f96fa..a715651f4a 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -783,8 +783,8 @@ def get_imageio_config( get_current_context_template_data) anatomy_data = get_current_context_template_data() - task_name = anatomy_data["task"]["name"] - folder_path = anatomy_data["folder"]["path"] + task_name = anatomy_data.get("task", {}).get("name") + folder_path = anatomy_data.get("folder", {}).get("path") return get_imageio_config_preset( project_name, folder_path, @@ -1029,13 +1029,17 @@ def get_imageio_config_preset( if not project_entity: project_entity = ayon_api.get_project(project_name) - folder_entity = ayon_api.get_folder_by_path( - project_name, folder_path - ) - folder_id = folder_entity["id"] - task_entity = ayon_api.get_task_by_name( - project_name, folder_id, task_name - ) + folder_entity = task_entity = folder_id = None + if folder_path: + folder_entity = ayon_api.get_folder_by_path( + project_name, folder_path + ) + folder_id = folder_entity["id"] + + if folder_id and task_name: + task_entity = ayon_api.get_task_by_name( + project_name, folder_id, task_name + ) template_data = get_template_data( project_entity, folder_entity, From cd857753ae6f24bf066514ecaf26d32bed827217 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:54:00 +0200 Subject: [PATCH 094/192] 'config_data' are now required in other functions --- client/ayon_core/pipeline/colorspace.py | 86 ++++++++++++++----------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index a715651f4a..9e33b2e531 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -126,42 +126,48 @@ def get_ocio_config_script_path(): def get_colorspace_name_from_filepath( - filepath, host_name, project_name, - config_data=None, file_rules=None, + filepath, + host_name, + project_name, + config_data, + file_rules=None, project_settings=None, validate=True ): """Get colorspace name from filepath Args: - filepath (str): path string, file rule pattern is tested on it - host_name (str): host name - project_name (str): project name - config_data (Optional[dict]): config path and template in dict. - Defaults to None. - file_rules (Optional[dict]): file rule data from settings. - Defaults to None. - project_settings (Optional[dict]): project settings. Defaults to None. + filepath (str): Path string, file rule pattern is tested on it. + host_name (str): Host name. + project_name (str): Project name. + config_data (dict): Config path and template in dict. + file_rules (Optional[dict]): File rule data from settings. + project_settings (Optional[dict]): Project settings. validate (Optional[bool]): should resulting colorspace be validated - with config file? Defaults to True. + with config file? Defaults to True. Returns: - str: name of colorspace - """ - project_settings, config_data, file_rules = _get_context_settings( - host_name, project_name, - config_data=config_data, file_rules=file_rules, - project_settings=project_settings - ) + Union[str, None]: name of colorspace + """ if not config_data: # in case global or host color management is not enabled return None + if file_rules is None: + if project_settings is None: + project_settings = get_project_settings(project_name) + file_rules = get_imageio_file_rules( + project_name, host_name, project_settings + ) + # use ImageIO file rules colorspace_name = get_imageio_file_rules_colorspace_from_filepath( - filepath, host_name, project_name, - config_data=config_data, file_rules=file_rules, + filepath, + host_name, + project_name, + config_data=config_data, + file_rules=file_rules, project_settings=project_settings ) @@ -187,7 +193,8 @@ def get_colorspace_name_from_filepath( # validate matching colorspace with config if validate: validate_imageio_colorspace_in_config( - config_data["path"], colorspace_name) + config_data["path"], colorspace_name + ) return colorspace_name @@ -226,8 +233,11 @@ def _get_context_settings( def get_imageio_file_rules_colorspace_from_filepath( - filepath, host_name, project_name, - config_data=None, file_rules=None, + filepath, + host_name, + project_name, + config_data, + file_rules=None, project_settings=None ): """Get colorspace name from filepath @@ -235,28 +245,28 @@ def get_imageio_file_rules_colorspace_from_filepath( ImageIO Settings file rules are tested for matching rule. Args: - filepath (str): path string, file rule pattern is tested on it - host_name (str): host name - project_name (str): project name - config_data (Optional[dict]): config path and template in dict. - Defaults to None. - file_rules (Optional[dict]): file rule data from settings. - Defaults to None. - project_settings (Optional[dict]): project settings. Defaults to None. + filepath (str): Path string, file rule pattern is tested on it. + host_name (str): Host name. + project_name (str): Project name. + config_data (dict): Config path and template in dict. + file_rules (Optional[dict]): File rule data from settings. + project_settings (Optional[dict]): Project settings. Returns: - str: name of colorspace - """ - project_settings, config_data, file_rules = _get_context_settings( - host_name, project_name, - config_data=config_data, file_rules=file_rules, - project_settings=project_settings - ) + Union[str, None]: Name of colorspace. + """ if not config_data: # in case global or host color management is not enabled return None + if file_rules is None: + if project_settings is None: + project_settings = get_project_settings(project_name) + file_rules = get_imageio_file_rules( + project_name, host_name, project_settings + ) + # match file rule from path colorspace_name = None for file_rule in file_rules: From 5d6993d1112c7e24285f43b78ae68193abe29e7b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:54:18 +0200 Subject: [PATCH 095/192] removed unused '_get_context_settings' --- client/ayon_core/pipeline/colorspace.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 9e33b2e531..0e745b625f 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -210,28 +210,6 @@ def get_colorspace_from_filepath(*args, **kwargs): return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs) -def _get_context_settings( - host_name, project_name, - config_data=None, file_rules=None, - project_settings=None -): - project_settings = project_settings or get_project_settings( - project_name - ) - - config_data = config_data or get_imageio_config( - project_name, host_name, project_settings) - - # in case host color management is not enabled - if not config_data: - return (None, None, None) - - file_rules = file_rules or get_imageio_file_rules( - project_name, host_name, project_settings) - - return project_settings, config_data, file_rules - - def get_imageio_file_rules_colorspace_from_filepath( filepath, host_name, From 1586b316c8ab700b1f9e42dc57e813454010356a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:54:47 +0200 Subject: [PATCH 096/192] implemented function for current context --- client/ayon_core/pipeline/colorspace.py | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 0e745b625f..e0fa613ae8 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -23,6 +23,7 @@ from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.load import get_representation_path_with_anatomy from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS +from .context_tools import get_current_context, get_current_host_name log = Logger.get_logger(__name__) @@ -1402,3 +1403,37 @@ def get_display_view_colorspace_subprocess(config_path, display, view): # return default view colorspace name with open(tmp_json_path, "r") as f: return json.load(f) + + +# --- Current context functions --- +def get_current_context_imageio_config_preset( + anatomy=None, + project_settings=None, + template_data=None, + env=None, +): + """Get ImageIO config preset for current context. + + Args: + anatomy (Optional[Anatomy]): Current project anatomy. + project_settings (Optional[dict[str, Any]]): Current project settings. + template_data (Optional[dict[str, Any]]): Prepared template data + for current context. + env (Optional[dict[str, str]]): Custom environment variable values. + + Returns: + dict: ImageIO config preset. + + """ + context = get_current_context() + host_name = get_current_host_name() + return get_imageio_config_preset( + context["project_name"], + context["folder_path"], + context["task_name"], + host_name, + anatomy=anatomy, + project_settings=project_settings, + template_data=template_data, + env=env, + ) From 3d1fa6471cbf8be68906a94aacea80bc90dc0a53 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 15:59:50 +0200 Subject: [PATCH 097/192] use 'get_current_context_imageio_config_preset' in host integrations --- client/ayon_core/hosts/hiero/api/lib.py | 5 +--- client/ayon_core/hosts/max/api/lib.py | 11 ++------ .../hosts/maya/plugins/load/load_image.py | 5 ++-- .../plugins/create/create_colorspace_look.py | 7 +----- .../publish/collect_explicit_colorspace.py | 25 +++++++++---------- 5 files changed, 18 insertions(+), 35 deletions(-) diff --git a/client/ayon_core/hosts/hiero/api/lib.py b/client/ayon_core/hosts/hiero/api/lib.py index aaf99546c7..456a68f125 100644 --- a/client/ayon_core/hosts/hiero/api/lib.py +++ b/client/ayon_core/hosts/hiero/api/lib.py @@ -1110,10 +1110,7 @@ def apply_colorspace_project(): ''' # backward compatibility layer # TODO: remove this after some time - config_data = get_imageio_config( - project_name=get_current_project_name(), - host_name="hiero" - ) + config_data = get_current_context_imageio_config_preset() if config_data: presets.update({ diff --git a/client/ayon_core/hosts/max/api/lib.py b/client/ayon_core/hosts/max/api/lib.py index d9a3af3336..4170a992a5 100644 --- a/client/ayon_core/hosts/max/api/lib.py +++ b/client/ayon_core/hosts/max/api/lib.py @@ -372,12 +372,8 @@ def reset_colorspace(): """ if int(get_max_version()) < 2024: return - project_name = get_current_project_name() - colorspace_mgr = rt.ColorPipelineMgr - project_settings = get_project_settings(project_name) - max_config_data = colorspace.get_imageio_config( - project_name, "max", project_settings) + max_config_data = colorspace.get_current_context_imageio_config_preset() if max_config_data: ocio_config_path = max_config_data["path"] colorspace_mgr = rt.ColorPipelineMgr @@ -392,10 +388,7 @@ def check_colorspace(): "because Max main window can't be found.") if int(get_max_version()) >= 2024: color_mgr = rt.ColorPipelineMgr - project_name = get_current_project_name() - project_settings = get_project_settings(project_name) - max_config_data = colorspace.get_imageio_config( - project_name, "max", project_settings) + max_config_data = colorspace.get_current_context_imageio_config_preset() if max_config_data and color_mgr.Mode != rt.Name("OCIO_Custom"): if not is_headless(): from ayon_core.tools.utils import SimplePopup 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 5b0858ce70..171920f747 100644 --- a/client/ayon_core/hosts/maya/plugins/load/load_image.py +++ b/client/ayon_core/hosts/maya/plugins/load/load_image.py @@ -8,7 +8,7 @@ from ayon_core.pipeline import ( from ayon_core.pipeline.load.utils import get_representation_path_from_context from ayon_core.pipeline.colorspace import ( get_imageio_file_rules_colorspace_from_filepath, - get_imageio_config, + get_current_context_imageio_config_preset, get_imageio_file_rules ) from ayon_core.settings import get_project_settings @@ -270,8 +270,7 @@ class FileNodeLoader(load.LoaderPlugin): host_name = get_current_host_name() project_settings = get_project_settings(project_name) - config_data = get_imageio_config( - project_name, host_name, + config_data = get_current_context_imageio_config_preset( project_settings=project_settings ) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py index 4d865c1c5c..da05afe86b 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py @@ -156,14 +156,9 @@ This creator publishes color space look file (LUT). ] def apply_settings(self, project_settings): - host = self.create_context.host - host_name = host.name - project_name = host.get_current_project_name() - config_data = colorspace.get_imageio_config( - project_name, host_name, + config_data = colorspace.get_current_context_imageio_config_preset( project_settings=project_settings ) - if not config_data: self.enabled = False return diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_explicit_colorspace.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_explicit_colorspace.py index 8e29a0048d..5fbb9a6f4c 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_explicit_colorspace.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_explicit_colorspace.py @@ -1,10 +1,7 @@ import pyblish.api -from ayon_core.pipeline import ( - publish, - registered_host -) from ayon_core.lib import EnumDef from ayon_core.pipeline import colorspace +from ayon_core.pipeline import publish from ayon_core.pipeline.publish import KnownPublishError @@ -19,9 +16,10 @@ class CollectColorspace(pyblish.api.InstancePlugin, families = ["render", "plate", "reference", "image", "online"] enabled = False - colorspace_items = [ + default_colorspace_items = [ (None, "Don't override") ] + colorspace_items = list(default_colorspace_items) colorspace_attr_show = False config_items = None @@ -69,14 +67,13 @@ class CollectColorspace(pyblish.api.InstancePlugin, @classmethod def apply_settings(cls, project_settings): - host = registered_host() - host_name = host.name - project_name = host.get_current_project_name() - config_data = colorspace.get_imageio_config( - project_name, host_name, + config_data = colorspace.get_current_context_imageio_config_preset( project_settings=project_settings ) + enabled = False + colorspace_items = list(cls.default_colorspace_items) + config_items = None if config_data: filepath = config_data["path"] config_items = colorspace.get_ocio_config_colorspaces(filepath) @@ -85,9 +82,11 @@ class CollectColorspace(pyblish.api.InstancePlugin, include_aliases=True, include_roles=True ) - cls.config_items = config_items - cls.colorspace_items.extend(labeled_colorspaces) - cls.enabled = True + colorspace_items.extend(labeled_colorspaces) + + cls.config_items = config_items + cls.colorspace_items = colorspace_items + cls.enabled = enabled @classmethod def get_attribute_defs(cls): From f827dc2060488de74421cd7d0b8fc826a499975e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 16:55:59 +0200 Subject: [PATCH 098/192] use new function in pre launch hook --- client/ayon_core/hooks/pre_ocio_hook.py | 52 ++++++++++++++----------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index 0817afec71..6c30b267bc 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -1,7 +1,7 @@ from ayon_applications import PreLaunchHook -from ayon_core.pipeline.colorspace import get_imageio_config -from ayon_core.pipeline.template_data import get_template_data_with_names +from ayon_core.pipeline.colorspace import get_imageio_config_preset +from ayon_core.pipeline.template_data import get_template_data class OCIOEnvHook(PreLaunchHook): @@ -26,32 +26,38 @@ class OCIOEnvHook(PreLaunchHook): def execute(self): """Hook entry method.""" - template_data = get_template_data_with_names( - project_name=self.data["project_name"], - folder_path=self.data["folder_path"], - task_name=self.data["task_name"], + folder_entity = self.data["folder_entity"] + + template_data = get_template_data( + self.data["project_entity"], + folder_entity=folder_entity, + task_entity=self.data["task_entity"], host_name=self.host_name, - settings=self.data["project_settings"] + settings=self.data["project_settings"], ) - config_data = get_imageio_config( - project_name=self.data["project_name"], - host_name=self.host_name, - project_settings=self.data["project_settings"], - anatomy_data=template_data, + config_data = get_imageio_config_preset( + self.data["project_name"], + self.data["folder_path"], + self.data["task_name"], + self.host_name, anatomy=self.data["anatomy"], + project_settings=self.data["project_settings"], + template_data=template_data, env=self.launch_context.env, + folder_id=folder_entity["id"], ) - if config_data: - ocio_path = config_data["path"] - - if self.host_name in ["nuke", "hiero"]: - ocio_path = ocio_path.replace("\\", "/") - - self.log.info( - f"Setting OCIO environment to config path: {ocio_path}") - - self.launch_context.env["OCIO"] = ocio_path - else: + if not config_data: self.log.debug("OCIO not set or enabled") + return + + ocio_path = config_data["path"] + + if self.host_name in ["nuke", "hiero"]: + ocio_path = ocio_path.replace("\\", "/") + + self.log.info( + f"Setting OCIO environment to config path: {ocio_path}") + + self.launch_context.env["OCIO"] = ocio_path From 5a43242bda200a79454fbef579e00156c29fd144 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 16:56:43 +0200 Subject: [PATCH 099/192] use 'get_current_context_imageio_config_preset' in nuke --- client/ayon_core/hosts/nuke/api/lib.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index e3505a16f2..0a4755c166 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -43,7 +43,9 @@ from ayon_core.pipeline import ( from ayon_core.pipeline.context_tools import ( get_current_context_custom_workfile_template ) -from ayon_core.pipeline.colorspace import get_imageio_config +from ayon_core.pipeline.colorspace import ( + get_current_context_imageio_config_preset +) from ayon_core.pipeline.workfile import BuildWorkfile from . import gizmo_menu from .constants import ASSIST @@ -1552,10 +1554,7 @@ class WorkfileSettings(object): imageio_host (dict): host colorspace configurations ''' - config_data = get_imageio_config( - project_name=get_current_project_name(), - host_name="nuke" - ) + config_data = get_current_context_imageio_config_preset() workfile_settings = imageio_host["workfile"] color_management = workfile_settings["color_management"] From 1568d40c98c07919dc90fdffc85f1c9a39af59c8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 16:57:03 +0200 Subject: [PATCH 100/192] temp json fole wrapper is safe --- client/ayon_core/pipeline/colorspace.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index e0fa613ae8..e985bdfcf5 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -87,28 +87,25 @@ def deprecated(new_destination): def _make_temp_json_file(): """Wrapping function for json temp file """ + temporary_json_file = None try: # Store dumped json to temporary file - temporary_json_file = tempfile.NamedTemporaryFile( + with tempfile.NamedTemporaryFile( mode="w", suffix=".json", delete=False - ) - temporary_json_file.close() - temporary_json_filepath = temporary_json_file.name.replace( - "\\", "/" - ) + ) as tmpfile: + temporary_json_filepath = tmpfile.name.replace("\\", "/") yield temporary_json_filepath - except IOError as _error: + except IOError as exc: raise IOError( - "Unable to create temp json file: {}".format( - _error - ) + "Unable to create temp json file: {}".format(exc) ) finally: # Remove the temporary json - os.remove(temporary_json_filepath) + if temporary_json_file is not None: + os.remove(temporary_json_filepath) def get_ocio_config_script_path(): From c0d5e77177463ca075ee1fed2e9c61416dd6196d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 16:57:27 +0200 Subject: [PATCH 101/192] simplified 'get_ocio_config_script_path' --- client/ayon_core/pipeline/colorspace.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index e985bdfcf5..cbd63c851f 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -113,13 +113,12 @@ def get_ocio_config_script_path(): Returns: str: path string + """ - return os.path.normpath( - os.path.join( - AYON_CORE_ROOT, - "scripts", - "ocio_wrapper.py" - ) + return os.path.join( + os.path.normpath(AYON_CORE_ROOT), + "scripts", + "ocio_wrapper.py" ) From 0c84c32e15ce1732139afaef5533c8f16e8f0c78 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 18:00:09 +0200 Subject: [PATCH 102/192] pass config data to 'get_imageio_file_rules_colorspace_from_filepath' in nuke loader --- client/ayon_core/hosts/nuke/plugins/load/load_clip.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index df8f2ab018..1f707c25cf 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -9,7 +9,8 @@ from ayon_core.pipeline import ( get_representation_path, ) from ayon_core.pipeline.colorspace import ( - get_imageio_file_rules_colorspace_from_filepath + get_imageio_file_rules_colorspace_from_filepath, + get_current_context_imageio_config_preset, ) from ayon_core.hosts.nuke.api.lib import ( get_imageio_input_colorspace, @@ -547,9 +548,10 @@ class LoadClip(plugin.NukeLoader): f"Colorspace from representation colorspaceData: {colorspace}" ) + config_data = get_current_context_imageio_config_preset() # check if any filerules are not applicable new_parsed_colorspace = get_imageio_file_rules_colorspace_from_filepath( # noqa - filepath, "nuke", project_name + filepath, "nuke", project_name, config_data=config_data ) self.log.debug(f"Colorspace new filerules: {new_parsed_colorspace}") From 5599d773c7f49f4ecee35a6cca1a0c1186b92a2e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 18:02:21 +0200 Subject: [PATCH 103/192] use 'get_imageio_file_rules_colorspace_from_filepath' instead of 'get_imageio_colorspace_from_filepath' --- client/ayon_core/pipeline/colorspace.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index cbd63c851f..7b0d4c8491 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1328,12 +1328,15 @@ def set_colorspace_data_to_representation( filename = filename[0] # get matching colorspace from rules - colorspace = colorspace or get_imageio_colorspace_from_filepath( - filename, host_name, project_name, - config_data=config_data, - file_rules=file_rules, - project_settings=project_settings - ) + if colorspace is None: + colorspace = get_imageio_file_rules_colorspace_from_filepath( + filename, + host_name, + project_name, + config_data=config_data, + file_rules=file_rules, + project_settings=project_settings + ) # infuse data to representation if colorspace: From bf8b2fb3fafd6d9ee8cd7a868b7b44f26514147a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 18:02:41 +0200 Subject: [PATCH 104/192] 'get_display_view_colorspace_subprocess' is private --- client/ayon_core/pipeline/colorspace.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 7b0d4c8491..4034527282 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1364,15 +1364,16 @@ def get_display_view_colorspace_name(config_path, display, view): if not compatibility_check(): # python environment is not compatible with PyOpenColorIO # needs to be run in subprocess - return get_display_view_colorspace_subprocess(config_path, - display, view) + return _get_display_view_colorspace_subprocess( + config_path, display, view + ) from ayon_core.scripts.ocio_wrapper import _get_display_view_colorspace_name # noqa return _get_display_view_colorspace_name(config_path, display, view) -def get_display_view_colorspace_subprocess(config_path, display, view): +def _get_display_view_colorspace_subprocess(config_path, display, view): """Returns the colorspace attribute of the (display, view) pair via subprocess. From 322e36128a8ed2497fa862e1ad150f834ecd73be Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 18:04:12 +0200 Subject: [PATCH 105/192] space sufficient cache logic --- client/ayon_core/pipeline/colorspace.py | 29 +++++++++++-------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 4034527282..77c830d44a 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -509,16 +509,15 @@ def get_ocio_config_colorspaces(config_path): if not compatibility_check(): # python environment is not compatible with PyOpenColorIO # needs to be run in subprocess - CachedData.ocio_config_colorspaces[config_path] = \ - _get_wrapped_with_subprocess( - "config", "get_colorspace", in_path=config_path + config_colorspaces = _get_wrapped_with_subprocess( + "config", "get_colorspace", in_path=config_path ) else: # TODO: refactor this so it is not imported but part of this file from ayon_core.scripts.ocio_wrapper import _get_colorspace_data - CachedData.ocio_config_colorspaces[config_path] = \ - _get_colorspace_data(config_path) + config_colorspaces = _get_colorspace_data(config_path) + CachedData.ocio_config_colorspaces[config_path] = config_colorspaces return CachedData.ocio_config_colorspaces[config_path] @@ -1160,16 +1159,15 @@ def get_remapped_colorspace_to_native( Union[str, None]: native colorspace name defined in remapping or None """ - CachedData.remapping.setdefault(host_name, {}) - if CachedData.remapping[host_name].get("to_native") is None: + host_mapping = CachedData.remapping.setdefault(host_name, {}) + if "to_native" not in host_mapping: remapping_rules = imageio_host_settings["remapping"]["rules"] - CachedData.remapping[host_name]["to_native"] = { + host_mapping["to_native"] = { rule["ocio_name"]: rule["host_native_name"] for rule in remapping_rules } - return CachedData.remapping[host_name]["to_native"].get( - ocio_colorspace_name) + return host_mapping["to_native"].get(ocio_colorspace_name) def get_remapped_colorspace_from_native( @@ -1184,18 +1182,17 @@ def get_remapped_colorspace_from_native( Returns: Union[str, None]: Ocio colorspace name defined in remapping or None. - """ - CachedData.remapping.setdefault(host_name, {}) - if CachedData.remapping[host_name].get("from_native") is None: + """ + host_mapping = CachedData.remapping.setdefault(host_name, {}) + if "from_native" not in host_mapping: remapping_rules = imageio_host_settings["remapping"]["rules"] - CachedData.remapping[host_name]["from_native"] = { + host_mapping["from_native"] = { rule["host_native_name"]: rule["ocio_name"] for rule in remapping_rules } - return CachedData.remapping[host_name]["from_native"].get( - host_native_colorspace_name) + return host_mapping["from_native"].get(host_native_colorspace_name) def _get_imageio_settings(project_settings, host_name): From b679b06919bfbde31790f920d6fb95555849f675 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 18:04:42 +0200 Subject: [PATCH 106/192] formatting changes --- client/ayon_core/pipeline/colorspace.py | 48 +++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 77c830d44a..ed590758a3 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -334,10 +334,10 @@ def parse_colorspace_from_filepath( pattern = "|".join( # Allow to match spaces also as underscores because the # integrator replaces spaces with underscores in filenames - re.escape(colorspace) for colorspace in + re.escape(colorspace) # Sort by longest first so the regex matches longer matches # over smaller matches, e.g. matching 'Output - sRGB' over 'sRGB' - sorted(colorspaces, key=len, reverse=True) + for colorspace in sorted(colorspaces, key=len, reverse=True) ) return re.compile(pattern) @@ -529,11 +529,12 @@ def convert_colorspace_enumerator_item( """Convert colorspace enumerator item to dictionary Args: - colorspace_item (str): colorspace and family in couple - config_items (dict[str,dict]): colorspace data + colorspace_enum_item (str): Colorspace and family in couple. + config_items (dict[str,dict]): Colorspace data. Returns: dict: colorspace data + """ if "::" not in colorspace_enum_item: return None @@ -1103,13 +1104,13 @@ def get_imageio_file_rules(project_name, host_name, project_settings=None): """Get ImageIO File rules from project settings Args: - project_name (str): project name - host_name (str): host name - project_settings (dict, optional): project settings. - Defaults to None. + project_name (str): Project name. + host_name (str): Host name. + project_settings (Optional[dict]): Project settings. Returns: list[dict[str, Any]]: file rules data + """ project_settings = project_settings or get_project_settings(project_name) @@ -1151,7 +1152,7 @@ def get_remapped_colorspace_to_native( """Return native colorspace name. Args: - ocio_colorspace_name (str | None): ocio colorspace name + ocio_colorspace_name (str | None): OCIO colorspace name. host_name (str): Host name. imageio_host_settings (dict[str, Any]): ImageIO host settings. @@ -1199,12 +1200,12 @@ def _get_imageio_settings(project_settings, host_name): """Get ImageIO settings for global and host Args: - project_settings (dict): project settings. - Defaults to None. - host_name (str): host name + project_settings (dict[str, Any]): Project settings. + host_name (str): Host name. Returns: - tuple[dict, dict]: image io settings for global and host + tuple[dict, dict]: Image io settings for global and host. + """ # get image io from global and host_name imageio_global = project_settings["core"]["imageio"] @@ -1266,18 +1267,13 @@ def get_colorspace_settings_from_publish_context(context_data): def set_colorspace_data_to_representation( - representation, context_data, + representation, + context_data, colorspace=None, log=None ): """Sets colorspace data to representation. - Args: - representation (dict): publishing representation - context_data (publish.Context.data): publishing context data - colorspace (str, optional): colorspace name. Defaults to None. - log (logging.Logger, optional): logger instance. Defaults to None. - Example: ``` { @@ -1292,6 +1288,12 @@ def set_colorspace_data_to_representation( } ``` + Args: + representation (dict): publishing representation + context_data (publish.Context.data): publishing context data + colorspace (Optional[str]): Colorspace name. + log (Optional[logging.Logger]): logger instance. + """ log = log or Logger.get_logger(__name__) @@ -1355,9 +1357,9 @@ def get_display_view_colorspace_name(config_path, display, view): view (str): view name e.g. "sRGB" Returns: - view color space name (str) e.g. "Output - sRGB" - """ + str: View color space name. e.g. "Output - sRGB" + """ if not compatibility_check(): # python environment is not compatible with PyOpenColorIO # needs to be run in subprocess @@ -1381,8 +1383,8 @@ def _get_display_view_colorspace_subprocess(config_path, display, view): Returns: view color space name (str) e.g. "Output - sRGB" - """ + """ with _make_temp_json_file() as tmp_json_path: # Prepare subprocess arguments args = [ From ff05fafb77e5901e34e2dbcb070435f5fc72cd96 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 May 2024 18:04:52 +0200 Subject: [PATCH 107/192] mark 'get_imageio_config' as deprecated --- client/ayon_core/pipeline/colorspace.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index ed590758a3..e9da194984 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -735,6 +735,7 @@ def get_views_data_subprocess(config_path): ) +@deprecated("get_imageio_config_preset") def get_imageio_config( project_name, host_name, From bd1f7dce4a55407c3e200b84a64083957b9b3234 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 May 2024 23:52:18 +0200 Subject: [PATCH 108/192] Fix repair for instances with older publish attributes that mismatch settings --- .../publish/validate_alembic_options_defaults.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 0abb734c9b..11f4c313fa 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -100,11 +100,20 @@ class ValidateAlembicDefaultsPointcache( ) # Set the settings values on the create context then save to workfile. - attributes = cls._get_publish_attributes(instance) settings = cls._get_settings(instance.context) - create_publish_attributes = create_instance.data["publish_attributes"] + attributes = cls._get_publish_attributes(create_instance) for key in attributes: - create_publish_attributes[cls.plugin_name][key] = settings[key] + if key not in settings: + # This may occur if attributes have changed over time and an + # existing instance has older legacy attributes that do not + # match the current settings definition. + cls.log.warning( + "Publish attribute %s not found in Alembic Export " + "default settings. Ignoring repair for attribute.", + key + ) + continue + attributes[key] = settings[key] create_context.save_changes() From a3c481732fc705ff65b9504f55472ff411d4207c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 May 2024 12:49:21 +0200 Subject: [PATCH 109/192] Update proper exception to tame linter Generic exception will be most likely JSON broken response, which is caught in ValueError in ws_stub --- .../hosts/photoshop/plugins/create/create_image.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index 97543e96de..e18644d038 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -35,7 +35,10 @@ class ImageCreator(Creator): create_empty_group = False stub = api.stub() # only after PS is up - top_level_selected_items = stub.get_selected_layers() + try: + top_level_selected_items = stub.get_selected_layers() + except ValueError: + raise CreatorError("Cannot group locked Background layer!") if pre_create_data.get("use_selection"): only_single_item_selected = len(top_level_selected_items) == 1 if ( @@ -51,10 +54,8 @@ class ImageCreator(Creator): groups_to_create.append(group) else: stub.select_layers(stub.get_layers()) - try: - group = stub.group_selected_layers(product_name_from_ui) - except: # noqa E722 - raise CreatorError("Cannot group locked Background layer!") + group = stub.group_selected_layers(product_name_from_ui) + groups_to_create.append(group) # create empty group if nothing selected From f1afd1653e9f4afd8f9f2ca1564684c7f8d9a8cb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 May 2024 12:55:18 +0200 Subject: [PATCH 110/192] Fix exception even for different use case --- .../photoshop/plugins/create/create_image.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index e18644d038..a44c3490c6 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -35,11 +35,12 @@ class ImageCreator(Creator): create_empty_group = False stub = api.stub() # only after PS is up - try: - top_level_selected_items = stub.get_selected_layers() - except ValueError: - raise CreatorError("Cannot group locked Background layer!") if pre_create_data.get("use_selection"): + try: + top_level_selected_items = stub.get_selected_layers() + except ValueError: + raise CreatorError("Cannot group locked Background layer!") + only_single_item_selected = len(top_level_selected_items) == 1 if ( only_single_item_selected or @@ -53,8 +54,11 @@ class ImageCreator(Creator): group = stub.group_selected_layers(product_name_from_ui) groups_to_create.append(group) else: - stub.select_layers(stub.get_layers()) - group = stub.group_selected_layers(product_name_from_ui) + try: + stub.select_layers(stub.get_layers()) + group = stub.group_selected_layers(product_name_from_ui) + except ValueError: + raise CreatorError("Cannot group locked Background layer!") groups_to_create.append(group) From 2facf91bcb4a5ea5812dc93b3b2c026978a7a7f3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 May 2024 13:51:36 +0200 Subject: [PATCH 111/192] AY-4801-Added conversion of resources Added similar configuration as for ExtractReview to control possible conversion from .mov to target format (.mp4) --- .../plugins/publish/extract_editorial_pckg.py | 151 ++++++++++++++++-- .../server/settings/publish_plugins.py | 89 ++++++++++- 2 files changed, 230 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py index dc8163e1ff..02f953d579 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -1,16 +1,20 @@ +import copy import os.path +import subprocess + import opentimelineio import pyblish.api +from ayon_core.lib import filter_profiles, get_ffmpeg_tool_args, run_subprocess from ayon_core.pipeline import publish -class ExtractEditorialPackage(publish.Extractor): +class ExtractEditorialPckgConversion(publish.Extractor): """Replaces movie paths in otio file with publish rootless - Prepares movie resources for integration. - TODO introduce conversion to .mp4 + Prepares movie resources for integration (adds them to `transfers`). + Converts .mov files according to output definition. """ label = "Extract Editorial Package" @@ -35,13 +39,22 @@ class ExtractEditorialPackage(publish.Extractor): instance.data["representations"].append(editorial_pckg_repre) - publish_path = self._get_published_path(instance) - publish_folder = os.path.dirname(publish_path) - publish_resource_folder = os.path.join(publish_folder, "resources") - + publish_resource_folder = self._get_publish_resource_folder(instance) resource_paths = editorial_pckg_data["resource_paths"] transfers = self._get_transfers(resource_paths, publish_resource_folder) + + project_settings = instance.context.data["project_settings"] + profiles = (project_settings["traypublisher"] + ["publish"] + ["ExtractEditorialPckgConversion"] + .get("profiles")) + output_def = None + if profiles: + output_def = self._get_output_definition(instance, profiles) + if output_def: + transfers = self._convert_resources(output_def, transfers) + if not "transfers" in instance.data: instance.data["transfers"] = [] instance.data["transfers"] = transfers @@ -57,6 +70,36 @@ class ExtractEditorialPackage(publish.Extractor): self.log.info("Added Editorial Package representation: {}".format( editorial_pckg_repre)) + def _get_publish_resource_folder(self, instance): + """Calculates publish folder and create it.""" + publish_path = self._get_published_path(instance) + publish_folder = os.path.dirname(publish_path) + publish_resource_folder = os.path.join(publish_folder, "resources") + + if not os.path.exists(publish_resource_folder): + os.makedirs(publish_resource_folder, exist_ok=True) + return publish_resource_folder + + def _get_output_definition(self, instance, profiles): + """Return appropriate profile by context information.""" + product_type = instance.data["productType"] + product_name = instance.data["productName"] + task_entity = instance.data["taskEntity"] or {} + task_name = task_entity.get("name") + task_type = task_entity.get("taskType") + filtering_criteria = { + "product_types": product_type, + "product_names": product_name, + "task_names": task_name, + "task_types": task_type, + } + profile = filter_profiles( + profiles, + filtering_criteria, + logger=self.log + ) + return profile + def _get_resource_path_mapping(self, instance, transfers): """Returns dict of {source_mov_path: rootless_published_path}.""" replace_paths = {} @@ -68,7 +111,7 @@ class ExtractEditorialPackage(publish.Extractor): return replace_paths def _get_transfers(self, resource_paths, publish_resource_folder): - """Returns list of tuples (source, destination) movie paths.""" + """Returns list of tuples (source, destination) with movie paths.""" transfers = [] for res_path in resource_paths: res_basename = os.path.basename(res_path) @@ -77,7 +120,7 @@ class ExtractEditorialPackage(publish.Extractor): return transfers def _replace_target_urls(self, otio_data, replace_paths): - """Replace original movie paths with published rootles ones.""" + """Replace original movie paths with published rootless ones.""" for track in otio_data.tracks: for clip in track: # Check if the clip has a media reference @@ -120,3 +163,93 @@ class ExtractEditorialPackage(publish.Extractor): template = anatomy.get_template_item("publish", "default", "path") template_filled = template.format_strict(template_data) return os.path.normpath(template_filled) + + def _convert_resources(self, output_def, transfers): + """Converts all resource files to configured format.""" + outputs = output_def["outputs"] + if not outputs: + self.log.warning("No output configured in " + "ayon+settings://traypublisher/publish/ExtractEditorialPckgConversion/profiles/0/outputs") # noqa + return transfers + + final_transfers = [] + # most likely only single output is expected + for output in outputs: + out_extension = output["ext"] + out_def_ffmpeg_args = output["ffmpeg_args"] + ffmpeg_input_args = [ + value.strip() + for value in out_def_ffmpeg_args["input"] + if value.strip() + ] + ffmpeg_video_filters = [ + value.strip() + for value in out_def_ffmpeg_args["video_filters"] + if value.strip() + ] + ffmpeg_audio_filters = [ + value.strip() + for value in out_def_ffmpeg_args["audio_filters"] + if value.strip() + ] + ffmpeg_output_args = [ + value.strip() + for value in out_def_ffmpeg_args["output"] + if value.strip() + ] + ffmpeg_input_args = self._split_ffmpeg_args(ffmpeg_input_args) + + generic_args = [ + subprocess.list2cmdline(get_ffmpeg_tool_args("ffmpeg")) + ] + generic_args.extend(ffmpeg_input_args) + if ffmpeg_video_filters: + generic_args.append("-filter:v") + generic_args.append( + "\"{}\"".format(",".join(ffmpeg_video_filters))) + + if ffmpeg_audio_filters: + generic_args.append("-filter:a") + generic_args.append( + "\"{}\"".format(",".join(ffmpeg_audio_filters))) + + for source, destination in transfers: + base_name = os.path.basename(destination) + file_name, ext = os.path.splitext(base_name) + dest_path = os.path.join(os.path.dirname(destination), + f"{file_name}.{out_extension}") + final_transfers.append((source, dest_path)) + + all_args = copy.deepcopy(generic_args) + all_args.append(f"-i {source}") + all_args.extend(ffmpeg_output_args) # order matters + all_args.append(f"{dest_path}") + subprcs_cmd = " ".join(all_args) + + # run subprocess + self.log.debug("Executing: {}".format(subprcs_cmd)) + run_subprocess(subprcs_cmd, shell=True, logger=self.log) + return final_transfers + + def _split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + diff --git a/server_addon/traypublisher/server/settings/publish_plugins.py b/server_addon/traypublisher/server/settings/publish_plugins.py index f413c86227..9869f54620 100644 --- a/server_addon/traypublisher/server/settings/publish_plugins.py +++ b/server_addon/traypublisher/server/settings/publish_plugins.py @@ -1,4 +1,11 @@ -from ayon_server.settings import BaseSettingsModel, SettingsField +from pydantic import validator + +from ayon_server.settings import ( + BaseSettingsModel, + SettingsField, + task_types_enum, + ensure_unique_names +) class ValidatePluginModel(BaseSettingsModel): @@ -14,6 +21,74 @@ class ValidateFrameRangeModel(ValidatePluginModel): 'my_asset_to_publish.mov')""" +class ExtractEditorialPckgFFmpegModel(BaseSettingsModel): + video_filters: list[str] = SettingsField( + default_factory=list, + title="Video filters" + ) + audio_filters: list[str] = SettingsField( + default_factory=list, + title="Audio filters" + ) + input: list[str] = SettingsField( + default_factory=list, + title="Input arguments" + ) + output: list[str] = SettingsField( + default_factory=list, + title="Output arguments" + ) + + +class ExtractEditorialPckgOutputDefModel(BaseSettingsModel): + """Set extension and ffmpeg arguments. See `ExtractReview` for example.""" + _layout = "expanded" + name: str = SettingsField("", title="Name") + ext: str = SettingsField("", title="Output extension") + + ffmpeg_args: ExtractEditorialPckgFFmpegModel = SettingsField( + default_factory=ExtractEditorialPckgFFmpegModel, + title="FFmpeg arguments" + ) + + +class ExtractEditorialPckgProfileModel(BaseSettingsModel): + product_types: list[str] = SettingsField( + default_factory=list, + title="Product types" + ) + task_types: list[str] = SettingsField( + default_factory=list, + title="Task types", + enum_resolver=task_types_enum + ) + task_names: list[str] = SettingsField( + default_factory=list, + title="Task names" + ) + product_names: list[str] = SettingsField( + default_factory=list, + title="Product names" + ) + outputs: list[ExtractEditorialPckgOutputDefModel] = SettingsField( + default_factory=list, + title="Output Definitions", + ) + + @validator("outputs") + def validate_unique_outputs(cls, value): + ensure_unique_names(value) + return value + + +class ExtractEditorialPckgConversionModel(BaseSettingsModel): + """Conversion of input movie files into expected format.""" + enabled: bool = SettingsField(True) + profiles: list[ExtractEditorialPckgProfileModel] = SettingsField( + default_factory=list, title="Profiles" + ) + + class TrayPublisherPublishPlugins(BaseSettingsModel): CollectFrameDataFromAssetEntity: ValidatePluginModel = SettingsField( default_factory=ValidatePluginModel, @@ -28,6 +103,13 @@ class TrayPublisherPublishPlugins(BaseSettingsModel): default_factory=ValidatePluginModel, ) + ExtractEditorialPckgConversion: ExtractEditorialPckgConversionModel = ( + SettingsField( + default_factory=ExtractEditorialPckgConversionModel, + title="Extract Editorial Package Conversion" + ) + ) + DEFAULT_PUBLISH_PLUGINS = { "CollectFrameDataFromAssetEntity": { @@ -44,5 +126,10 @@ DEFAULT_PUBLISH_PLUGINS = { "enabled": True, "optional": True, "active": True + }, + "ExtractEditorialPckgConversion": { + "enabled": True, + "optional": True, + "active": True } } From c3910256b117487bff677ff81ef7b4c2c67a07a1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 May 2024 14:40:45 +0200 Subject: [PATCH 112/192] fix circular import --- client/ayon_core/pipeline/colorspace.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index e9da194984..705d1570b0 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -23,7 +23,6 @@ from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.load import get_representation_path_with_anatomy from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS -from .context_tools import get_current_context, get_current_host_name log = Logger.get_logger(__name__) @@ -765,8 +764,7 @@ def get_imageio_config( """ if not anatomy_data: - from ayon_core.pipeline.context_tools import ( - get_current_context_template_data) + from .context_tools import get_current_context_template_data anatomy_data = get_current_context_template_data() task_name = anatomy_data.get("task", {}).get("name") @@ -1425,6 +1423,8 @@ def get_current_context_imageio_config_preset( dict: ImageIO config preset. """ + from .context_tools import get_current_context, get_current_host_name + context = get_current_context() host_name = get_current_host_name() return get_imageio_config_preset( From 393897f2a9b2f4af3533ba2f6f00716d2de151d2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 May 2024 18:08:55 +0200 Subject: [PATCH 113/192] don't change controller project on close --- client/ayon_core/tools/loader/ui/window.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index 3a6f4679fa..8529a53b06 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -335,9 +335,7 @@ class LoaderWindow(QtWidgets.QWidget): def closeEvent(self, event): super(LoaderWindow, self).closeEvent(event) - # Deselect project so current context will be selected - # on next 'showEvent' - self._controller.set_selected_project(None) + self._reset_on_show = True def keyPressEvent(self, event): From 4d502a55481adbfdbd5fe06aeefe2f7ad14d381e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 May 2024 21:42:04 +0800 Subject: [PATCH 114/192] add reset max file and clear undo buffer in the callback of starting new scene --- client/ayon_core/hosts/max/api/lib.py | 3 --- client/ayon_core/hosts/max/api/pipeline.py | 11 +++++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/max/api/lib.py b/client/ayon_core/hosts/max/api/lib.py index d9a3af3336..0e3abe25ec 100644 --- a/client/ayon_core/hosts/max/api/lib.py +++ b/client/ayon_core/hosts/max/api/lib.py @@ -6,12 +6,9 @@ import json from typing import Any, Dict, Union import six -import ayon_api from ayon_core.pipeline import ( get_current_project_name, - get_current_folder_path, - get_current_task_name, colorspace ) from ayon_core.settings import get_project_settings diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index dc13f47795..c6298bf590 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -52,11 +52,8 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): self._has_been_setup = True - def context_setting(): - return lib.set_context_setting() - rt.callbacks.addScript(rt.Name('systemPostNew'), - context_setting) + on_post_open) rt.callbacks.addScript(rt.Name('filePostOpen'), lib.check_colorspace) @@ -163,6 +160,12 @@ def ls() -> list: yield lib.read(container) +def on_post_open(): + lib.set_context_setting() + rt.resetMaxFile(rt.Name("noPrompt")) + rt.clearUndoBuffer() + + def containerise(name: str, nodes: list, context, namespace=None, loader=None, suffix="_CON"): data = { From 7dadac74ac23db64d89512e792e7725d403a84f7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 May 2024 21:50:43 +0800 Subject: [PATCH 115/192] restore unncessary change --- client/ayon_core/hosts/max/api/lib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/max/api/lib.py b/client/ayon_core/hosts/max/api/lib.py index 0e3abe25ec..d9a3af3336 100644 --- a/client/ayon_core/hosts/max/api/lib.py +++ b/client/ayon_core/hosts/max/api/lib.py @@ -6,9 +6,12 @@ import json from typing import Any, Dict, Union import six +import ayon_api from ayon_core.pipeline import ( get_current_project_name, + get_current_folder_path, + get_current_task_name, colorspace ) from ayon_core.settings import get_project_settings From d25e8f508eeef612f99cca12976774fd3e401c5f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 May 2024 22:21:07 +0800 Subject: [PATCH 116/192] use on_new as name of the function --- client/ayon_core/hosts/max/api/pipeline.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index c6298bf590..776565bade 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -52,8 +52,7 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): self._has_been_setup = True - rt.callbacks.addScript(rt.Name('systemPostNew'), - on_post_open) + rt.callbacks.addScript(rt.Name('systemPostNew'), on_new) rt.callbacks.addScript(rt.Name('filePostOpen'), lib.check_colorspace) @@ -160,7 +159,7 @@ def ls() -> list: yield lib.read(container) -def on_post_open(): +def on_new(): lib.set_context_setting() rt.resetMaxFile(rt.Name("noPrompt")) rt.clearUndoBuffer() From 36cbdcfde77580e86a512b292603a5f810c8f77d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 May 2024 22:56:26 +0800 Subject: [PATCH 117/192] only reset max file and clear undo buffer when there is unsaved change before starting new scene --- client/ayon_core/hosts/max/api/pipeline.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 776565bade..4782159ef8 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -161,8 +161,9 @@ def ls() -> list: def on_new(): lib.set_context_setting() - rt.resetMaxFile(rt.Name("noPrompt")) - rt.clearUndoBuffer() + if rt.checkForSave(): + rt.resetMaxFile(rt.Name("noPrompt")) + rt.clearUndoBuffer() def containerise(name: str, nodes: list, context, From 46ed96cad8ac8fc34804bf52232fa302e1c39071 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 May 2024 23:10:44 +0800 Subject: [PATCH 118/192] add redraw views for new scene --- client/ayon_core/hosts/max/api/pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/client/ayon_core/hosts/max/api/pipeline.py index 4782159ef8..d9cfc3407f 100644 --- a/client/ayon_core/hosts/max/api/pipeline.py +++ b/client/ayon_core/hosts/max/api/pipeline.py @@ -164,6 +164,7 @@ def on_new(): if rt.checkForSave(): rt.resetMaxFile(rt.Name("noPrompt")) rt.clearUndoBuffer() + rt.redrawViews() def containerise(name: str, nodes: list, context, From 8b9a45e7152bb5c83d3eafaa11c466742ea1df2b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 May 2024 15:20:56 +0800 Subject: [PATCH 119/192] color channel from baking write node should be aligning with that from the write node created from creator --- client/ayon_core/hosts/nuke/api/plugin.py | 3 +++ .../ayon_core/hosts/nuke/plugins/publish/collect_writes.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index fb56dec833..02f41ff865 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -778,6 +778,7 @@ class ExporterReviewMov(ExporterReview): # deal with now lut defined in viewer lut self.viewer_lut_raw = klass.viewer_lut_raw self.write_colorspace = instance.data["colorspace"] + self.color_channels = instance.data["color_channels"] self.name = name or "baked" self.ext = ext or "mov" @@ -947,6 +948,8 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Path: {}".format(self.path)) write_node["file"].setValue(str(self.path)) write_node["file_type"].setValue(str(self.ext)) + write_node["channels"].setValue(str(self.color_channels)) + # Knobs `meta_codec` and `mov64_codec` are not available on centos. # TODO shouldn't this come from settings on outputs? try: diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py index 745351dc49..27525bcad1 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py @@ -153,6 +153,9 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, # Determine defined file type ext = write_node["file_type"].value() + # determine defined channel type + color_channels = write_node["channels"].value() + # get frame range data handle_start = instance.context.data["handleStart"] handle_end = instance.context.data["handleEnd"] @@ -172,7 +175,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, "path": write_file_path, "outputDir": output_dir, "ext": ext, - "colorspace": colorspace + "colorspace": colorspace, + "color_channels": color_channels }) if product_type == "render": From ab1b49b988183d0de95db27a249e89cfb59ed364 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Thu, 29 Feb 2024 12:23:57 +1300 Subject: [PATCH 120/192] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../tools/publisher/widgets/card_view_widgets.py | 11 +++++++++++ .../tools/publisher/widgets/list_view_widgets.py | 6 ++++++ .../tools/publisher/widgets/overview_widget.py | 10 ++++++++++ 3 files changed, 27 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 47c5399cf7..7d178e98e5 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -534,6 +534,8 @@ class InstanceCardView(AbstractInstanceView): Wrapper of all widgets in card view. """ + double_clicked = QtCore.Signal() + def __init__(self, controller, parent): super(InstanceCardView, self).__init__(parent) @@ -578,6 +580,9 @@ class InstanceCardView(AbstractInstanceView): self.sizePolicy().verticalPolicy() ) + def mouseDoubleClickEvent(self, event): + self.double_clicked.emit() + def sizeHint(self): """Modify sizeHint based on visibility of scroll bars.""" # Calculate width hint by content widget and vertical scroll bar @@ -715,6 +720,7 @@ class InstanceCardView(AbstractInstanceView): ) group_widget.active_changed.connect(self._on_active_changed) group_widget.selected.connect(self._on_widget_selection) + # group_widget.double_clicked.connect(self._on_widget_double_clicked) self._content_layout.insertWidget(widget_idx, group_widget) self._widgets_by_group[group_name] = group_widget @@ -825,6 +831,11 @@ class InstanceCardView(AbstractInstanceView): self.selection_changed.emit() + # def _on_widget_double_clicked(self): + # print("_on_widget_double_clicked") + # widgets = self._get_selected_widgets() + # print(widgets) + def _select_item_clear(self, instance_id, group_name, new_widget): """Select specific item by instance id and clear previous selection. diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 3322a73be6..8dfabed8e9 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -425,6 +425,9 @@ class InstanceListView(AbstractInstanceView): This is public access to and from list view. """ + + double_clicked = QtCore.Signal() + def __init__(self, controller, parent): super(InstanceListView, self).__init__(parent) @@ -474,6 +477,9 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled = True + def mouseDoubleClickEvent(self, event): + self.double_clicked.emit() + def _on_expand(self, index): self._update_widget_expand_state(index, True) diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index dd82185830..82e4153681 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -113,9 +113,15 @@ class OverviewWidget(QtWidgets.QFrame): product_list_view.selection_changed.connect( self._on_product_change ) + product_list_view.double_clicked.connect( + self._on_double_clicked + ) product_view_cards.selection_changed.connect( self._on_product_change ) + product_view_cards.double_clicked.connect( + self._on_double_clicked + ) # Active instances changed product_list_view.active_changed.connect( self._on_active_changed @@ -293,6 +299,10 @@ class OverviewWidget(QtWidgets.QFrame): instances, context_selected, convertor_identifiers ) + def _on_double_clicked(self): + from ayon_core.tools.utils import host_tools + host_tools.show_publisher(tab="publish") + def _on_active_changed(self): if self._refreshing_instances: return From 427d36c5ac796fdb6993ae34483e8eebf06ff874 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Thu, 29 Feb 2024 12:25:02 +1300 Subject: [PATCH 121/192] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../ayon_core/tools/publisher/widgets/card_view_widgets.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 7d178e98e5..d1d062c18f 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -720,7 +720,6 @@ class InstanceCardView(AbstractInstanceView): ) group_widget.active_changed.connect(self._on_active_changed) group_widget.selected.connect(self._on_widget_selection) - # group_widget.double_clicked.connect(self._on_widget_double_clicked) self._content_layout.insertWidget(widget_idx, group_widget) self._widgets_by_group[group_name] = group_widget @@ -831,11 +830,6 @@ class InstanceCardView(AbstractInstanceView): self.selection_changed.emit() - # def _on_widget_double_clicked(self): - # print("_on_widget_double_clicked") - # widgets = self._get_selected_widgets() - # print(widgets) - def _select_item_clear(self, instance_id, group_name, new_widget): """Select specific item by instance id and clear previous selection. From 58998d970c41d1a17275722c3eb97df7dd645325 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Thu, 29 Feb 2024 15:25:43 +1300 Subject: [PATCH 122/192] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 8dfabed8e9..90f5f3fab9 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -457,6 +457,7 @@ class InstanceListView(AbstractInstanceView): instance_view.collapsed.connect(self._on_collapse) instance_view.expanded.connect(self._on_expand) instance_view.toggle_requested.connect(self._on_toggle_request) + instance_view.doubleClicked.connect(self._on_double_clicked) self._group_items = {} self._group_widgets = {} @@ -477,7 +478,7 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled = True - def mouseDoubleClickEvent(self, event): + def _on_double_clicked(self, event): self.double_clicked.emit() def _on_expand(self, index): From 2f326c1467ca26dbedf76a56784d2a37b9dbf2b8 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Tue, 5 Mar 2024 09:57:08 +1300 Subject: [PATCH 123/192] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../ayon_core/tools/publisher/widgets/list_view_widgets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 90f5f3fab9..7c4c07baea 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -457,7 +457,7 @@ class InstanceListView(AbstractInstanceView): instance_view.collapsed.connect(self._on_collapse) instance_view.expanded.connect(self._on_expand) instance_view.toggle_requested.connect(self._on_toggle_request) - instance_view.doubleClicked.connect(self._on_double_clicked) + instance_view.doubleClicked.connect(self.double_clicked) self._group_items = {} self._group_widgets = {} @@ -478,9 +478,6 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled = True - def _on_double_clicked(self, event): - self.double_clicked.emit() - def _on_expand(self, index): self._update_widget_expand_state(index, True) From dd776b728ba510283ea17b96dc6b65fa65a8809e Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Tue, 5 Mar 2024 10:02:23 +1300 Subject: [PATCH 124/192] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../ayon_core/tools/publisher/widgets/overview_widget.py | 9 +++------ client/ayon_core/tools/publisher/window.py | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index 82e4153681..cedf52ae01 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -18,6 +18,7 @@ class OverviewWidget(QtWidgets.QFrame): instance_context_changed = QtCore.Signal() create_requested = QtCore.Signal() convert_requested = QtCore.Signal() + publish_tab_requested = QtCore.Signal() anim_end_value = 200 anim_duration = 200 @@ -114,13 +115,13 @@ class OverviewWidget(QtWidgets.QFrame): self._on_product_change ) product_list_view.double_clicked.connect( - self._on_double_clicked + self.publish_tab_requested ) product_view_cards.selection_changed.connect( self._on_product_change ) product_view_cards.double_clicked.connect( - self._on_double_clicked + self.publish_tab_requested ) # Active instances changed product_list_view.active_changed.connect( @@ -299,10 +300,6 @@ class OverviewWidget(QtWidgets.QFrame): instances, context_selected, convertor_identifiers ) - def _on_double_clicked(self): - from ayon_core.tools.utils import host_tools - host_tools.show_publisher(tab="publish") - def _on_active_changed(self): if self._refreshing_instances: return diff --git a/client/ayon_core/tools/publisher/window.py b/client/ayon_core/tools/publisher/window.py index 123864ff6c..1b13ced317 100644 --- a/client/ayon_core/tools/publisher/window.py +++ b/client/ayon_core/tools/publisher/window.py @@ -258,6 +258,9 @@ class PublisherWindow(QtWidgets.QDialog): overview_widget.convert_requested.connect( self._on_convert_requested ) + overview_widget.publish_tab_requested.connect( + self._go_to_publish_tab + ) save_btn.clicked.connect(self._on_save_clicked) reset_btn.clicked.connect(self._on_reset_clicked) From b8f0d590f16cf0d7a6e4add5daef27cfb5140a1e Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Mon, 11 Mar 2024 10:30:42 +1300 Subject: [PATCH 125/192] right clicking tree view doen't switch tab --- .../tools/publisher/widgets/card_view_widgets.py | 3 ++- .../tools/publisher/widgets/list_view_widgets.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index d1d062c18f..28ed237fe6 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -581,7 +581,8 @@ class InstanceCardView(AbstractInstanceView): ) def mouseDoubleClickEvent(self, event): - self.double_clicked.emit() + if event.button() == QtCore.Qt.LeftButton: + self.double_clicked.emit() def sizeHint(self): """Modify sizeHint based on visibility of scroll bars.""" diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 7c4c07baea..eb5f41be4a 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -379,7 +379,7 @@ class InstanceTreeView(QtWidgets.QTreeView): "double click" as 2x "single click". """ if event.button() != QtCore.Qt.LeftButton: - return + return False pressed_group_index = None pos_index = self.indexAt(event.pos()) @@ -388,13 +388,17 @@ class InstanceTreeView(QtWidgets.QTreeView): self._pressed_group_index = pressed_group_index + return True + def mousePressEvent(self, event): - self._mouse_press(event) - super(InstanceTreeView, self).mousePressEvent(event) + handled = self._mouse_press(event) + if handled: + super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): - self._mouse_press(event) - super(InstanceTreeView, self).mouseDoubleClickEvent(event) + handled = self._mouse_press(event) + if handled: + super(InstanceTreeView, self).mouseDoubleClickEvent(event) def _mouse_release(self, event, pressed_index): if event.button() != QtCore.Qt.LeftButton: From 7c028a752cd012e9914c1d3a29829869708c032c Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Mon, 11 Mar 2024 10:52:27 +1300 Subject: [PATCH 126/192] clicking in empty area in tree view should switch tab --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index eb5f41be4a..f142faff83 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -317,6 +317,7 @@ class InstanceListGroupWidget(QtWidgets.QFrame): class InstanceTreeView(QtWidgets.QTreeView): """View showing instances and their groups.""" toggle_requested = QtCore.Signal(int) + double_clicked = QtCore.Signal() def __init__(self, *args, **kwargs): super(InstanceTreeView, self).__init__(*args, **kwargs) @@ -396,6 +397,7 @@ class InstanceTreeView(QtWidgets.QTreeView): super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): + self.double_clicked.emit() handled = self._mouse_press(event) if handled: super(InstanceTreeView, self).mouseDoubleClickEvent(event) @@ -461,7 +463,7 @@ class InstanceListView(AbstractInstanceView): instance_view.collapsed.connect(self._on_collapse) instance_view.expanded.connect(self._on_expand) instance_view.toggle_requested.connect(self._on_toggle_request) - instance_view.doubleClicked.connect(self.double_clicked) + instance_view.double_clicked.connect(self.double_clicked) self._group_items = {} self._group_widgets = {} From 55256d59a3ad315974ce3a6aa9f70d00e26561af Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Mon, 11 Mar 2024 10:56:15 +1300 Subject: [PATCH 127/192] clicking in empty area in tree view should switch tab --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index f142faff83..6a7335bd74 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -397,9 +397,9 @@ class InstanceTreeView(QtWidgets.QTreeView): super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): - self.double_clicked.emit() handled = self._mouse_press(event) if handled: + self.double_clicked.emit() super(InstanceTreeView, self).mouseDoubleClickEvent(event) def _mouse_release(self, event, pressed_index): From e2849b83e0fbb3d90edf637b3a3161172facecf8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 10:58:24 +0200 Subject: [PATCH 128/192] card widgets can tell if they should trigger double click --- .../publisher/widgets/card_view_widgets.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 28ed237fe6..4e34f9b58c 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -52,6 +52,7 @@ class SelectionTypes: class BaseGroupWidget(QtWidgets.QWidget): selected = QtCore.Signal(str, str, str) removed_selected = QtCore.Signal() + double_clicked = QtCore.Signal() def __init__(self, group_name, parent): super(BaseGroupWidget, self).__init__(parent) @@ -192,6 +193,7 @@ class ConvertorItemsGroupWidget(BaseGroupWidget): else: widget = ConvertorItemCardWidget(item, self) widget.selected.connect(self._on_widget_selection) + widget.double_clicked(self.double_clicked) self._widgets_by_id[item.id] = widget self._content_layout.insertWidget(widget_idx, widget) widget_idx += 1 @@ -254,6 +256,7 @@ class InstanceGroupWidget(BaseGroupWidget): ) widget.selected.connect(self._on_widget_selection) widget.active_changed.connect(self._on_active_changed) + widget.double_clicked.connect(self.double_clicked) self._widgets_by_id[instance.id] = widget self._content_layout.insertWidget(widget_idx, widget) widget_idx += 1 @@ -271,6 +274,7 @@ class CardWidget(BaseClickableFrame): # Group identifier of card # - this must be set because if send when mouse is released with card id _group_identifier = None + double_clicked = QtCore.Signal() def __init__(self, parent): super(CardWidget, self).__init__(parent) @@ -279,6 +283,11 @@ class CardWidget(BaseClickableFrame): self._selected = False self._id = None + def mouseDoubleClickEvent(self, event): + super(CardWidget, self).mouseDoubleClickEvent(event) + if self._is_valid_double_click(event): + self.double_clicked.emit() + @property def id(self): """Id of card.""" @@ -312,6 +321,9 @@ class CardWidget(BaseClickableFrame): self.selected.emit(self._id, self._group_identifier, selection_type) + def _is_valid_double_click(self, event): + return True + class ContextCardWidget(CardWidget): """Card for global context. @@ -527,6 +539,15 @@ class InstanceCardWidget(CardWidget): def _on_expend_clicked(self): self._set_expanded() + def _is_valid_double_click(self, event): + widget = self.childAt(event.pos()) + if ( + widget is self._active_checkbox + or widget is self._expand_btn + ): + return False + return True + class InstanceCardView(AbstractInstanceView): """Publish access to card view. @@ -580,10 +601,6 @@ class InstanceCardView(AbstractInstanceView): self.sizePolicy().verticalPolicy() ) - def mouseDoubleClickEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - self.double_clicked.emit() - def sizeHint(self): """Modify sizeHint based on visibility of scroll bars.""" # Calculate width hint by content widget and vertical scroll bar @@ -721,6 +738,7 @@ class InstanceCardView(AbstractInstanceView): ) group_widget.active_changed.connect(self._on_active_changed) group_widget.selected.connect(self._on_widget_selection) + group_widget.double_clicked.connect(self.double_clicked) self._content_layout.insertWidget(widget_idx, group_widget) self._widgets_by_group[group_name] = group_widget @@ -761,6 +779,7 @@ class InstanceCardView(AbstractInstanceView): widget = ContextCardWidget(self._content_widget) widget.selected.connect(self._on_widget_selection) + widget.double_clicked.connect(self.double_clicked) self._context_widget = widget @@ -784,6 +803,7 @@ class InstanceCardView(AbstractInstanceView): CONVERTOR_ITEM_GROUP, self._content_widget ) group_widget.selected.connect(self._on_widget_selection) + group_widget.double_clicked.connect(self.double_clicked) self._content_layout.insertWidget(1, group_widget) self._convertor_items_group = group_widget From 8b17cf5485c52bf72c5b624ee4a1c9788f83fe92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 10:58:48 +0200 Subject: [PATCH 129/192] list view widgets can tell if should trigger double click --- .../publisher/widgets/list_view_widgets.py | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 6a7335bd74..aa8f26998a 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -110,6 +110,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): This is required to be able use custom checkbox on custom place. """ active_changed = QtCore.Signal(str, bool) + double_clicked = QtCore.Signal() def __init__(self, instance, parent): super(InstanceListItemWidget, self).__init__(parent) @@ -149,6 +150,12 @@ class InstanceListItemWidget(QtWidgets.QWidget): self._set_valid_property(instance.has_valid_context) + def mouseDoubleClickEvent(self, event): + widget = self.childAt(event.pos()) + super(InstanceListItemWidget, self).mouseDoubleClickEvent(event) + if widget is not self._active_checkbox: + self.double_clicked.emit() + def _set_valid_property(self, valid): if self._has_valid_context == valid: return @@ -209,6 +216,8 @@ class InstanceListItemWidget(QtWidgets.QWidget): class ListContextWidget(QtWidgets.QFrame): """Context (or global attributes) widget.""" + double_clicked = QtCore.Signal() + def __init__(self, parent): super(ListContextWidget, self).__init__(parent) @@ -225,6 +234,10 @@ class ListContextWidget(QtWidgets.QFrame): self.label_widget = label_widget + def mouseDoubleClickEvent(self, event): + super(ListContextWidget, self).mouseDoubleClickEvent(event) + self.double_clicked.emit() + class InstanceListGroupWidget(QtWidgets.QFrame): """Widget representing group of instances. @@ -392,15 +405,12 @@ class InstanceTreeView(QtWidgets.QTreeView): return True def mousePressEvent(self, event): - handled = self._mouse_press(event) - if handled: - super(InstanceTreeView, self).mousePressEvent(event) + self._mouse_press(event) + super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): - handled = self._mouse_press(event) - if handled: - self.double_clicked.emit() - super(InstanceTreeView, self).mouseDoubleClickEvent(event) + self._mouse_press(event) + super(InstanceTreeView, self).mouseDoubleClickEvent(event) def _mouse_release(self, event, pressed_index): if event.button() != QtCore.Qt.LeftButton: @@ -697,6 +707,7 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled ) widget.active_changed.connect(self._on_active_changed) + widget.double_clicked.connect(self.double_clicked) self._instance_view.setIndexWidget(proxy_index, widget) self._widgets_by_id[instance.id] = widget @@ -727,6 +738,7 @@ class InstanceListView(AbstractInstanceView): ) proxy_index = self._proxy_model.mapFromSource(index) widget = ListContextWidget(self._instance_view) + widget.double_clicked.connect(self.double_clicked) self._instance_view.setIndexWidget(proxy_index, widget) self._context_widget = widget From 168f8cdcc20435105bcf2f73bbca0ea22b08b298 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 10:59:05 +0200 Subject: [PATCH 130/192] revert output of '_mouse_press' --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index aa8f26998a..71be0ab1a4 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -393,7 +393,7 @@ class InstanceTreeView(QtWidgets.QTreeView): "double click" as 2x "single click". """ if event.button() != QtCore.Qt.LeftButton: - return False + return pressed_group_index = None pos_index = self.indexAt(event.pos()) @@ -402,8 +402,6 @@ class InstanceTreeView(QtWidgets.QTreeView): self._pressed_group_index = pressed_group_index - return True - def mousePressEvent(self, event): self._mouse_press(event) super(InstanceTreeView, self).mousePressEvent(event) From a19350eea9925cb5c8ba6b3af08bbef8bf953fe5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 12:10:50 +0200 Subject: [PATCH 131/192] implemented helper function to determine if current process is ayon launcher --- client/ayon_core/lib/__init__.py | 2 ++ client/ayon_core/lib/ayon_info.py | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index e436396c6c..e25d3479ee 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -139,6 +139,7 @@ from .path_tools import ( ) from .ayon_info import ( + is_in_ayon_launcher_process, is_running_from_build, is_using_ayon_console, is_staging_enabled, @@ -248,6 +249,7 @@ __all__ = [ "Logger", + "is_in_ayon_launcher_process", "is_running_from_build", "is_using_ayon_console", "is_staging_enabled", diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index fc09a7c90c..c4333fab95 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -1,4 +1,5 @@ import os +import sys import json import datetime import platform @@ -25,6 +26,18 @@ def get_ayon_launcher_version(): return content["__version__"] +def is_in_ayon_launcher_process(): + """Determine if current process is running from AYON launcher. + + Returns: + bool: True if running from AYON launcher. + + """ + ayon_executable_path = os.path.normpath(os.environ["AYON_EXECUTABLE"]) + executable_path = os.path.normpath(sys.executable) + return ayon_executable_path == executable_path + + def is_running_from_build(): """Determine if current process is running from build or code. From fa4569402395e88543796db5903157ea917a9cd5 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 9 May 2024 12:04:27 +0100 Subject: [PATCH 132/192] Use transform cache to handle camera updates --- .../blender/plugins/load/load_camera_abc.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py index 6178578081..a49bb40d9a 100644 --- a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py @@ -43,7 +43,10 @@ class AbcCameraLoader(plugin.AssetLoader): def _process(self, libpath, asset_group, group_name): plugin.deselect_all() - bpy.ops.wm.alembic_import(filepath=libpath) + # Force the creation of the transform cache even if the camera + # doesn't have an animation. We use the cache to update the camera. + bpy.ops.wm.alembic_import( + filepath=libpath, always_add_cache_reader=True) objects = lib.get_selection() @@ -178,12 +181,33 @@ class AbcCameraLoader(plugin.AssetLoader): self.log.info("Library already loaded, not updating...") return - mat = asset_group.matrix_basis.copy() + for obj in asset_group.children: + found = False + for constraint in obj.constraints: + if constraint.type == "TRANSFORM_CACHE": + constraint.cache_file.filepath = libpath.as_posix() + found = True + break + if not found: + # This is to keep compatibility with cameras loaded with + # the old loader + # Create a new constraint for the cache file + constraint = obj.constraints.new("TRANSFORM_CACHE") + bpy.ops.cachefile.open(filepath=libpath.as_posix()) + constraint.cache_file = bpy.data.cache_files[-1] + constraint.cache_file.scale = 1.0 - self._remove(asset_group) - self._process(str(libpath), asset_group, object_name) + # This is a workaround to set the object path. Blender doesn't + # load the list of object paths until the object is evaluated. + # This is a hack to force the object to be evaluated. + # The modifier doesn't need to be removed because camera + # objects don't have modifiers. + obj.modifiers.new( + name='MeshSequenceCache', type='MESH_SEQUENCE_CACHE') + bpy.context.evaluated_depsgraph_get() - asset_group.matrix_basis = mat + constraint.object_path = ( + constraint.cache_file.object_paths[0].path) metadata["libpath"] = str(libpath) metadata["representation"] = repre_entity["id"] From 145268e94fe4da3f449ff4e46ca3a4f1d41a2a69 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 14:58:17 +0200 Subject: [PATCH 133/192] skip the plugin logic if all keys are set --- .../publish/collect_frame_data_from_asset_entity.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py index 4d203649c7..76ecc1cd8b 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py @@ -26,6 +26,13 @@ class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin): ): if key not in instance.data: missing_keys.append(key) + + # Skip the logic if all keys are already collected. + # NOTE: In editorial is not 'folderEntity' filled, so it would crash + # even if we don't need it. + if not missing_keys: + return + keys_set = [] folder_attributes = instance.data["folderEntity"]["attrib"] for key in missing_keys: From c9ad59525506674ad68a9ec16e8ef0214a9eb62e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:05:31 +0200 Subject: [PATCH 134/192] formatting changes --- .../collect_frame_data_from_asset_entity.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py index 76ecc1cd8b..2e564a2e4e 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py @@ -10,9 +10,13 @@ class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder + 0.491 label = "Collect Missing Frame Data From Folder" - families = ["plate", "pointcache", - "vdbcache", "online", - "render"] + families = [ + "plate", + "pointcache", + "vdbcache", + "online", + "render", + ] hosts = ["traypublisher"] def process(self, instance): @@ -22,7 +26,7 @@ class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin): "frameStart", "frameEnd", "handleStart", - "handleEnd" + "handleEnd", ): if key not in instance.data: missing_keys.append(key) @@ -39,6 +43,9 @@ class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin): if key in folder_attributes: instance.data[key] = folder_attributes[key] keys_set.append(key) + if keys_set: - self.log.debug(f"Frame range data {keys_set} " - "has been collected from folder entity.") + self.log.debug( + f"Frame range data {keys_set} " + "has been collected from folder entity." + ) From f343664a3836fb4dfcc0c753335baa80521ae72c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:07:19 +0200 Subject: [PATCH 135/192] rename the file --- ...m_asset_entity.py => collect_frame_data_from_folder_entity.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/ayon_core/hosts/traypublisher/plugins/publish/{collect_frame_data_from_asset_entity.py => collect_frame_data_from_folder_entity.py} (100%) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_folder_entity.py similarity index 100% rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py rename to client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_folder_entity.py From 0bba27e4dd97b28be9d47c5aaa08f526b97c928c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:16:52 +0200 Subject: [PATCH 136/192] removed unused imports --- client/ayon_core/hosts/max/api/lib.py | 3 --- .../hosts/maya/plugins/create/create_animation_pointcache.py | 1 - client/ayon_core/hosts/traypublisher/csv_publish.py | 2 -- 3 files changed, 6 deletions(-) diff --git a/client/ayon_core/hosts/max/api/lib.py b/client/ayon_core/hosts/max/api/lib.py index d9a3af3336..0e3abe25ec 100644 --- a/client/ayon_core/hosts/max/api/lib.py +++ b/client/ayon_core/hosts/max/api/lib.py @@ -6,12 +6,9 @@ import json from typing import Any, Dict, Union import six -import ayon_api from ayon_core.pipeline import ( get_current_project_name, - get_current_folder_path, - get_current_task_name, colorspace ) from ayon_core.settings import get_project_settings diff --git a/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py b/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py index 08d50a1ab8..069762e4ae 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py @@ -6,7 +6,6 @@ from ayon_core.lib import ( BoolDef, NumberDef, ) -from ayon_core.pipeline import CreatedInstance def _get_animation_attr_defs(cls): diff --git a/client/ayon_core/hosts/traypublisher/csv_publish.py b/client/ayon_core/hosts/traypublisher/csv_publish.py index b43792a357..2762172936 100644 --- a/client/ayon_core/hosts/traypublisher/csv_publish.py +++ b/client/ayon_core/hosts/traypublisher/csv_publish.py @@ -1,5 +1,3 @@ -import os - import pyblish.api import pyblish.util From 270921f63eb9a4032cf0f1d04f1056b71eab0071 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:18:10 +0200 Subject: [PATCH 137/192] use preferred order in condition --- .../modules/deadline/plugins/publish/validate_deadline_pools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py index 5094b3deaf..2fb511bf51 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py @@ -72,7 +72,7 @@ class ValidateDeadlinePools(OptionalPyblishPluginMixin, auth=auth, log=self.log) # some DL return "none" as a pool name - if not "none" in pools: + if "none" not in pools: pools.append("none") self.log.info("Available pools: {}".format(pools)) self.pools_per_url[deadline_url] = pools From 2e6ee298a7aa87c869af9d5f18a57173b2933aac Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:18:25 +0200 Subject: [PATCH 138/192] removed unnecessary check for 'deadline' key --- .../modules/deadline/plugins/publish/submit_publish_job.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 06dd62e18b..0f505dce78 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -467,8 +467,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, # Inject deadline url to instances to query DL for job id for overrides for inst in instances: - if not "deadline" in inst: - inst["deadline"] = {} inst["deadline"] = instance.data["deadline"] # publish job file From b7662645b5c980c09c599ce4d88ad03a77d0d00f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 May 2024 16:15:30 +0200 Subject: [PATCH 139/192] Refactor indentation for better readability Adjusted the indentation in a function to improve code clarity and readability. --- 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 705d1570b0..3503d0c534 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -873,7 +873,7 @@ def _get_global_config_data( product_entities_by_name = { product_entity["name"]: product_entity for product_entity in ayon_api.get_products( - project_name, + project_name, folder_ids={folder_id}, product_name_regex=product_name, fields={"id", "name"} From 9b2564cfd7049cbaff6670451c9cd6762023bb7f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 May 2024 16:22:21 +0200 Subject: [PATCH 140/192] fixing settings type name key --- server/settings/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/main.py b/server/settings/main.py index c9c86bdd0d..97bd376f47 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -58,7 +58,7 @@ def _ocio_config_profile_types(): return [ {"value": "builtin_path", "label": "AYON built-in OCIO config"}, {"value": "custom_path", "label": "Path to OCIO config"}, - {"value": "product", "label": "Published product"}, + {"value": "product_name", "label": "Published product"}, ] From f70bdc5795a698e8316b064a57720ddb8585ccb7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 16:59:50 +0200 Subject: [PATCH 141/192] fix product name in base value --- server/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/__init__.py b/server/__init__.py index f6f89f2049..31a6e8dfca 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -39,7 +39,7 @@ class CoreAddon(BaseServerAddon): ) base_value = { "type": "builtin_path", - "product": "", + "product_name": "", "host_names": [], "task_names": [], "task_types": [], From bcd1e864c0b2507080521141c61adbc70c5b4fc8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 17:00:04 +0200 Subject: [PATCH 142/192] use correct builtin path --- server/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/__init__.py b/server/__init__.py index 31a6e8dfca..50eda35c89 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -46,10 +46,13 @@ class CoreAddon(BaseServerAddon): "custom_path": "", "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio" } - if first_filepath not in ( + if first_filepath in ( "{BUILTIN_OCIO_ROOT}/aces_1.2/config.oci", "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", ): + base_value["type"] = "builtin_path" + base_value["builtin_path"] = first_filepath + else: base_value["type"] = "custom_path" base_value["custom_path"] = first_filepath From 925ff8b86f11e595be591b7458b28694608efb91 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 17:04:09 +0200 Subject: [PATCH 143/192] fix value check --- server/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/__init__.py b/server/__init__.py index 50eda35c89..82473927b6 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -47,7 +47,7 @@ class CoreAddon(BaseServerAddon): "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio" } if first_filepath in ( - "{BUILTIN_OCIO_ROOT}/aces_1.2/config.oci", + "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", ): base_value["type"] = "builtin_path" From 9570d92411dcd8efa97a93da61e34141a829f049 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 18:10:58 +0200 Subject: [PATCH 144/192] change layout of profiles --- server/settings/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/settings/main.py b/server/settings/main.py index 97bd376f47..d1cee32afa 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -77,6 +77,7 @@ def _ocio_built_in_paths(): class CoreImageIOConfigProfilesModel(BaseSettingsModel): + _layout = "expanded" host_names: list[str] = SettingsField( default_factory=list, title="Host names" From b7be1952e8533a6f794c7604ad4de939060a7495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 9 May 2024 20:24:54 +0200 Subject: [PATCH 145/192] Update client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py Co-authored-by: Roy Nieterau --- .../traypublisher/plugins/create/create_editorial_package.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py index 6a581b59d1..19ca032a0f 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py @@ -24,7 +24,6 @@ class EditorialPackageCreator(TrayPublishCreator): # Position batch creator after simple creators order = 120 - def get_icon(self): return "fa.folder" From 4d737790de9d598f7a5b88e7e20b6ca6e738b444 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 9 May 2024 21:04:54 +0100 Subject: [PATCH 146/192] Working version --- client/ayon_core/hosts/nuke/api/plugin.py | 9 +++++++-- .../nuke/plugins/publish/extract_review_intermediates.py | 9 +++++++-- server_addon/nuke/package.py | 2 +- server_addon/nuke/server/settings/publish_plugins.py | 3 +++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/client/ayon_core/hosts/nuke/api/plugin.py index fb56dec833..ec256ea303 100644 --- a/client/ayon_core/hosts/nuke/api/plugin.py +++ b/client/ayon_core/hosts/nuke/api/plugin.py @@ -834,7 +834,7 @@ class ExporterReviewMov(ExporterReview): self.log.info("Nodes exported...") return path - def generate_mov(self, farm=False, **kwargs): + def generate_mov(self, farm=False, delete=True, **kwargs): # colorspace data colorspace = None # get colorspace settings @@ -987,8 +987,13 @@ class ExporterReviewMov(ExporterReview): self.render(write_node.name()) # ---------- generate representation data + tags = ["review", "need_thumbnail"] + + if delete: + tags.append("delete") + self.get_representation_data( - tags=["review", "need_thumbnail", "delete"] + add_tags, + tags=tags + add_tags, custom_tags=add_custom_tags, range=True, colorspace=colorspace diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py index 8d7a3ec311..82c7b6e4c5 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py @@ -136,11 +136,16 @@ class ExtractReviewIntermediates(publish.Extractor): self, instance, o_name, o_data["extension"], multiple_presets) + o_data["add_custom_tags"].append("intermediate") + delete = not o_data.get("publish", False) + if instance.data.get("farm"): if "review" in instance.data["families"]: instance.data["families"].remove("review") - data = exporter.generate_mov(farm=True, **o_data) + data = exporter.generate_mov( + farm=True, delete=delete, **o_data + ) self.log.debug( "_ data: {}".format(data)) @@ -154,7 +159,7 @@ class ExtractReviewIntermediates(publish.Extractor): "bakeWriteNodeName": data.get("bakeWriteNodeName") }) else: - data = exporter.generate_mov(**o_data) + data = exporter.generate_mov(delete=delete, **o_data) # add representation generated by exporter generated_repres.extend(data["representations"]) diff --git a/server_addon/nuke/package.py b/server_addon/nuke/package.py index bf03c4e7e7..e522b9fb5d 100644 --- a/server_addon/nuke/package.py +++ b/server_addon/nuke/package.py @@ -1,3 +1,3 @@ name = "nuke" title = "Nuke" -version = "0.1.11" +version = "0.1.12" diff --git a/server_addon/nuke/server/settings/publish_plugins.py b/server_addon/nuke/server/settings/publish_plugins.py index d5b05d8715..e67f7be24f 100644 --- a/server_addon/nuke/server/settings/publish_plugins.py +++ b/server_addon/nuke/server/settings/publish_plugins.py @@ -125,6 +125,7 @@ class ReformatNodesConfigModel(BaseSettingsModel): class IntermediateOutputModel(BaseSettingsModel): name: str = SettingsField(title="Output name") + publish: bool = SettingsField(title="Publish") filter: BakingStreamFilterModel = SettingsField( title="Filter", default_factory=BakingStreamFilterModel) read_raw: bool = SettingsField( @@ -346,6 +347,7 @@ DEFAULT_PUBLISH_PLUGIN_SETTINGS = { "outputs": [ { "name": "baking", + "publish": False, "filter": { "task_types": [], "product_types": [], @@ -401,6 +403,7 @@ DEFAULT_PUBLISH_PLUGIN_SETTINGS = { "outputs": [ { "name": "baking", + "publish": False, "filter": { "task_types": [], "product_types": [], From 3bd7c7dddfd3a6e419d34640de4d248fc664396e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 10 May 2024 11:02:55 +0200 Subject: [PATCH 147/192] Fix after effects launch logic variable for `AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH` -> `AVALON_AFTEREFFECTS_WORKFILES_ON_LAUNCH` --- client/ayon_core/hosts/aftereffects/api/launch_logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/aftereffects/api/launch_logic.py b/client/ayon_core/hosts/aftereffects/api/launch_logic.py index 5a23f2cb35..da6887668a 100644 --- a/client/ayon_core/hosts/aftereffects/api/launch_logic.py +++ b/client/ayon_core/hosts/aftereffects/api/launch_logic.py @@ -60,7 +60,7 @@ def main(*subprocess_args): ) ) - elif os.environ.get("AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH", True): + elif os.environ.get("AVALON_AFTEREFFECTS_WORKFILES_ON_LAUNCH", True): save = False if os.getenv("WORKFILES_SAVE_AS"): save = True From 39da0bc7a3e23d7a89ed3ed40c1aee094540f2fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 10 May 2024 12:33:52 +0200 Subject: [PATCH 148/192] define server version with ayon attributes --- package.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.py b/package.py index 79450d029f..0f2e855161 100644 --- a/package.py +++ b/package.py @@ -5,7 +5,5 @@ version = "0.3.1-dev.1" client_dir = "ayon_core" plugin_for = ["ayon_server"] -requires = [ - "~ayon_server-1.0.3+<2.0.0", -] +ayon_server_version = ">=1.0.3<2.0.0" From 43bf0b135c4923ce14319f79ca4b39e3be27c7fc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 10 May 2024 12:34:06 +0200 Subject: [PATCH 149/192] define minimum launcher version --- package.py | 1 + 1 file changed, 1 insertion(+) diff --git a/package.py b/package.py index 0f2e855161..459b0034bd 100644 --- a/package.py +++ b/package.py @@ -7,3 +7,4 @@ client_dir = "ayon_core" plugin_for = ["ayon_server"] ayon_server_version = ">=1.0.3<2.0.0" +ayon_launcher_version = ">=1.0.2" From c4b07146adeea379fbe1d6ff99ae2f7f2c408384 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 10 May 2024 12:34:15 +0200 Subject: [PATCH 150/192] add remaining attributes --- package.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.py b/package.py index 459b0034bd..9e644fa310 100644 --- a/package.py +++ b/package.py @@ -8,3 +8,5 @@ plugin_for = ["ayon_server"] ayon_server_version = ">=1.0.3<2.0.0" ayon_launcher_version = ">=1.0.2" +ayon_required_addons = {} +ayon_compatible_addons = {} From 68cfa1a4a1ee7705f43e7999756d0a14dbbbc22b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 10 May 2024 13:54:30 +0200 Subject: [PATCH 151/192] fix server version compatibility --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 9e644fa310..fa3eaba9bd 100644 --- a/package.py +++ b/package.py @@ -6,7 +6,7 @@ client_dir = "ayon_core" plugin_for = ["ayon_server"] -ayon_server_version = ">=1.0.3<2.0.0" +ayon_server_version = ">=1.0.3,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = {} From 2a675f51a6db20322b3e1d5e8f537723bc33154d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 14:38:16 +0200 Subject: [PATCH 152/192] AY-4801-simplified Settings Got rid of profiles, didn't make much sense. Git rid of multiple output definitions, didn't make sense with single otio file. --- .../server/settings/publish_plugins.py | 44 +++---------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/server_addon/traypublisher/server/settings/publish_plugins.py b/server_addon/traypublisher/server/settings/publish_plugins.py index 9869f54620..2afe20c865 100644 --- a/server_addon/traypublisher/server/settings/publish_plugins.py +++ b/server_addon/traypublisher/server/settings/publish_plugins.py @@ -41,9 +41,7 @@ class ExtractEditorialPckgFFmpegModel(BaseSettingsModel): class ExtractEditorialPckgOutputDefModel(BaseSettingsModel): - """Set extension and ffmpeg arguments. See `ExtractReview` for example.""" _layout = "expanded" - name: str = SettingsField("", title="Name") ext: str = SettingsField("", title="Output extension") ffmpeg_args: ExtractEditorialPckgFFmpegModel = SettingsField( @@ -52,40 +50,13 @@ class ExtractEditorialPckgOutputDefModel(BaseSettingsModel): ) -class ExtractEditorialPckgProfileModel(BaseSettingsModel): - product_types: list[str] = SettingsField( - default_factory=list, - title="Product types" - ) - task_types: list[str] = SettingsField( - default_factory=list, - title="Task types", - enum_resolver=task_types_enum - ) - task_names: list[str] = SettingsField( - default_factory=list, - title="Task names" - ) - product_names: list[str] = SettingsField( - default_factory=list, - title="Product names" - ) - outputs: list[ExtractEditorialPckgOutputDefModel] = SettingsField( - default_factory=list, - title="Output Definitions", - ) - - @validator("outputs") - def validate_unique_outputs(cls, value): - ensure_unique_names(value) - return value - - class ExtractEditorialPckgConversionModel(BaseSettingsModel): - """Conversion of input movie files into expected format.""" - enabled: bool = SettingsField(True) - profiles: list[ExtractEditorialPckgProfileModel] = SettingsField( - default_factory=list, title="Profiles" + """Set output definition if resource files should be converted.""" + conversion_enabled: bool = SettingsField(True, + title="Conversion enabled") + output: ExtractEditorialPckgOutputDefModel = SettingsField( + default_factory=ExtractEditorialPckgOutputDefModel, + title="Output Definitions", ) @@ -128,8 +99,7 @@ DEFAULT_PUBLISH_PLUGINS = { "active": True }, "ExtractEditorialPckgConversion": { - "enabled": True, - "optional": True, + "optional": False, "active": True } } From a1d310fad04e210ac1cf60c86473346c5a3061e4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 14:39:07 +0200 Subject: [PATCH 153/192] AY-4801-exposed state of conversion from Settings in creator Settings have toggle to on/off conversion, this is exposed for Artist to decide ad-hoc. --- .../create/create_editorial_package.py | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py index 6a581b59d1..72a156dfb7 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py @@ -4,7 +4,12 @@ from ayon_core.pipeline import ( CreatedInstance, ) -from ayon_core.lib.attribute_definitions import FileDef +from ayon_core.lib.attribute_definitions import ( + FileDef, + BoolDef, + TextDef, + HiddenDef +) from ayon_core.hosts.traypublisher.api.plugin import TrayPublishCreator @@ -24,6 +29,16 @@ class EditorialPackageCreator(TrayPublishCreator): # Position batch creator after simple creators order = 120 + conversion_enabled = False + + def apply_settings(self, project_settings): + self.conversion_enabled = ( + project_settings["traypublisher"] + ["publish"] + ["ExtractEditorialPckgConversion"] + ["conversion_enabled"] + ) + print(project_settings) def get_icon(self): return "fa.folder" @@ -34,8 +49,9 @@ class EditorialPackageCreator(TrayPublishCreator): return instance_data["creator_attributes"] = { - "path": (Path(folder_path["directory"]) / - Path(folder_path["filenames"][0])).as_posix() + "folder_path": (Path(folder_path["directory"]) / + Path(folder_path["filenames"][0])).as_posix(), + "conversion_enabled": pre_create_data["conversion_enabled"] } # Create new instance @@ -53,7 +69,23 @@ class EditorialPackageCreator(TrayPublishCreator): extensions=[], allow_sequences=False, label="Folder path" - ) + ), + BoolDef("conversion_enabled", + tooltip="Convert to output defined in Settings.", + default=self.conversion_enabled, + label="Convert resources"), + ] + + def get_instance_attr_defs(self): + return [ + TextDef( + "folder_path", + label="Folder path", + disabled=True + ), + BoolDef("conversion_enabled", + tooltip="Convert to output defined in Settings.", + label="Convert resources"), ] def get_detail_description(self): From b55ed2e7869a508f33621b656cc03e23e8bd66d2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 14:39:42 +0200 Subject: [PATCH 154/192] AY-4801-updated variable name Old 'path' clashed in output of instance attributes in Publisher. --- .../traypublisher/plugins/publish/collect_editorial_package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py index 101f58b6d1..cb1277546c 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_package.py @@ -27,7 +27,7 @@ class CollectEditorialPackage(pyblish.api.InstancePlugin): families = ["editorial_pckg"] def process(self, instance): - folder_path = instance.data["creator_attributes"].get("path") + folder_path = instance.data["creator_attributes"]["folder_path"] if not folder_path or not os.path.exists(folder_path): self.log.info(( "Instance doesn't contain collected existing folder path." From 663ace6c8f23ae8faf61408c761aed6457d4c100 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 14:40:44 +0200 Subject: [PATCH 155/192] AY-4801-conversion controlled by instance attribute --- .../plugins/publish/extract_editorial_pckg.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py index 02f953d579..6b6f0bfe1d 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -45,14 +45,15 @@ class ExtractEditorialPckgConversion(publish.Extractor): publish_resource_folder) project_settings = instance.context.data["project_settings"] - profiles = (project_settings["traypublisher"] - ["publish"] - ["ExtractEditorialPckgConversion"] - .get("profiles")) - output_def = None - if profiles: - output_def = self._get_output_definition(instance, profiles) - if output_def: + output_def = (project_settings["traypublisher"] + ["publish"] + ["ExtractEditorialPckgConversion"] + ["output"]) + + conversion_enabled = (instance.data["creator_attributes"] + ["conversion_enabled"]) + + if conversion_enabled and output_def["ext"]: transfers = self._convert_resources(output_def, transfers) if not "transfers" in instance.data: From 0a45c5f8fff5e1bae29cec10033cff3263cfc638 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 14:41:10 +0200 Subject: [PATCH 156/192] AY-4801-removed profile filtering --- .../plugins/publish/extract_editorial_pckg.py | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py index 6b6f0bfe1d..e2eb4cb916 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -6,7 +6,7 @@ import opentimelineio import pyblish.api -from ayon_core.lib import filter_profiles, get_ffmpeg_tool_args, run_subprocess +from ayon_core.lib import get_ffmpeg_tool_args, run_subprocess from ayon_core.pipeline import publish @@ -81,26 +81,6 @@ class ExtractEditorialPckgConversion(publish.Extractor): os.makedirs(publish_resource_folder, exist_ok=True) return publish_resource_folder - def _get_output_definition(self, instance, profiles): - """Return appropriate profile by context information.""" - product_type = instance.data["productType"] - product_name = instance.data["productName"] - task_entity = instance.data["taskEntity"] or {} - task_name = task_entity.get("name") - task_type = task_entity.get("taskType") - filtering_criteria = { - "product_types": product_type, - "product_names": product_name, - "task_names": task_name, - "task_types": task_type, - } - profile = filter_profiles( - profiles, - filtering_criteria, - logger=self.log - ) - return profile - def _get_resource_path_mapping(self, instance, transfers): """Returns dict of {source_mov_path: rootless_published_path}.""" replace_paths = {} From f8503aa5dc7b62e0d5fdd9d982dc55b1204b604a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 14:41:37 +0200 Subject: [PATCH 157/192] AY-4801-removed multiple output definitions --- .../plugins/publish/extract_editorial_pckg.py | 107 +++++++++--------- 1 file changed, 52 insertions(+), 55 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py index e2eb4cb916..488b8e5a75 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -147,69 +147,66 @@ class ExtractEditorialPckgConversion(publish.Extractor): def _convert_resources(self, output_def, transfers): """Converts all resource files to configured format.""" - outputs = output_def["outputs"] - if not outputs: - self.log.warning("No output configured in " - "ayon+settings://traypublisher/publish/ExtractEditorialPckgConversion/profiles/0/outputs") # noqa + out_extension = output_def["ext"] + if not out_extension: + self.log.warning("No output extension configured in " + "ayon+settings://traypublisher/publish/ExtractEditorialPckgConversion") # noqa return transfers final_transfers = [] - # most likely only single output is expected - for output in outputs: - out_extension = output["ext"] - out_def_ffmpeg_args = output["ffmpeg_args"] - ffmpeg_input_args = [ - value.strip() - for value in out_def_ffmpeg_args["input"] - if value.strip() - ] - ffmpeg_video_filters = [ - value.strip() - for value in out_def_ffmpeg_args["video_filters"] - if value.strip() - ] - ffmpeg_audio_filters = [ - value.strip() - for value in out_def_ffmpeg_args["audio_filters"] - if value.strip() - ] - ffmpeg_output_args = [ - value.strip() - for value in out_def_ffmpeg_args["output"] - if value.strip() - ] - ffmpeg_input_args = self._split_ffmpeg_args(ffmpeg_input_args) + out_def_ffmpeg_args = output_def["ffmpeg_args"] + ffmpeg_input_args = [ + value.strip() + for value in out_def_ffmpeg_args["input"] + if value.strip() + ] + ffmpeg_video_filters = [ + value.strip() + for value in out_def_ffmpeg_args["video_filters"] + if value.strip() + ] + ffmpeg_audio_filters = [ + value.strip() + for value in out_def_ffmpeg_args["audio_filters"] + if value.strip() + ] + ffmpeg_output_args = [ + value.strip() + for value in out_def_ffmpeg_args["output"] + if value.strip() + ] + ffmpeg_input_args = self._split_ffmpeg_args(ffmpeg_input_args) - generic_args = [ - subprocess.list2cmdline(get_ffmpeg_tool_args("ffmpeg")) - ] - generic_args.extend(ffmpeg_input_args) - if ffmpeg_video_filters: - generic_args.append("-filter:v") - generic_args.append( - "\"{}\"".format(",".join(ffmpeg_video_filters))) + generic_args = [ + subprocess.list2cmdline(get_ffmpeg_tool_args("ffmpeg")) + ] + generic_args.extend(ffmpeg_input_args) + if ffmpeg_video_filters: + generic_args.append("-filter:v") + generic_args.append( + "\"{}\"".format(",".join(ffmpeg_video_filters))) - if ffmpeg_audio_filters: - generic_args.append("-filter:a") - generic_args.append( - "\"{}\"".format(",".join(ffmpeg_audio_filters))) + if ffmpeg_audio_filters: + generic_args.append("-filter:a") + generic_args.append( + "\"{}\"".format(",".join(ffmpeg_audio_filters))) - for source, destination in transfers: - base_name = os.path.basename(destination) - file_name, ext = os.path.splitext(base_name) - dest_path = os.path.join(os.path.dirname(destination), - f"{file_name}.{out_extension}") - final_transfers.append((source, dest_path)) + for source, destination in transfers: + base_name = os.path.basename(destination) + file_name, ext = os.path.splitext(base_name) + dest_path = os.path.join(os.path.dirname(destination), + f"{file_name}.{out_extension}") + final_transfers.append((source, dest_path)) - all_args = copy.deepcopy(generic_args) - all_args.append(f"-i {source}") - all_args.extend(ffmpeg_output_args) # order matters - all_args.append(f"{dest_path}") - subprcs_cmd = " ".join(all_args) + all_args = copy.deepcopy(generic_args) + all_args.append(f"-i {source}") + all_args.extend(ffmpeg_output_args) # order matters + all_args.append(f"{dest_path}") + subprcs_cmd = " ".join(all_args) - # run subprocess - self.log.debug("Executing: {}".format(subprcs_cmd)) - run_subprocess(subprcs_cmd, shell=True, logger=self.log) + # run subprocess + self.log.debug("Executing: {}".format(subprcs_cmd)) + run_subprocess(subprcs_cmd, shell=True, logger=self.log) return final_transfers def _split_ffmpeg_args(self, in_args): From ac2453db1e1cb37dd29cbd76cf9f9d61d0ca6751 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 10 May 2024 14:42:13 +0200 Subject: [PATCH 158/192] bump version to '0.3.1' --- client/ayon_core/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index a60de0493a..952df2a2c0 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.3.1-dev.1" +__version__ = "0.3.1" diff --git a/package.py b/package.py index fa3eaba9bd..4398bd9920 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.3.1-dev.1" +version = "0.3.1" client_dir = "ayon_core" From 0e4e845407d9f0c425b392d3f454dd5866b819b5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 10 May 2024 14:43:02 +0200 Subject: [PATCH 159/192] bump version to '0.3.2-dev.1' --- client/ayon_core/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 952df2a2c0..275e1b1dd6 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.3.1" +__version__ = "0.3.2-dev.1" diff --git a/package.py b/package.py index 4398bd9920..b7b8d2dae6 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.3.1" +version = "0.3.2-dev.1" client_dir = "ayon_core" From ba0918964f3766465104d278489d49bad7585233 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 14:41:57 +0200 Subject: [PATCH 160/192] AY-4801-updated validation message --- .../plugins/publish/extract_editorial_pckg.py | 8 +++++--- .../plugins/publish/validate_editorial_package.py | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py index 488b8e5a75..d25a7146a0 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -111,9 +111,11 @@ class ExtractEditorialPckgConversion(publish.Extractor): if not target_url: continue file_name = os.path.basename(target_url) - replace_value = replace_paths.get(file_name) - if replace_value: - clip.media_reference.target_url = replace_value + replace_path = replace_paths.get(file_name) + if replace_path: + clip.media_reference.target_url = replace_path + if clip.name == file_name: + clip.name = os.path.basename(replace_path) return otio_data diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py index 869dc73811..ce545610ea 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py @@ -47,8 +47,9 @@ class ValidateEditorialPackage(pyblish.api.InstancePlugin): missing_files.add(target_basename) if missing_files: - raise PublishValidationError("Otio file contains missing files " - f"'{missing_files}'.") + raise PublishValidationError( + "Otio file contains missing files `{missing_files}`.\n\n" + f"Please add them to `{folder_path}` and republish.") instance.data["editorial_pckg"]["otio_data"] = otio_data From 4cfc90bbb3b7bf97315b993028edc201280fabe4 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 10 May 2024 16:29:56 +0300 Subject: [PATCH 161/192] Add OPMenu Stencil --- .../hosts/houdini/startup/OPmenu.xml | 528 ++++++++++++++++++ 1 file changed, 528 insertions(+) create mode 100644 client/ayon_core/hosts/houdini/startup/OPmenu.xml diff --git a/client/ayon_core/hosts/houdini/startup/OPmenu.xml b/client/ayon_core/hosts/houdini/startup/OPmenu.xml new file mode 100644 index 0000000000..bdb559a084 --- /dev/null +++ b/client/ayon_core/hosts/houdini/startup/OPmenu.xml @@ -0,0 +1,528 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2c12a540c184b7d7323bdf5306d3dbae9031a787 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 10 May 2024 16:36:18 +0300 Subject: [PATCH 162/192] update OPMenu: add 'Create New (AYON)...' script item --- client/ayon_core/hosts/houdini/startup/OPmenu.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/client/ayon_core/hosts/houdini/startup/OPmenu.xml b/client/ayon_core/hosts/houdini/startup/OPmenu.xml index bdb559a084..0d58cf53fe 100644 --- a/client/ayon_core/hosts/houdini/startup/OPmenu.xml +++ b/client/ayon_core/hosts/houdini/startup/OPmenu.xml @@ -432,6 +432,21 @@ examples.load_token(kwargs['selectedtoken'], kwargs['node'], shift=kwargs['shift + + + + + + + + From 171ecf2be41c9d6e13e862b7479713d5f3ce221e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 15:53:50 +0200 Subject: [PATCH 163/192] AY-4801-bump up version of OpenTimelineIO --- client/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pyproject.toml b/client/pyproject.toml index 1a0ad7e5f2..5e811321f8 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -16,7 +16,7 @@ aiohttp_json_rpc = "*" # TVPaint server aiohttp-middlewares = "^2.0.0" wsrpc_aiohttp = "^3.1.1" # websocket server Click = "^8" -OpenTimelineIO = "0.14.1" +OpenTimelineIO = "0.16.0" opencolorio = "2.2.1" Pillow = "9.5.0" pynput = "^1.7.2" # Timers manager - TODO remove From c976261786de427ba2cff49afee44d5a6e28c99e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 16:04:02 +0200 Subject: [PATCH 164/192] AY-4801-added default setting to .mp4 conversion --- .../server/settings/publish_plugins.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/server_addon/traypublisher/server/settings/publish_plugins.py b/server_addon/traypublisher/server/settings/publish_plugins.py index 2afe20c865..dc659f6110 100644 --- a/server_addon/traypublisher/server/settings/publish_plugins.py +++ b/server_addon/traypublisher/server/settings/publish_plugins.py @@ -100,6 +100,21 @@ DEFAULT_PUBLISH_PLUGINS = { }, "ExtractEditorialPckgConversion": { "optional": False, - "active": True + "conversion_enabled": True, + "output": { + "ext": "", + "ffmpeg_args": { + "video_filters": [], + "audio_filters": [], + "input": [ + "-apply_trc gamma22" + ], + "output": [ + "-pix_fmt yuv420p", + "-crf 18", + "-intra" + ] + } + } } } From e19986a792972962be07d060249cfe3c56764ca3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 May 2024 16:26:10 +0200 Subject: [PATCH 165/192] AY-4801-bump up version of traypublisher package --- server_addon/traypublisher/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/traypublisher/package.py b/server_addon/traypublisher/package.py index 4ca8ae9fd3..c138a2296d 100644 --- a/server_addon/traypublisher/package.py +++ b/server_addon/traypublisher/package.py @@ -1,3 +1,3 @@ name = "traypublisher" title = "TrayPublisher" -version = "0.1.4" +version = "0.1.5" From 922db60bd031f5a071c5de84b595e9a7a6d5bab2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 May 2024 17:18:36 +0200 Subject: [PATCH 166/192] Add quotes to file paths in ExtractEditorialPckgConversion, improve error message handling in ValidateEditorialPackage. --- .../traypublisher/plugins/publish/extract_editorial_pckg.py | 5 ++--- .../plugins/publish/validate_editorial_package.py | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py index d25a7146a0..a4d8d2c6fb 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -201,9 +201,9 @@ class ExtractEditorialPckgConversion(publish.Extractor): final_transfers.append((source, dest_path)) all_args = copy.deepcopy(generic_args) - all_args.append(f"-i {source}") + all_args.append(f"-i \"{source}\"") all_args.extend(ffmpeg_output_args) # order matters - all_args.append(f"{dest_path}") + all_args.append(f"\"{dest_path}\"") subprcs_cmd = " ".join(all_args) # run subprocess @@ -232,4 +232,3 @@ class ExtractEditorialPckgConversion(publish.Extractor): if arg and arg not in splitted_args: splitted_args.append(arg) return splitted_args - diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py index ce545610ea..89594ce441 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py @@ -48,7 +48,7 @@ class ValidateEditorialPackage(pyblish.api.InstancePlugin): if missing_files: raise PublishValidationError( - "Otio file contains missing files `{missing_files}`.\n\n" + f"Otio file contains missing files `{missing_files}`.\n\n" f"Please add them to `{folder_path}` and republish.") instance.data["editorial_pckg"]["otio_data"] = otio_data @@ -67,5 +67,3 @@ class ValidateEditorialPackage(pyblish.api.InstancePlugin): target_urls.append(target_url) return target_urls - - From 86b2f3b5b52431f6cb11b91e771552c717fa6df8 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 10 May 2024 18:45:15 +0300 Subject: [PATCH 167/192] remove redundant elements --- .../hosts/houdini/startup/OPmenu.xml | 520 +----------------- 1 file changed, 3 insertions(+), 517 deletions(-) diff --git a/client/ayon_core/hosts/houdini/startup/OPmenu.xml b/client/ayon_core/hosts/houdini/startup/OPmenu.xml index 0d58cf53fe..0a7b265fa1 100644 --- a/client/ayon_core/hosts/houdini/startup/OPmenu.xml +++ b/client/ayon_core/hosts/houdini/startup/OPmenu.xml @@ -1,438 +1,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + opmenu.unsynchronize + opmenu.vhda_create @@ -447,97 +24,6 @@ create_interactive("io.openpype.creators.houdini.hda", **kwargs) ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 8dc0043d0308b3e823bd055ae7a54217d95462d6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 11 May 2024 00:00:52 +0800 Subject: [PATCH 168/192] add joint into accepted_controllers & make sure the bake animation doesn't get resample when it is exported --- .../hosts/maya/plugins/publish/extract_fbx_animation.py | 1 + .../hosts/maya/plugins/publish/validate_animated_reference.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py index ee66ed2fb7..36dc1b1544 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py @@ -36,6 +36,7 @@ class ExtractFBXAnimation(publish.Extractor): out_members = instance.data.get("animated_skeleton", []) # Export instance.data["constraints"] = True + instance.data["bakeResampleAnimation"] = False instance.data["skeletonDefinitions"] = True instance.data["referencedAssetsContent"] = True fbx_exporter.set_options_from_instance(instance) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py index 2ba2bff6fc..c9dcc662af 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py @@ -16,7 +16,7 @@ class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin, hosts = ["maya"] families = ["animation.fbx"] label = "Animated Reference Rig" - accepted_controllers = ["transform", "locator"] + accepted_controllers = ["transform", "locator", "joint"] actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] optional = False From 2d4bc1e7ca9546692a242a40e273d1e88fe6fcdf Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 11 May 2024 00:23:53 +0800 Subject: [PATCH 169/192] add inputchildren as options --- client/ayon_core/hosts/maya/api/fbx.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/maya/api/fbx.py b/client/ayon_core/hosts/maya/api/fbx.py index 3f1395cb40..437d64abbf 100644 --- a/client/ayon_core/hosts/maya/api/fbx.py +++ b/client/ayon_core/hosts/maya/api/fbx.py @@ -102,6 +102,7 @@ class FBXExtractor: "constraints": False, "lights": True, "embeddedTextures": False, + "includeChildren": True, "inputConnections": True, "upAxis": "y", "triangulate": False, From 828fe3ad266324fa64d20ec2cd17c7a9a4803a6f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 10 May 2024 19:14:34 +0200 Subject: [PATCH 170/192] Fix call to `create_context_node` --- .../ayon_core/hosts/houdini/plugins/create/create_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py index a958509e25..40a607e81a 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py @@ -95,7 +95,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator): # write workfile information to context container. op_ctx = hou.node(CONTEXT_CONTAINER) if not op_ctx: - op_ctx = self.create_context_node() + op_ctx = self.host.create_context_node() workfile_data = {"workfile": current_instance.data_to_store()} imprint(op_ctx, workfile_data) From f820050f7eb7ac95d3c4f07174a5a409e47d2d3a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 May 2024 18:39:33 +0800 Subject: [PATCH 171/192] add inputChildren into the dict from options function --- client/ayon_core/hosts/maya/api/fbx.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/maya/api/fbx.py b/client/ayon_core/hosts/maya/api/fbx.py index 437d64abbf..fd1bf2c901 100644 --- a/client/ayon_core/hosts/maya/api/fbx.py +++ b/client/ayon_core/hosts/maya/api/fbx.py @@ -59,6 +59,7 @@ class FBXExtractor: "constraints": bool, "lights": bool, "embeddedTextures": bool, + "includeChildren": bool, "inputConnections": bool, "upAxis": str, # x, y or z, "triangulate": bool, From 4a70e5bb0084285062527937c3b2d3858d36e506 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 May 2024 18:56:56 +0800 Subject: [PATCH 172/192] make sure the constraint is false when exporting --- .../hosts/maya/plugins/publish/extract_fbx_animation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py index 36dc1b1544..21a8abd46f 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py @@ -35,8 +35,7 @@ class ExtractFBXAnimation(publish.Extractor): fbx_exporter = fbx.FBXExtractor(log=self.log) out_members = instance.data.get("animated_skeleton", []) # Export - instance.data["constraints"] = True - instance.data["bakeResampleAnimation"] = False + instance.data["constraints"] = False instance.data["skeletonDefinitions"] = True instance.data["referencedAssetsContent"] = True fbx_exporter.set_options_from_instance(instance) From 8cf1d53e2b66eccc6040ebbc48f4f99c9e4a19d0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 May 2024 22:42:37 +0800 Subject: [PATCH 173/192] add TODO --- .../hosts/maya/plugins/publish/extract_fbx_animation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py index 21a8abd46f..77b5b79b5f 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py @@ -35,7 +35,8 @@ class ExtractFBXAnimation(publish.Extractor): fbx_exporter = fbx.FBXExtractor(log=self.log) out_members = instance.data.get("animated_skeleton", []) # Export - instance.data["constraints"] = False + # TODO: need to set up the options for users to set up + # the flags they intended to export instance.data["skeletonDefinitions"] = True instance.data["referencedAssetsContent"] = True fbx_exporter.set_options_from_instance(instance) From fb2714005999c262d5e023afd17c76ae9c296dca Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 May 2024 23:39:49 +0800 Subject: [PATCH 174/192] remove joint --- .../hosts/maya/plugins/publish/validate_animated_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py index c9dcc662af..2ba2bff6fc 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py @@ -16,7 +16,7 @@ class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin, hosts = ["maya"] families = ["animation.fbx"] label = "Animated Reference Rig" - accepted_controllers = ["transform", "locator", "joint"] + accepted_controllers = ["transform", "locator"] actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] optional = False From aa6b254326f052b05f95fee20e91fad784a54c1a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 14 May 2024 11:14:06 +0200 Subject: [PATCH 175/192] do not try to fix own overrides --- server/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/__init__.py b/server/__init__.py index 82473927b6..79f505ccd5 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -26,10 +26,14 @@ class CoreAddon(BaseServerAddon): def _convert_imagio_configs_0_3_1(self, overrides): """Imageio config settings did change to profiles since 0.3.1. .""" imageio_overrides = overrides.get("imageio") or {} - if "ocio_config" not in imageio_overrides: + if ( + "ocio_config" not in imageio_overrides + or "filepath" not in imageio_overrides["ocio_config"] + ): return ocio_config = imageio_overrides.pop("ocio_config") + filepath = ocio_config["filepath"] if not filepath: return From 37cd670f40a89206023adc0da2e6b11aff0ed273 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 14 May 2024 11:40:47 +0200 Subject: [PATCH 176/192] removed deprecated function 'get_template_data_from_session' --- client/ayon_core/pipeline/context_tools.py | 30 ---------------------- 1 file changed, 30 deletions(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 33567d7280..c32d04c44c 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -459,36 +459,6 @@ def is_representation_from_latest(representation): ) -def get_template_data_from_session(session=None, settings=None): - """Template data for template fill from session keys. - - Args: - session (Union[Dict[str, str], None]): The Session to use. If not - provided use the currently active global Session. - settings (Optional[Dict[str, Any]]): Prepared studio or project - settings. - - Returns: - Dict[str, Any]: All available data from session. - """ - - if session is not None: - project_name = session["AYON_PROJECT_NAME"] - folder_path = session["AYON_FOLDER_PATH"] - task_name = session["AYON_TASK_NAME"] - host_name = session["AYON_HOST_NAME"] - else: - context = get_current_context() - project_name = context["project_name"] - folder_path = context["folder_path"] - task_name = context["task_name"] - host_name = get_current_host_name() - - return get_template_data_with_names( - project_name, folder_path, task_name, host_name, settings - ) - - def get_current_context_template_data(settings=None): """Prepare template data for current context. From 217cd06f9a7c94b4bc0e12c54e2da5c48e73855d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 14 May 2024 12:31:50 +0200 Subject: [PATCH 177/192] fix ruff comments --- .../traypublisher/plugins/create/create_editorial_package.py | 1 - .../traypublisher/plugins/publish/extract_editorial_pckg.py | 2 -- .../plugins/publish/validate_editorial_package.py | 3 +-- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py index 830cfa5564..82b109be28 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py +++ b/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial_package.py @@ -8,7 +8,6 @@ from ayon_core.lib.attribute_definitions import ( FileDef, BoolDef, TextDef, - HiddenDef ) from ayon_core.hosts.traypublisher.api.plugin import TrayPublishCreator diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py index a4d8d2c6fb..6dd4e84704 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/extract_editorial_pckg.py @@ -56,8 +56,6 @@ class ExtractEditorialPckgConversion(publish.Extractor): if conversion_enabled and output_def["ext"]: transfers = self._convert_resources(output_def, transfers) - if not "transfers" in instance.data: - instance.data["transfers"] = [] instance.data["transfers"] = transfers source_to_rootless = self._get_resource_path_mapping(instance, diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py index 89594ce441..c63c4a6a73 100644 --- a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py +++ b/client/ayon_core/hosts/traypublisher/plugins/publish/validate_editorial_package.py @@ -22,8 +22,7 @@ class ValidateEditorialPackage(pyblish.api.InstancePlugin): def process(self, instance): editorial_pckg_data = instance.data.get("editorial_pckg") if not editorial_pckg_data: - raise PublishValidationError( - f"Editorial package not collected") + raise PublishValidationError("Editorial package not collected") folder_path = editorial_pckg_data["folder_path"] From 4dc893dfd571365337594ac2e764e69c230e352c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 May 2024 12:32:21 +0200 Subject: [PATCH 178/192] Nuke: Refactor metadata imprinting - Simplified code by removing 'author' attribute from metadata imprinting in various plugins. --- client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py | 4 ++-- client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py | 4 ++-- client/ayon_core/hosts/nuke/plugins/load/load_clip.py | 4 +--- client/ayon_core/hosts/nuke/plugins/load/load_effects.py | 2 -- client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py | 2 -- client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py | 2 -- client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py | 2 -- client/ayon_core/hosts/nuke/plugins/load/load_image.py | 3 +-- client/ayon_core/hosts/nuke/plugins/load/load_model.py | 4 ++-- .../ayon_core/hosts/nuke/plugins/load/load_script_precomp.py | 2 -- 10 files changed, 8 insertions(+), 21 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py index 7d823919dc..50af8a4eb9 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py @@ -62,7 +62,7 @@ class LoadBackdropNodes(load.LoaderPlugin): } # add attributes from the version to imprint to metadata knob - for k in ["source", "author", "fps"]: + for k in ["source", "fps"]: data_imprint[k] = version_attributes[k] # getting file path @@ -206,7 +206,7 @@ class LoadBackdropNodes(load.LoaderPlugin): "colorspaceInput": colorspace, } - for k in ["source", "author", "fps"]: + for k in ["source", "fps"]: data_imprint[k] = version_attributes[k] # adding nodes to node graph diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py index 14c54c3adc..3c7d4f3bb2 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py @@ -48,7 +48,7 @@ class AlembicCameraLoader(load.LoaderPlugin): "frameEnd": last, "version": version_entity["version"], } - for k in ["source", "author", "fps"]: + for k in ["source", "fps"]: data_imprint[k] = version_attributes[k] # getting file path @@ -123,7 +123,7 @@ class AlembicCameraLoader(load.LoaderPlugin): } # add attributes from the version to imprint to metadata knob - for k in ["source", "author", "fps"]: + for k in ["source", "fps"]: data_imprint[k] = version_attributes[k] # getting file path diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py index df8f2ab018..22203a5978 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_clip.py @@ -197,7 +197,6 @@ class LoadClip(plugin.NukeLoader): "frameStart", "frameEnd", "source", - "author", "fps", "handleStart", "handleEnd", @@ -347,8 +346,7 @@ class LoadClip(plugin.NukeLoader): "source": version_attributes.get("source"), "handleStart": str(self.handle_start), "handleEnd": str(self.handle_end), - "fps": str(version_attributes.get("fps")), - "author": version_attributes.get("author") + "fps": str(version_attributes.get("fps")) } last_version_entity = ayon_api.get_last_version_by_product_id( diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py index a87c81295a..be7420fcf0 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects.py @@ -69,7 +69,6 @@ class LoadEffects(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] @@ -189,7 +188,6 @@ class LoadEffects(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps", ]: data_imprint[k] = version_attributes[k] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py index 8fa1347598..9bb430b37b 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py @@ -69,7 +69,6 @@ class LoadEffectsInputProcess(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] @@ -192,7 +191,6 @@ class LoadEffectsInputProcess(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py index 95f85bacfc..57d00795ae 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py @@ -71,7 +71,6 @@ class LoadGizmo(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] @@ -139,7 +138,6 @@ class LoadGizmo(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py index 3112e27811..ed2b1ec458 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -73,7 +73,6 @@ class LoadGizmoInputProcess(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] @@ -145,7 +144,6 @@ class LoadGizmoInputProcess(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/client/ayon_core/hosts/nuke/plugins/load/load_image.py index d825b621fc..b5fccd8a0d 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_image.py @@ -133,7 +133,7 @@ class LoadImage(load.LoaderPlugin): "version": version_entity["version"], "colorspace": colorspace, } - for k in ["source", "author", "fps"]: + for k in ["source", "fps"]: data_imprint[k] = version_attributes.get(k, str(None)) r["tile_color"].setValue(int("0x4ecd25ff", 16)) @@ -207,7 +207,6 @@ class LoadImage(load.LoaderPlugin): "colorspace": version_attributes.get("colorSpace"), "source": version_attributes.get("source"), "fps": str(version_attributes.get("fps")), - "author": version_attributes.get("author") } # change color of node diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_model.py b/client/ayon_core/hosts/nuke/plugins/load/load_model.py index 0326e0a4fc..40862cd1e0 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_model.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_model.py @@ -47,7 +47,7 @@ class AlembicModelLoader(load.LoaderPlugin): "version": version_entity["version"] } # add attributes from the version to imprint to metadata knob - for k in ["source", "author", "fps"]: + for k in ["source", "fps"]: data_imprint[k] = version_attributes[k] # getting file path @@ -130,7 +130,7 @@ class AlembicModelLoader(load.LoaderPlugin): } # add additional metadata from the version to imprint to Avalon knob - for k in ["source", "author", "fps"]: + for k in ["source", "fps"]: data_imprint[k] = version_attributes[k] # getting file path diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py index 3e554f9d3b..d6699be164 100644 --- a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py +++ b/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py @@ -55,7 +55,6 @@ class LinkAsGroup(load.LoaderPlugin): "handleStart", "handleEnd", "source", - "author", "fps" ]: data_imprint[k] = version_attributes[k] @@ -131,7 +130,6 @@ class LinkAsGroup(load.LoaderPlugin): "colorspace": version_attributes.get("colorSpace"), "source": version_attributes.get("source"), "fps": version_attributes.get("fps"), - "author": version_attributes.get("author") } # Update the imprinted representation From 7f4385c9a23020b044a7a34a3de4aec6f3491543 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 14 May 2024 12:33:44 +0200 Subject: [PATCH 179/192] remove unused imports --- server_addon/traypublisher/server/settings/publish_plugins.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server_addon/traypublisher/server/settings/publish_plugins.py b/server_addon/traypublisher/server/settings/publish_plugins.py index dc659f6110..99a0bbf107 100644 --- a/server_addon/traypublisher/server/settings/publish_plugins.py +++ b/server_addon/traypublisher/server/settings/publish_plugins.py @@ -1,10 +1,6 @@ -from pydantic import validator - from ayon_server.settings import ( BaseSettingsModel, SettingsField, - task_types_enum, - ensure_unique_names ) From 53b25cb77e65a48ca10149d2eff743e6cf13fd3c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 14 May 2024 13:28:41 +0200 Subject: [PATCH 180/192] fix import --- client/ayon_core/modules/royalrender/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/modules/royalrender/api.py b/client/ayon_core/modules/royalrender/api.py index a69f88c43c..ef715811c5 100644 --- a/client/ayon_core/modules/royalrender/api.py +++ b/client/ayon_core/modules/royalrender/api.py @@ -7,7 +7,7 @@ from ayon_core.lib import Logger, run_subprocess, AYONSettingsRegistry from ayon_core.lib.vendor_bin_utils import find_tool_in_custom_paths from .rr_job import SubmitFile -from .rr_job import RRjob, SubmitterParameter # noqa F401 +from .rr_job import RRJob, SubmitterParameter # noqa F401 class Api: From c5b0a1e0cebf5c4c6cbf8768d41ead634ba07a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Tue, 14 May 2024 14:19:00 +0200 Subject: [PATCH 181/192] :bug: fix merge --- client/ayon_core/plugins/publish/integrate.py | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 33dc3024d1..865b566e6e 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -108,75 +108,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): label = "Integrate Asset" order = pyblish.api.IntegratorOrder -<<<<<<< enhancement/AY-4138_remove-explicit-products-type-list -- Incoming Change -======= - families = ["workfile", - "pointcache", - "pointcloud", - "proxyAbc", - "camera", - "animation", - "model", - "maxScene", - "mayaAscii", - "mayaScene", - "setdress", - "layout", - "ass", - "assProxy", - "vdbcache", - "scene", - "vrayproxy", - "vrayscene_layer", - "render", - "prerender", - "imagesequence", - "review", - "rendersetup", - "rig", - "plate", - "look", - "ociolook", - "audio", - "yetiRig", - "yeticache", - "nukenodes", - "gizmo", - "source", - "matchmove", - "image", - "assembly", - "fbx", - "gltf", - "textures", - "action", - "harmony.template", - "harmony.palette", - "editorial", - "background", - "camerarig", - "redshiftproxy", - "effect", - "xgen", - "hda", - "usd", - "staticMesh", - "skeletalMesh", - "mvLook", - "mvUsd", - "mvUsdComposition", - "mvUsdOverride", - "online", - "uasset", - "blendScene", - "yeticacheUE", - "tycache", - "csv_ingest_file", - "editorial_pckg", - "render.local.hou", - ] ->>>>>>> develop -- Current Change default_template_name = "publish" # Representation context keys that should always be written to From 1b05428eb73087b222890509e0a12c0e59a90654 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 10:17:30 +0200 Subject: [PATCH 182/192] expand default settings for readability --- server/settings/main.py | 42 ++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index d1cee32afa..40e16e7e91 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -268,26 +268,50 @@ DEFAULT_VALUES = { }, "studio_name": "", "studio_code": "", - "environments": '{\n"STUDIO_SW": {\n "darwin": "/mnt/REPO_SW",\n "linux": "/mnt/REPO_SW",\n "windows": "P:/REPO_SW"\n }\n}', + "environments": json.dumps( + { + "STUDIO_SW": { + "darwin": "/mnt/REPO_SW", + "linux": "/mnt/REPO_SW", + "windows": "P:/REPO_SW" + } + }, + indent=4 + ), "tools": DEFAULT_TOOLS_VALUES, - "version_start_category": {"profiles": []}, + "version_start_category": { + "profiles": [] + }, "publish": DEFAULT_PUBLISH_VALUES, "project_folder_structure": json.dumps( { "__project_root__": { "prod": {}, "resources": { - "footage": {"plates": {}, "offline": {}}, + "footage": { + "plates": {}, + "offline": {} + }, "audio": {}, - "art_dept": {}, + "art_dept": {} }, "editorial": {}, - "assets": {"characters": {}, "locations": {}}, - "shots": {}, + "assets": { + "characters": {}, + "locations": {} + }, + "shots": {} } }, - indent=4, + indent=4 ), - "project_plugins": {"windows": [], "darwin": [], "linux": []}, - "project_environments": "{}", + "project_plugins": { + "windows": [], + "darwin": [], + "linux": [] + }, + "project_environments": json.dumps( + {}, + indent=4 + ) } From b300db793a9af3d93e093df074dbf07685440f82 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:28:19 +0200 Subject: [PATCH 183/192] move deprecated functions at the end of file --- client/ayon_core/pipeline/colorspace.py | 215 ++++++++++++------------ 1 file changed, 106 insertions(+), 109 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 3503d0c534..c081b58752 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -195,17 +195,6 @@ def get_colorspace_name_from_filepath( return colorspace_name -# TODO: remove this in future - backward compatibility -@deprecated("get_imageio_file_rules_colorspace_from_filepath") -def get_imageio_colorspace_from_filepath(*args, **kwargs): - return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs) - -# TODO: remove this in future - backward compatibility -@deprecated("get_imageio_file_rules_colorspace_from_filepath") -def get_colorspace_from_filepath(*args, **kwargs): - return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs) - - def get_imageio_file_rules_colorspace_from_filepath( filepath, host_name, @@ -394,21 +383,6 @@ def validate_imageio_colorspace_in_config(config_path, colorspace_name): return True -# TODO: remove this in future - backward compatibility -@deprecated("_get_wrapped_with_subprocess") -def get_data_subprocess(config_path, data_type): - """[Deprecated] Get data via subprocess - - Wrapper for Python 2 hosts. - - Args: - config_path (str): path leading to config.ocio file - """ - return _get_wrapped_with_subprocess( - "config", data_type, in_path=config_path, - ) - - def _get_wrapped_with_subprocess(command_group, command, **kwargs): """Get data via subprocess @@ -673,24 +647,6 @@ def get_colorspaces_enumerator_items( return labeled_colorspaces -# TODO: remove this in future - backward compatibility -@deprecated("_get_wrapped_with_subprocess") -def get_colorspace_data_subprocess(config_path): - """[Deprecated] Get colorspace data via subprocess - - Wrapper for Python 2 hosts. - - Args: - config_path (str): path leading to config.ocio file - - Returns: - dict: colorspace and family in couple - """ - return _get_wrapped_with_subprocess( - "config", "get_colorspace", in_path=config_path - ) - - def get_ocio_config_views(config_path): """Get all viewer data @@ -716,71 +672,6 @@ def get_ocio_config_views(config_path): return _get_views_data(config_path) -# TODO: remove this in future - backward compatibility -@deprecated("_get_wrapped_with_subprocess") -def get_views_data_subprocess(config_path): - """[Deprecated] Get viewers data via subprocess - - Wrapper for Python 2 hosts. - - Args: - config_path (str): path leading to config.ocio file - - Returns: - dict: `display/viewer` and viewer data - """ - return _get_wrapped_with_subprocess( - "config", "get_views", in_path=config_path - ) - - -@deprecated("get_imageio_config_preset") -def get_imageio_config( - project_name, - host_name, - project_settings=None, - anatomy_data=None, - anatomy=None, - env=None -): - """Returns config data from settings - - Config path is formatted in `path` key - and original settings input is saved into `template` key. - - Deprecated: - Deprecated since '0.3.1' . Use `get_imageio_config_preset` instead. - - Args: - project_name (str): project name - host_name (str): host name - project_settings (Optional[dict]): Project settings. - anatomy_data (Optional[dict]): anatomy formatting data. - anatomy (Optional[Anatomy]): Anatomy object. - env (Optional[dict]): Environment variables. - - Returns: - dict: config path data or empty dict - - """ - if not anatomy_data: - from .context_tools import get_current_context_template_data - anatomy_data = get_current_context_template_data() - - task_name = anatomy_data.get("task", {}).get("name") - folder_path = anatomy_data.get("folder", {}).get("path") - return get_imageio_config_preset( - project_name, - folder_path, - task_name, - host_name, - anatomy=anatomy, - project_settings=project_settings, - template_data=anatomy_data, - env=env, - ) - - def _get_global_config_data( project_name, host_name, @@ -1437,3 +1328,109 @@ def get_current_context_imageio_config_preset( template_data=template_data, env=env, ) + + +# --- Deprecated functions --- +@deprecated("get_imageio_file_rules_colorspace_from_filepath") +def get_imageio_colorspace_from_filepath(*args, **kwargs): + return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs) + + +@deprecated("get_imageio_file_rules_colorspace_from_filepath") +def get_colorspace_from_filepath(*args, **kwargs): + return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs) + + +@deprecated("_get_wrapped_with_subprocess") +def get_colorspace_data_subprocess(config_path): + """[Deprecated] Get colorspace data via subprocess + + Wrapper for Python 2 hosts. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: colorspace and family in couple + """ + return _get_wrapped_with_subprocess( + "config", "get_colorspace", in_path=config_path + ) + + +@deprecated("_get_wrapped_with_subprocess") +def get_views_data_subprocess(config_path): + """[Deprecated] Get viewers data via subprocess + + Wrapper for Python 2 hosts. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: `display/viewer` and viewer data + """ + return _get_wrapped_with_subprocess( + "config", "get_views", in_path=config_path + ) + + +@deprecated("_get_wrapped_with_subprocess") +def get_data_subprocess(config_path, data_type): + """[Deprecated] Get data via subprocess + + Wrapper for Python 2 hosts. + + Args: + config_path (str): path leading to config.ocio file + """ + return _get_wrapped_with_subprocess( + "config", data_type, in_path=config_path, + ) + + +@deprecated("get_imageio_config_preset") +def get_imageio_config( + project_name, + host_name, + project_settings=None, + anatomy_data=None, + anatomy=None, + env=None +): + """Returns config data from settings + + Config path is formatted in `path` key + and original settings input is saved into `template` key. + + Deprecated: + Deprecated since '0.3.1' . Use `get_imageio_config_preset` instead. + + Args: + project_name (str): project name + host_name (str): host name + project_settings (Optional[dict]): Project settings. + anatomy_data (Optional[dict]): anatomy formatting data. + anatomy (Optional[Anatomy]): Anatomy object. + env (Optional[dict]): Environment variables. + + Returns: + dict: config path data or empty dict + + """ + if not anatomy_data: + from .context_tools import get_current_context_template_data + anatomy_data = get_current_context_template_data() + + task_name = anatomy_data.get("task", {}).get("name") + folder_path = anatomy_data.get("folder", {}).get("path") + return get_imageio_config_preset( + project_name, + folder_path, + task_name, + host_name, + anatomy=anatomy, + project_settings=project_settings, + template_data=anatomy_data, + env=env, + ) From 4d398e21371ff8a840d14f35887a4107bbcf46f1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:32:04 +0200 Subject: [PATCH 184/192] added functions that are using 'PyOpenColorIO' --- client/ayon_core/pipeline/colorspace.py | 170 ++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index c081b58752..562e698f75 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1294,6 +1294,176 @@ def _get_display_view_colorspace_subprocess(config_path, display, view): return json.load(f) +# --- Implementation of logic using 'PyOpenColorIO' --- +def _get_ocio_config(config_path): + """Helper function to create OCIO config object. + + Args: + config_path (str): Path to config. + + Returns: + PyOpenColorIO.Config: OCIO config for the confing path. + + """ + import PyOpenColorIO + + config_path = os.path.abspath(config_path) + + if not os.path.isfile(config_path): + raise IOError("Input path should be `config.ocio` file") + + return PyOpenColorIO.Config.CreateFromFile(config_path) + + +def _get_config_file_rules_colorspace_from_filepath(config_path, filepath): + """Return found colorspace data found in v2 file rules. + + Args: + config_path (str): path string leading to config.ocio + filepath (str): path string leading to v2 file rules + + Raises: + IOError: Input config does not exist. + + Returns: + dict: aggregated available colorspaces + + """ + config = _get_ocio_config(config_path) + + # TODO: use `parseColorSpaceFromString` instead if ocio v1 + return config.getColorSpaceFromFilepath(str(filepath)) + + +def _get_config_version_data(config_path): + """Return major and minor version info. + + Args: + config_path (str): path string leading to config.ocio + + Raises: + IOError: Input config does not exist. + + Returns: + dict: minor and major keys with values + + """ + config = _get_ocio_config(config_path) + + return { + "major": config.getMajorVersion(), + "minor": config.getMinorVersion() + } + + +def _get_display_view_colorspace_name(config_path, display, view): + """Returns the colorspace attribute of the (display, view) pair. + + Args: + config_path (str): path string leading to config.ocio + display (str): display name e.g. "ACES" + view (str): view name e.g. "sRGB" + + Raises: + IOError: Input config does not exist. + + Returns: + str: view color space name e.g. "Output - sRGB" + + """ + config = _get_ocio_config(config_path) + return config.getDisplayViewColorSpaceName(display, view) + + +def _get_ocio_config_colorspaces(config_path): + """Return all found colorspace data. + + Args: + config_path (str): path string leading to config.ocio + + Raises: + IOError: Input config does not exist. + + Returns: + dict: aggregated available colorspaces + + """ + config = _get_ocio_config(config_path) + + colorspace_data = { + "roles": {}, + "colorspaces": { + color.getName(): { + "family": color.getFamily(), + "categories": list(color.getCategories()), + "aliases": list(color.getAliases()), + "equalitygroup": color.getEqualityGroup(), + } + for color in config.getColorSpaces() + }, + "displays_views": { + f"{view} ({display})": { + "display": display, + "view": view + + } + for display in config.getDisplays() + for view in config.getViews(display) + }, + "looks": {} + } + + # add looks + looks = config.getLooks() + if looks: + colorspace_data["looks"] = { + look.getName(): {"process_space": look.getProcessSpace()} + for look in looks + } + + # add roles + roles = config.getRoles() + if roles: + colorspace_data["roles"] = { + role: {"colorspace": colorspace} + for (role, colorspace) in roles + } + + return colorspace_data + + +def _get_ocio_config_views(config_path): + """Return all found viewer data. + + Args: + config_path (str): path string leading to config.ocio + + Raises: + IOError: Input config does not exist. + + Returns: + dict: aggregated available viewers + + """ + config = _get_ocio_config(config_path) + + output = {} + for display in config.getDisplays(): + for view in config.getViews(display): + colorspace = config.getDisplayViewColorSpaceName(display, view) + # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views # noqa + if colorspace == "": + colorspace = display + + output[f"{display}/{view}"] = { + "display": display, + "view": view, + "colorspace": colorspace + } + + return output + + # --- Current context functions --- def get_current_context_imageio_config_preset( anatomy=None, From b0340f4f3b94e1dae75305410dda2c33c70ad03a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:35:42 +0200 Subject: [PATCH 185/192] renamed 'compatibility_check' to 'has_compatible_ocio_package' --- client/ayon_core/pipeline/colorspace.py | 58 ++++++++++++++++--------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 562e698f75..a503c37101 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -107,6 +107,27 @@ def _make_temp_json_file(): os.remove(temporary_json_filepath) +def has_compatible_ocio_package(): + """Current process has available compatible 'PyOpenColorIO'. + + Returns: + bool: True if compatible package is available. + + """ + if CachedData.has_compatible_ocio_package is not None: + return CachedData.has_compatible_ocio_package + + try: + import PyOpenColorIO # noqa: F401 + # TODO validate 'PyOpenColorIO' version + CachedData.has_compatible_ocio_package = True + except ImportError: + CachedData.has_compatible_ocio_package = False + + # compatible + return CachedData.has_compatible_ocio_package + + def get_ocio_config_script_path(): """Get path to ocio wrapper script @@ -261,7 +282,7 @@ def get_config_file_rules_colorspace_from_filepath(config_path, filepath): Returns: Union[str, None]: matching colorspace name """ - if not compatibility_check(): + if not has_compatible_ocio_package(): # python environment is not compatible with PyOpenColorIO # needs to be run in subprocess result_data = _get_wrapped_with_subprocess( @@ -418,28 +439,12 @@ def _get_wrapped_with_subprocess(command_group, command, **kwargs): return json.load(f_) -# TODO: this should be part of ocio_wrapper.py -def compatibility_check(): - """Making sure PyOpenColorIO is importable""" - if CachedData.has_compatible_ocio_package is not None: - return CachedData.has_compatible_ocio_package - - try: - import PyOpenColorIO # noqa: F401 - CachedData.has_compatible_ocio_package = True - except ImportError: - CachedData.has_compatible_ocio_package = False - - # compatible - return CachedData.has_compatible_ocio_package - - # TODO: this should be part of ocio_wrapper.py def compatibility_check_config_version(config_path, major=1, minor=None): """Making sure PyOpenColorIO config version is compatible""" if not CachedData.config_version_data.get(config_path): - if compatibility_check(): + if has_compatible_ocio_package(): # TODO: refactor this so it is not imported but part of this file from ayon_core.scripts.ocio_wrapper import _get_version_data @@ -479,7 +484,7 @@ def get_ocio_config_colorspaces(config_path): dict: colorspace and family in couple """ if not CachedData.ocio_config_colorspaces.get(config_path): - if not compatibility_check(): + if not has_compatible_ocio_package(): # python environment is not compatible with PyOpenColorIO # needs to be run in subprocess config_colorspaces = _get_wrapped_with_subprocess( @@ -659,7 +664,7 @@ def get_ocio_config_views(config_path): Returns: dict: `display/viewer` and viewer data """ - if not compatibility_check(): + if not has_compatible_ocio_package(): # python environment is not compatible with PyOpenColorIO # needs to be run in subprocess return _get_wrapped_with_subprocess( @@ -1250,7 +1255,7 @@ def get_display_view_colorspace_name(config_path, display, view): str: View color space name. e.g. "Output - sRGB" """ - if not compatibility_check(): + if not has_compatible_ocio_package(): # python environment is not compatible with PyOpenColorIO # needs to be run in subprocess return _get_display_view_colorspace_subprocess( @@ -1501,6 +1506,17 @@ def get_current_context_imageio_config_preset( # --- Deprecated functions --- +@deprecated("has_compatible_ocio_package") +def compatibility_check(): + """Making sure PyOpenColorIO is importable + + Deprecated: + Deprecated since '0.3.2'. Use `has_compatible_ocio_package` instead. + """ + + return has_compatible_ocio_package() + + @deprecated("get_imageio_file_rules_colorspace_from_filepath") def get_imageio_colorspace_from_filepath(*args, **kwargs): return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs) From c668ab49397b564dab51a9f901ef9e76a4974d87 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:38:41 +0200 Subject: [PATCH 186/192] modified ocio wrapper to use new functions --- client/ayon_core/scripts/ocio_wrapper.py | 480 +++++++---------------- 1 file changed, 142 insertions(+), 338 deletions(-) diff --git a/client/ayon_core/scripts/ocio_wrapper.py b/client/ayon_core/scripts/ocio_wrapper.py index 0a78e33c1f..9cbab32956 100644 --- a/client/ayon_core/scripts/ocio_wrapper.py +++ b/client/ayon_core/scripts/ocio_wrapper.py @@ -1,28 +1,31 @@ """OpenColorIO Wrapper. -Only to be interpreted by Python 3. It is run in subprocess in case -Python 2 hosts needs to use it. Or it is used as module for Python 3 -processing. - -Providing functionality: -- get_colorspace - console command - python 2 - - returning all available color spaces - found in input config path. -- _get_colorspace_data - python 3 - module function - - returning all available colorspaces - found in input config path. -- get_views - console command - python 2 - - returning all available viewers - found in input config path. -- _get_views_data - python 3 - module function - - returning all available viewers - found in input config path. +Receive OpenColorIO information and store it in JSON format for processed +that don't have access to OpenColorIO or their version of OpenColorIO is +not compatible. """ -import click import json from pathlib import Path -import PyOpenColorIO as ocio + +import click + +from ayon_core.pipeline.colorspace import ( + has_compatible_ocio_package, + get_display_view_colorspace_name, + get_config_file_rules_colorspace_from_filepath, + get_config_version_data, + get_ocio_config_views, + get_ocio_config_colorspaces, +) + + +def _save_output_to_json_file(output, output_path): + json_path = Path(output_path) + with open(json_path, "w") as stream: + json.dump(output, stream) + + print(f"Data are saved to '{json_path}'") @click.group() @@ -51,383 +54,184 @@ def colorspace(): @config.command( - name="get_colorspace", - help=( - "return all colorspaces from config file " - "--path input arg is required" - ) -) -@click.option("--in_path", required=True, - help="path where to read ocio config file", - type=click.Path(exists=True)) -@click.option("--out_path", required=True, - help="path where to write output json file", - type=click.Path()) -def get_colorspace(in_path, out_path): + name="get_ocio_config_colorspaces", + help="return all colorspaces from config file") +@click.option( + "--config_path", + required=True, + help="OCIO config path to read ocio config file.", + type=click.Path(exists=True)) +@click.option( + "--output_path", + required=True, + help="path where to write output json file", + type=click.Path()) +def _get_ocio_config_colorspaces(config_path, output_path): """Aggregate all colorspace to file. - Python 2 wrapped console command - Args: - in_path (str): config file path string - out_path (str): temp json file path string + config_path (str): config file path string + output_path (str): temp json file path string Example of use: > pyton.exe ./ocio_wrapper.py config get_colorspace - --in_path= --out_path= + --config_path --output_path """ - json_path = Path(out_path) - - out_data = _get_colorspace_data(in_path) - - with open(json_path, "w") as f_: - json.dump(out_data, f_) - - print(f"Colorspace data are saved to '{json_path}'") - - -def _get_colorspace_data(config_path): - """Return all found colorspace data. - - Args: - config_path (str): path string leading to config.ocio - - Raises: - IOError: Input config does not exist. - - Returns: - dict: aggregated available colorspaces - """ - config_path = Path(config_path) - - if not config_path.is_file(): - raise IOError( - f"Input path `{config_path}` should be `config.ocio` file") - - config = ocio.Config().CreateFromFile(str(config_path)) - - colorspace_data = { - "roles": {}, - "colorspaces": { - color.getName(): { - "family": color.getFamily(), - "categories": list(color.getCategories()), - "aliases": list(color.getAliases()), - "equalitygroup": color.getEqualityGroup(), - } - for color in config.getColorSpaces() - }, - "displays_views": { - f"{view} ({display})": { - "display": display, - "view": view - - } - for display in config.getDisplays() - for view in config.getViews(display) - }, - "looks": {} - } - - # add looks - looks = config.getLooks() - if looks: - colorspace_data["looks"] = { - look.getName(): {"process_space": look.getProcessSpace()} - for look in looks - } - - # add roles - roles = config.getRoles() - if roles: - colorspace_data["roles"] = { - role: {"colorspace": colorspace} - for (role, colorspace) in roles - } - - return colorspace_data + _save_output_to_json_file( + get_ocio_config_colorspaces(config_path), + output_path + ) @config.command( - name="get_views", - help=( - "return all viewers from config file " - "--path input arg is required" - ) -) -@click.option("--in_path", required=True, - help="path where to read ocio config file", - type=click.Path(exists=True)) -@click.option("--out_path", required=True, - help="path where to write output json file", - type=click.Path()) -def get_views(in_path, out_path): + name="get_ocio_config_views", + help="All viewers from config file") +@click.option( + "--config_path", + required=True, + help="OCIO config path to read ocio config file.", + type=click.Path(exists=True)) +@click.option( + "--output_path", + required=True, + help="path where to write output json file", + type=click.Path()) +def _get_ocio_config_views(config_path, output_path): """Aggregate all viewers to file. - Python 2 wrapped console command - Args: - in_path (str): config file path string - out_path (str): temp json file path string + config_path (str): config file path string + output_path (str): temp json file path string Example of use: > pyton.exe ./ocio_wrapper.py config get_views \ - --in_path= --out_path= + --config_path --output """ - json_path = Path(out_path) - - out_data = _get_views_data(in_path) - - with open(json_path, "w") as f_: - json.dump(out_data, f_) - - print(f"Viewer data are saved to '{json_path}'") - - -def _get_views_data(config_path): - """Return all found viewer data. - - Args: - config_path (str): path string leading to config.ocio - - Raises: - IOError: Input config does not exist. - - Returns: - dict: aggregated available viewers - """ - config_path = Path(config_path) - - if not config_path.is_file(): - raise IOError("Input path should be `config.ocio` file") - - config = ocio.Config().CreateFromFile(str(config_path)) - - data_ = {} - for display in config.getDisplays(): - for view in config.getViews(display): - colorspace = config.getDisplayViewColorSpaceName(display, view) - # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views # noqa - if colorspace == "": - colorspace = display - - data_[f"{display}/{view}"] = { - "display": display, - "view": view, - "colorspace": colorspace - } - - return data_ + _save_output_to_json_file( + get_ocio_config_views(config_path), + output_path + ) @config.command( - name="get_version", - help=( - "return major and minor version from config file " - "--config_path input arg is required" - "--out_path input arg is required" - ) -) -@click.option("--config_path", required=True, - help="path where to read ocio config file", - type=click.Path(exists=True)) -@click.option("--out_path", required=True, - help="path where to write output json file", - type=click.Path()) -def get_version(config_path, out_path): + name="get_config_version_data", + help="Get major and minor version from config file") +@click.option( + "--config_path", + required=True, + help="OCIO config path to read ocio config file.", + type=click.Path(exists=True)) +@click.option( + "--output_path", + required=True, + help="path where to write output json file", + type=click.Path()) +def _get_config_version_data(config_path, output_path): """Get version of config. - Python 2 wrapped console command - Args: config_path (str): ocio config file path string - out_path (str): temp json file path string + output_path (str): temp json file path string Example of use: > pyton.exe ./ocio_wrapper.py config get_version \ - --config_path= --out_path= + --config_path --output_path """ - json_path = Path(out_path) - - out_data = _get_version_data(config_path) - - with open(json_path, "w") as f_: - json.dump(out_data, f_) - - print(f"Config version data are saved to '{json_path}'") - - -def _get_version_data(config_path): - """Return major and minor version info. - - Args: - config_path (str): path string leading to config.ocio - - Raises: - IOError: Input config does not exist. - - Returns: - dict: minor and major keys with values - """ - config_path = Path(config_path) - - if not config_path.is_file(): - raise IOError("Input path should be `config.ocio` file") - - config = ocio.Config().CreateFromFile(str(config_path)) - - return { - "major": config.getMajorVersion(), - "minor": config.getMinorVersion() - } + _save_output_to_json_file( + get_config_version_data(config_path), + output_path + ) @colorspace.command( name="get_config_file_rules_colorspace_from_filepath", - help=( - "return colorspace from filepath " - "--config_path - ocio config file path (input arg is required) " - "--filepath - any file path (input arg is required) " - "--out_path - temp json file path (input arg is required)" - ) -) -@click.option("--config_path", required=True, - help="path where to read ocio config file", - type=click.Path(exists=True)) -@click.option("--filepath", required=True, - help="path to file to get colorspace from", - type=click.Path()) -@click.option("--out_path", required=True, - help="path where to write output json file", - type=click.Path()) -def get_config_file_rules_colorspace_from_filepath( - config_path, filepath, out_path + help="Colorspace file rules from filepath") +@click.option( + "--config_path", + required=True, + help="OCIO config path to read ocio config file.", + type=click.Path(exists=True)) +@click.option( + "--filepath", + equired=True, + help="Path to file to get colorspace from.", + type=click.Path()) +@click.option( + "--output_path", + required=True, + help="Path where to write output json file.", + type=click.Path()) +def _get_config_file_rules_colorspace_from_filepath( + config_path, filepath, output_path ): """Get colorspace from file path wrapper. - Python 2 wrapped console command - Args: config_path (str): config file path string filepath (str): path string leading to file - out_path (str): temp json file path string + output_path (str): temp json file path string Example of use: - > pyton.exe ./ocio_wrapper.py \ + > python.exe ./ocio_wrapper.py \ colorspace get_config_file_rules_colorspace_from_filepath \ - --config_path= --filepath= --out_path= + --config_path --filepath --output_path """ - json_path = Path(out_path) - - colorspace = _get_config_file_rules_colorspace_from_filepath( - config_path, filepath) - - with open(json_path, "w") as f_: - json.dump(colorspace, f_) - - print(f"Colorspace name is saved to '{json_path}'") - - -def _get_config_file_rules_colorspace_from_filepath(config_path, filepath): - """Return found colorspace data found in v2 file rules. - - Args: - config_path (str): path string leading to config.ocio - filepath (str): path string leading to v2 file rules - - Raises: - IOError: Input config does not exist. - - Returns: - dict: aggregated available colorspaces - """ - config_path = Path(config_path) - - if not config_path.is_file(): - raise IOError( - f"Input path `{config_path}` should be `config.ocio` file") - - config = ocio.Config().CreateFromFile(str(config_path)) - - # TODO: use `parseColorSpaceFromString` instead if ocio v1 - colorspace = config.getColorSpaceFromFilepath(str(filepath)) - - return colorspace - - -def _get_display_view_colorspace_name(config_path, display, view): - """Returns the colorspace attribute of the (display, view) pair. - - Args: - config_path (str): path string leading to config.ocio - display (str): display name e.g. "ACES" - view (str): view name e.g. "sRGB" - - - Raises: - IOError: Input config does not exist. - - Returns: - view color space name (str) e.g. "Output - sRGB" - """ - - config_path = Path(config_path) - - if not config_path.is_file(): - raise IOError("Input path should be `config.ocio` file") - - config = ocio.Config.CreateFromFile(str(config_path)) - colorspace = config.getDisplayViewColorSpaceName(display, view) - - return colorspace + _save_output_to_json_file( + get_config_file_rules_colorspace_from_filepath(config_path, filepath), + output_path + ) @config.command( name="get_display_view_colorspace_name", help=( - "return default view colorspace name " - "for the given display and view " - "--path input arg is required" - ) -) -@click.option("--in_path", required=True, - help="path where to read ocio config file", - type=click.Path(exists=True)) -@click.option("--out_path", required=True, - help="path where to write output json file", - type=click.Path()) -@click.option("--display", required=True, - help="display name", - type=click.STRING) -@click.option("--view", required=True, - help="view name", - type=click.STRING) -def get_display_view_colorspace_name(in_path, out_path, - display, view): + "Default view colorspace name for the given display and view" + )) +@click.option( + "--config_path", + required=True, + help="path where to read ocio config file", + type=click.Path(exists=True)) +@click.option( + "--display", + required=True, + help="Display name", + type=click.STRING) +@click.option( + "--view", + required=True, + help="view name", + type=click.STRING) +@click.option( + "--output_path", + required=True, + help="path where to write output json file", + type=click.Path()) +def _get_display_view_colorspace_name( + config_path, display, view, output_path +): """Aggregate view colorspace name to file. Wrapper command for processes without access to OpenColorIO Args: - in_path (str): config file path string - out_path (str): temp json file path string + config_path (str): config file path string + output_path (str): temp json file path string display (str): display name e.g. "ACES" view (str): view name e.g. "sRGB" Example of use: > pyton.exe ./ocio_wrapper.py config \ - get_display_view_colorspace_name --in_path= \ - --out_path= --display= --view= + get_display_view_colorspace_name --config_path \ + --output_path --display --view """ + _save_output_to_json_file( + get_display_view_colorspace_name(config_path, display, view), + output_path + ) - out_data = _get_display_view_colorspace_name(in_path, - display, - view) - with open(out_path, "w") as f: - json.dump(out_data, f) - - print(f"Display view colorspace saved to '{out_path}'") - -if __name__ == '__main__': +if __name__ == "__main__": + if not has_compatible_ocio_package(): + raise RuntimeError("OpenColorIO is not available.") main() From b23faf324903e5c5b4ae2deaa76087c3358b405d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:40:50 +0200 Subject: [PATCH 187/192] modified existing functions to use functions from colorspace.py --- client/ayon_core/pipeline/colorspace.py | 286 +++++++++++------------- 1 file changed, 132 insertions(+), 154 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index a503c37101..c347638937 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -18,11 +18,10 @@ from ayon_core.lib import ( run_ayon_launcher_process, Logger, ) +from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS from ayon_core.pipeline import Anatomy from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.load import get_representation_path_with_anatomy -from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS - log = Logger.get_logger(__name__) @@ -281,26 +280,50 @@ def get_config_file_rules_colorspace_from_filepath(config_path, filepath): Returns: Union[str, None]: matching colorspace name + """ - if not has_compatible_ocio_package(): - # python environment is not compatible with PyOpenColorIO - # needs to be run in subprocess + if has_compatible_ocio_package(): + result_data = _get_config_file_rules_colorspace_from_filepath( + config_path, filepath + ) + else: result_data = _get_wrapped_with_subprocess( - "colorspace", "get_config_file_rules_colorspace_from_filepath", + "colorspace", + "get_config_file_rules_colorspace_from_filepath", config_path=config_path, filepath=filepath ) - if result_data: - return result_data[0] - - # TODO: refactor this so it is not imported but part of this file - from ayon_core.scripts.ocio_wrapper import _get_config_file_rules_colorspace_from_filepath # noqa: E501 - - result_data = _get_config_file_rules_colorspace_from_filepath( - config_path, filepath) if result_data: return result_data[0] + return None + + +def get_config_version_data(config_path): + """Return major and minor version info. + + Args: + config_path (str): path string leading to config.ocio + + Raises: + IOError: Input config does not exist. + + Returns: + dict: minor and major keys with values + + """ + if config_path not in CachedData.config_version_data: + if has_compatible_ocio_package(): + version_data = _get_config_version_data(config_path) + else: + version_data = _get_wrapped_with_subprocess( + "config", + "get_config_version_data", + config_path=config_path + ) + CachedData.config_version_data[config_path] = version_data + + return deepcopy(CachedData.config_version_data[config_path]) def parse_colorspace_from_filepath( @@ -394,6 +417,7 @@ def validate_imageio_colorspace_in_config(config_path, colorspace_name): Returns: bool: True if exists + """ colorspaces = get_ocio_config_colorspaces(config_path)["colorspaces"] if colorspace_name not in colorspaces: @@ -405,9 +429,7 @@ def validate_imageio_colorspace_in_config(config_path, colorspace_name): def _get_wrapped_with_subprocess(command_group, command, **kwargs): - """Get data via subprocess - - Wrapper for Python 2 hosts. + """Get data via subprocess. Args: command_group (str): command group name @@ -420,14 +442,16 @@ def _get_wrapped_with_subprocess(command_group, command, **kwargs): with _make_temp_json_file() as tmp_json_path: # Prepare subprocess arguments args = [ - "run", get_ocio_config_script_path(), - command_group, command + "run", + get_ocio_config_script_path(), + command_group, + command ] - for key_, value_ in kwargs.items(): - args.extend(("--{}".format(key_), value_)) + for key, value in kwargs.items(): + args.extend(("--{}".format(key), value)) - args.append("--out_path") + args.append("--output_path") args.append(tmp_json_path) log.info("Executing: {}".format(" ".join(args))) @@ -435,39 +459,23 @@ def _get_wrapped_with_subprocess(command_group, command, **kwargs): run_ayon_launcher_process(*args, logger=log) # return all colorspaces - with open(tmp_json_path, "r") as f_: - return json.load(f_) + with open(tmp_json_path, "r") as stream: + return json.load(stream) -# TODO: this should be part of ocio_wrapper.py def compatibility_check_config_version(config_path, major=1, minor=None): """Making sure PyOpenColorIO config version is compatible""" - if not CachedData.config_version_data.get(config_path): - if has_compatible_ocio_package(): - # TODO: refactor this so it is not imported but part of this file - from ayon_core.scripts.ocio_wrapper import _get_version_data - - CachedData.config_version_data[config_path] = \ - _get_version_data(config_path) - - else: - # python environment is not compatible with PyOpenColorIO - # needs to be run in subprocess - CachedData.config_version_data[config_path] = \ - _get_wrapped_with_subprocess( - "config", "get_version", config_path=config_path - ) + version_data = get_config_version_data(config_path) # check major version - if CachedData.config_version_data[config_path]["major"] != major: + if version_data["major"] != major: return False # check minor version - if minor and CachedData.config_version_data[config_path]["minor"] != minor: + if minor is not None and version_data["minor"] != minor: return False - # compatible return True @@ -482,22 +490,20 @@ def get_ocio_config_colorspaces(config_path): Returns: dict: colorspace and family in couple - """ - if not CachedData.ocio_config_colorspaces.get(config_path): - if not has_compatible_ocio_package(): - # python environment is not compatible with PyOpenColorIO - # needs to be run in subprocess - config_colorspaces = _get_wrapped_with_subprocess( - "config", "get_colorspace", in_path=config_path - ) - else: - # TODO: refactor this so it is not imported but part of this file - from ayon_core.scripts.ocio_wrapper import _get_colorspace_data - config_colorspaces = _get_colorspace_data(config_path) + """ + if config_path not in CachedData.ocio_config_colorspaces: + if has_compatible_ocio_package(): + config_colorspaces = _get_ocio_config_colorspaces(config_path) + else: + config_colorspaces = _get_wrapped_with_subprocess( + "config", + "get_ocio_config_colorspaces", + config_path=config_path + ) CachedData.ocio_config_colorspaces[config_path] = config_colorspaces - return CachedData.ocio_config_colorspaces[config_path] + return deepcopy(CachedData.ocio_config_colorspaces[config_path]) def convert_colorspace_enumerator_item( @@ -571,16 +577,18 @@ def get_colorspaces_enumerator_items( Families can be used for building menu and submenus in gui. Args: - config_items (dict[str,dict]): colorspace data coming from - `get_ocio_config_colorspaces` function - include_aliases (bool): include aliases in result - include_looks (bool): include looks in result - include_roles (bool): include roles in result + config_items (dict[str,dict]): Colorspace data coming from + `get_ocio_config_colorspaces` function. + include_aliases (Optional[bool]): Include aliases in result. + include_looks (Optional[bool]): Include looks in result. + include_roles (Optional[bool]): Include roles in result. + include_display_views (Optional[bool]): Include display views + in result. Returns: - list[tuple[str,str]]: colorspace and family in couple + list[tuple[str, str]]: Colorspace and family in couples. + """ - labeled_colorspaces = [] aliases = set() colorspaces = set() looks = set() @@ -590,64 +598,74 @@ def get_colorspaces_enumerator_items( if items_type == "colorspaces": for color_name, color_data in colorspace_items.items(): if color_data.get("aliases"): - aliases.update([ + aliases.update({ ( "aliases::{}".format(alias_name), "[alias] {} ({})".format(alias_name, color_name) ) for alias_name in color_data["aliases"] - ]) + }) colorspaces.add(( "{}::{}".format(items_type, color_name), "[colorspace] {}".format(color_name) )) elif items_type == "looks": - looks.update([ + looks.update({ ( "{}::{}".format(items_type, name), "[look] {} ({})".format(name, role_data["process_space"]) ) for name, role_data in colorspace_items.items() - ]) + }) elif items_type == "displays_views": - display_views.update([ + display_views.update({ ( "{}::{}".format(items_type, name), "[view (display)] {}".format(name) ) for name, _ in colorspace_items.items() - ]) + }) elif items_type == "roles": - roles.update([ + roles.update({ ( "{}::{}".format(items_type, name), "[role] {} ({})".format(name, role_data["colorspace"]) ) for name, role_data in colorspace_items.items() - ]) + }) - if roles and include_roles: - roles = sorted(roles, key=lambda x: x[0]) - labeled_colorspaces.extend(roles) + def _sort_key_getter(item): + """Use colorspace for sorting.""" + return item[0] - # add colorspaces as second so it is not first in menu - colorspaces = sorted(colorspaces, key=lambda x: x[0]) - labeled_colorspaces.extend(colorspaces) + labeled_colorspaces = [] + if include_roles: + labeled_colorspaces.extend( + sorted(roles, key=_sort_key_getter) + ) - if aliases and include_aliases: - aliases = sorted(aliases, key=lambda x: x[0]) - labeled_colorspaces.extend(aliases) + # Add colorspaces after roles, so it is not first in menu + labeled_colorspaces.extend( + sorted(colorspaces, key=_sort_key_getter) + ) - if looks and include_looks: - looks = sorted(looks, key=lambda x: x[0]) - labeled_colorspaces.extend(looks) + if include_aliases: + labeled_colorspaces.extend( + sorted(aliases, key=_sort_key_getter) + ) - if display_views and include_display_views: - display_views = sorted(display_views, key=lambda x: x[0]) - labeled_colorspaces.extend(display_views) + if include_looks: + labeled_colorspaces.extend( + sorted(looks, key=_sort_key_getter) + ) + + if include_display_views: + labeled_colorspaces.extend( + sorted(display_views, key=_sort_key_getter) + ) return labeled_colorspaces @@ -663,18 +681,16 @@ def get_ocio_config_views(config_path): Returns: dict: `display/viewer` and viewer data + """ - if not has_compatible_ocio_package(): - # python environment is not compatible with PyOpenColorIO - # needs to be run in subprocess - return _get_wrapped_with_subprocess( - "config", "get_views", in_path=config_path - ) + if has_compatible_ocio_package(): + return _get_ocio_config_views(config_path) - # TODO: refactor this so it is not imported but part of this file - from ayon_core.scripts.ocio_wrapper import _get_views_data - - return _get_views_data(config_path) + return _get_wrapped_with_subprocess( + "config", + "get_ocio_config_views", + config_path=config_path + ) def _get_global_config_data( @@ -1255,48 +1271,17 @@ def get_display_view_colorspace_name(config_path, display, view): str: View color space name. e.g. "Output - sRGB" """ - if not has_compatible_ocio_package(): - # python environment is not compatible with PyOpenColorIO - # needs to be run in subprocess - return _get_display_view_colorspace_subprocess( + if has_compatible_ocio_package(): + return _get_display_view_colorspace_name( config_path, display, view ) - - from ayon_core.scripts.ocio_wrapper import _get_display_view_colorspace_name # noqa - - return _get_display_view_colorspace_name(config_path, display, view) - - -def _get_display_view_colorspace_subprocess(config_path, display, view): - """Returns the colorspace attribute of the (display, view) pair - via subprocess. - - Args: - config_path (str): path string leading to config.ocio - display (str): display name e.g. "ACES" - view (str): view name e.g. "sRGB" - - Returns: - view color space name (str) e.g. "Output - sRGB" - - """ - with _make_temp_json_file() as tmp_json_path: - # Prepare subprocess arguments - args = [ - "run", get_ocio_config_script_path(), - "config", "get_display_view_colorspace_name", - "--in_path", config_path, - "--out_path", tmp_json_path, - "--display", display, - "--view", view - ] - log.debug("Executing: {}".format(" ".join(args))) - - run_ayon_launcher_process(*args, logger=log) - - # return default view colorspace name - with open(tmp_json_path, "r") as f: - return json.load(f) + return _get_wrapped_with_subprocess( + "config", + "get_display_view_colorspace_name", + config_path=config_path, + display=display, + view=view + ) # --- Implementation of logic using 'PyOpenColorIO' --- @@ -1531,7 +1516,8 @@ def get_colorspace_from_filepath(*args, **kwargs): def get_colorspace_data_subprocess(config_path): """[Deprecated] Get colorspace data via subprocess - Wrapper for Python 2 hosts. + Deprecated: + Deprecated since OpenPype. Use `_get_wrapped_with_subprocess` instead. Args: config_path (str): path leading to config.ocio file @@ -1540,7 +1526,9 @@ def get_colorspace_data_subprocess(config_path): dict: colorspace and family in couple """ return _get_wrapped_with_subprocess( - "config", "get_colorspace", in_path=config_path + "config", + "get_ocio_config_colorspaces", + config_path=config_path ) @@ -1548,30 +1536,20 @@ def get_colorspace_data_subprocess(config_path): def get_views_data_subprocess(config_path): """[Deprecated] Get viewers data via subprocess - Wrapper for Python 2 hosts. + Deprecated: + Deprecated since OpenPype. Use `_get_wrapped_with_subprocess` instead. Args: config_path (str): path leading to config.ocio file Returns: dict: `display/viewer` and viewer data + """ return _get_wrapped_with_subprocess( - "config", "get_views", in_path=config_path - ) - - -@deprecated("_get_wrapped_with_subprocess") -def get_data_subprocess(config_path, data_type): - """[Deprecated] Get data via subprocess - - Wrapper for Python 2 hosts. - - Args: - config_path (str): path leading to config.ocio file - """ - return _get_wrapped_with_subprocess( - "config", data_type, in_path=config_path, + "config", + "get_ocio_config_views", + config_path=config_path ) From 25fe55aa316f8f5d16bd6c773d0aa3c1a3d07935 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:42:21 +0200 Subject: [PATCH 188/192] validate 'PyOpenColorIO' version --- client/ayon_core/pipeline/colorspace.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index c347638937..c29bdd762d 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -116,13 +116,21 @@ def has_compatible_ocio_package(): if CachedData.has_compatible_ocio_package is not None: return CachedData.has_compatible_ocio_package + is_compatible = False try: - import PyOpenColorIO # noqa: F401 - # TODO validate 'PyOpenColorIO' version - CachedData.has_compatible_ocio_package = True - except ImportError: - CachedData.has_compatible_ocio_package = False + import PyOpenColorIO + # Check if PyOpenColorIO is compatible + # - version 2.0.0 or higher is required + # NOTE version 1 does not have '__version__' attribute + if hasattr(PyOpenColorIO, "__version__"): + version_parts = PyOpenColorIO.__version__.split(".") + major = int(version_parts[0]) + is_compatible = (major, ) >= (2, ) + except ImportError: + pass + + CachedData.has_compatible_ocio_package = is_compatible # compatible return CachedData.has_compatible_ocio_package From 8806463c7789fdcc2ce222576062c759c56c8b19 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:42:30 +0200 Subject: [PATCH 189/192] use raw DeprecationWarning --- client/ayon_core/pipeline/colorspace.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index c29bdd762d..b568e2cdf1 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -36,10 +36,6 @@ class CachedData: } -class DeprecatedWarning(DeprecationWarning): - pass - - def deprecated(new_destination): """Mark functions as deprecated. @@ -64,13 +60,13 @@ def deprecated(new_destination): @functools.wraps(decorated_func) def wrapper(*args, **kwargs): - warnings.simplefilter("always", DeprecatedWarning) + warnings.simplefilter("always", DeprecationWarning) warnings.warn( ( "Call to deprecated function '{}'" "\nFunction was moved or removed.{}" ).format(decorated_func.__name__, warning_message), - category=DeprecatedWarning, + category=DeprecationWarning, stacklevel=4 ) return decorated_func(*args, **kwargs) From 3b93392eb75663351e04de49d2c55cf163cd56cc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:47:23 +0200 Subject: [PATCH 190/192] fix typo --- client/ayon_core/scripts/ocio_wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/scripts/ocio_wrapper.py b/client/ayon_core/scripts/ocio_wrapper.py index 9cbab32956..897e910fa5 100644 --- a/client/ayon_core/scripts/ocio_wrapper.py +++ b/client/ayon_core/scripts/ocio_wrapper.py @@ -153,7 +153,7 @@ def _get_config_version_data(config_path, output_path): type=click.Path(exists=True)) @click.option( "--filepath", - equired=True, + required=True, help="Path to file to get colorspace from.", type=click.Path()) @click.option( From ddbec4ea71ae9e57c9c2a1d9d82abbe01933896f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 15 May 2024 18:57:04 +0200 Subject: [PATCH 191/192] added docstring to sorter --- client/ayon_core/pipeline/colorspace.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index b568e2cdf1..d9785b61fb 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -642,7 +642,15 @@ def get_colorspaces_enumerator_items( }) def _sort_key_getter(item): - """Use colorspace for sorting.""" + """Use colorspace for sorting. + + Args: + item (tuple[str, str]): Item with colorspace and label. + + Returns: + str: Colorspace. + + """ return item[0] labeled_colorspaces = [] From d353dd145a4c82a72498de87f9dfb32f2cffac09 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 16 May 2024 09:40:53 +0200 Subject: [PATCH 192/192] removed unnecessary command group --- client/ayon_core/pipeline/colorspace.py | 11 +-------- client/ayon_core/scripts/ocio_wrapper.py | 30 ++++-------------------- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index d9785b61fb..239c187959 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -292,7 +292,6 @@ def get_config_file_rules_colorspace_from_filepath(config_path, filepath): ) else: result_data = _get_wrapped_with_subprocess( - "colorspace", "get_config_file_rules_colorspace_from_filepath", config_path=config_path, filepath=filepath @@ -321,7 +320,6 @@ def get_config_version_data(config_path): version_data = _get_config_version_data(config_path) else: version_data = _get_wrapped_with_subprocess( - "config", "get_config_version_data", config_path=config_path ) @@ -432,11 +430,10 @@ def validate_imageio_colorspace_in_config(config_path, colorspace_name): return True -def _get_wrapped_with_subprocess(command_group, command, **kwargs): +def _get_wrapped_with_subprocess(command, **kwargs): """Get data via subprocess. Args: - command_group (str): command group name command (str): command name **kwargs: command arguments @@ -448,7 +445,6 @@ def _get_wrapped_with_subprocess(command_group, command, **kwargs): args = [ "run", get_ocio_config_script_path(), - command_group, command ] @@ -501,7 +497,6 @@ def get_ocio_config_colorspaces(config_path): config_colorspaces = _get_ocio_config_colorspaces(config_path) else: config_colorspaces = _get_wrapped_with_subprocess( - "config", "get_ocio_config_colorspaces", config_path=config_path ) @@ -699,7 +694,6 @@ def get_ocio_config_views(config_path): return _get_ocio_config_views(config_path) return _get_wrapped_with_subprocess( - "config", "get_ocio_config_views", config_path=config_path ) @@ -1288,7 +1282,6 @@ def get_display_view_colorspace_name(config_path, display, view): config_path, display, view ) return _get_wrapped_with_subprocess( - "config", "get_display_view_colorspace_name", config_path=config_path, display=display, @@ -1538,7 +1531,6 @@ def get_colorspace_data_subprocess(config_path): dict: colorspace and family in couple """ return _get_wrapped_with_subprocess( - "config", "get_ocio_config_colorspaces", config_path=config_path ) @@ -1559,7 +1551,6 @@ def get_views_data_subprocess(config_path): """ return _get_wrapped_with_subprocess( - "config", "get_ocio_config_views", config_path=config_path ) diff --git a/client/ayon_core/scripts/ocio_wrapper.py b/client/ayon_core/scripts/ocio_wrapper.py index 897e910fa5..0414fc59ce 100644 --- a/client/ayon_core/scripts/ocio_wrapper.py +++ b/client/ayon_core/scripts/ocio_wrapper.py @@ -33,27 +33,7 @@ def main(): pass # noqa: WPS100 -@main.group() -def config(): - """Config related commands group - - Example of use: - > pyton.exe ./ocio_wrapper.py config *args - """ - pass # noqa: WPS100 - - -@main.group() -def colorspace(): - """Colorspace related commands group - - Example of use: - > pyton.exe ./ocio_wrapper.py config *args - """ - pass # noqa: WPS100 - - -@config.command( +@main.command( name="get_ocio_config_colorspaces", help="return all colorspaces from config file") @click.option( @@ -83,7 +63,7 @@ def _get_ocio_config_colorspaces(config_path, output_path): ) -@config.command( +@main.command( name="get_ocio_config_views", help="All viewers from config file") @click.option( @@ -113,7 +93,7 @@ def _get_ocio_config_views(config_path, output_path): ) -@config.command( +@main.command( name="get_config_version_data", help="Get major and minor version from config file") @click.option( @@ -143,7 +123,7 @@ def _get_config_version_data(config_path, output_path): ) -@colorspace.command( +@main.command( name="get_config_file_rules_colorspace_from_filepath", help="Colorspace file rules from filepath") @click.option( @@ -182,7 +162,7 @@ def _get_config_file_rules_colorspace_from_filepath( ) -@config.command( +@main.command( name="get_display_view_colorspace_name", help=( "Default view colorspace name for the given display and view"