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/145] :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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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/145] 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 bedebd8f8e871665d6b117f5c13c8a20a63ad24a Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 15 Apr 2024 12:28:31 +0200 Subject: [PATCH 031/145] 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 032/145] 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 033/145] 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 034/145] 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 035/145] 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 036/145] 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 037/145] 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 038/145] 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 039/145] 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 040/145] 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 041/145] 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 6e548a83c9d40be1012201160bb794eec50da6d5 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 23 Apr 2024 21:21:30 +0200 Subject: [PATCH 042/145] 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 043/145] 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 044/145] 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 045/145] 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 046/145] 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 8110a601bbc0d114c1bcd22be8122cc591ef51a7 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 25 Apr 2024 11:02:31 +0200 Subject: [PATCH 047/145] 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 048/145] 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 049/145] 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 050/145] 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 051/145] 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 052/145] _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 053/145] 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 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 054/145] 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 055/145] 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 056/145] 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 057/145] 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 058/145] 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 20dad59947e4fb13d026670baa73820e9e378ecd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 May 2024 18:31:40 +0200 Subject: [PATCH 059/145] 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 060/145] 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 061/145] 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 062/145] 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 063/145] 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 064/145] 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 065/145] 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 066/145] 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 067/145] 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 068/145] '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 069/145] 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 070/145] 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 071/145] 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 072/145] 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 073/145] 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 074/145] 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 075/145] 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 076/145] 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 077/145] 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 078/145] '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 079/145] 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 080/145] 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 081/145] 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 2facf91bcb4a5ea5812dc93b3b2c026978a7a7f3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 May 2024 13:51:36 +0200 Subject: [PATCH 082/145] 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 083/145] 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 4d502a55481adbfdbd5fe06aeefe2f7ad14d381e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 May 2024 21:42:04 +0800 Subject: [PATCH 084/145] 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 085/145] 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 086/145] 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 087/145] 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 088/145] 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 fa4569402395e88543796db5903157ea917a9cd5 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 9 May 2024 12:04:27 +0100 Subject: [PATCH 089/145] 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 090/145] 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 091/145] 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 092/145] 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 b7662645b5c980c09c599ce4d88ad03a77d0d00f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 May 2024 16:15:30 +0200 Subject: [PATCH 093/145] 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 094/145] 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 095/145] 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 096/145] 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 097/145] 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 098/145] 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 099/145] 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 3bd7c7dddfd3a6e419d34640de4d248fc664396e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 10 May 2024 11:02:55 +0200 Subject: [PATCH 100/145] 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 101/145] 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 102/145] 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 103/145] 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 104/145] 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 105/145] 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 106/145] 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 107/145] 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 108/145] 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 109/145] 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 110/145] 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 111/145] 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 112/145] 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 113/145] 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 114/145] 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 115/145] 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 116/145] 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 117/145] 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 118/145] 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 119/145] 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 120/145] 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 121/145] 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 122/145] 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 123/145] 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 124/145] 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 125/145] 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 126/145] 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 127/145] 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 128/145] 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 129/145] 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 130/145] 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 131/145] 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 132/145] 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 133/145] 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 134/145] :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 135/145] 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 136/145] 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 137/145] 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 138/145] 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 139/145] 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 140/145] 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 141/145] 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 142/145] 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 143/145] 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 144/145] 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 145/145] 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"